本文主要總結至libjingle源碼和官方文章:http://code.google.com/apis/talk/libjingle/libjingle_applications.html
ligjingle的總體架構如:
1.Application模組
Ligjingle的應用程式首先調用XMPP Messaging Component的XmppClient對象進行登入,然後做一些message,iq,presence等request/respond操作。
其次,每個application可能包含一個或多個session client用來做P2P操作,比如遠程協助,視頻會議,音頻串連,檔案分享權限設定等等。
2.XMPP Messaging Component.模組
此模組主要由三個部分組成:XmppClient,LoginHandler和Xmpp Helper Task.此模組主要做相當於一個peer的防火牆的功能,串連伺服器和用戶端,負責發送所有本地的stanza請求(即XMPP協議內容),並負責接收伺服器的stanza請求,並分發到各個Helper Task裡。
- XmppClient主要是代理登入,發送stanza,接收stanza。之所以說是代理,是因為真正發送,接收的訊息都是通過XmppEngine來實現的。
- XmppEngine能註冊多個XmppStanzaHandler回調,然後所有從伺服器接收的Stanza都轉寄到已綁定的XmppStanzaHandler進行過濾,而實際上是回調XmppTask對象。
- XmppStanzaHandler類定義如下:
//! Callback to deliver stanzas to an Xmpp application module.//! Register via XmppEngine.SetDefaultSessionHandler or via//! XmppEngine.AddSessionHAndler. class XmppStanzaHandler {public: virtual ~XmppStanzaHandler() {} //! Process the given stanza. //! The handler must return true if it has handled the stanza. //! A false return value causes the stanza to be passed on to //! the next registered handler. virtual bool HandleStanza(const XmlElement * stanza) = 0;};
- XmppTask是所有XmppHelperTask的基類,並繼承自XmppStanzaHandler,主要有監聽,過濾XmppEngine對象轉寄過來的Stanza訊息。XmppTask有多種類型,當取類型為HL_PEEK時,只有監聽功能,無法做到過濾;而其他類型可以做到過濾。過濾是有HandlerStanza函數來完成,當返回為true時,過濾,否則XmppEngine枚舉下一個綁定的XmppTask繼續嘗試分發、過濾。
- 所有XmppHelperTask都要繼承XmppTask並要重載HandlerStanza函數和ProcessStart函數。
- HandlerStanza是用來過濾,相當於windows訊息處理的GetMessage()
- 而ProcessStart是用來處理HandlerStanza過濾的訊息。
- 比如在原始碼example/call/presencepushtask.h裡:
class PresencePushTask : public XmppTask { public: PresencePushTask(XmppTaskParentInterface* parent, CallClient* client) : XmppTask(parent, XmppEngine::HL_TYPE), client_(client) {} virtual int ProcessStart(); sigslot::signal1<const Status&> SignalStatusUpdate; sigslot::signal1<const Jid&> SignalMucJoined; sigslot::signal2<const Jid&, int> SignalMucLeft; sigslot::signal2<const Jid&, const MucStatus&> SignalMucStatusUpdate; protected: virtual bool HandleStanza(const XmlElement * stanza); void HandlePresence(const Jid& from, const XmlElement * stanza); void HandleMucPresence(buzz::Muc* muc, const Jid& from, const XmlElement * stanza); static void FillStatus(const Jid& from, const XmlElement * stanza, Status* status); static void FillMucStatus(const Jid& from, const XmlElement * stanza, MucStatus* status); private: CallClient* client_;};
- 這裡PresencePushTask類,通過HandleStanza過濾所有presence相關的stanza並在ProcessStart裡處理所有來自伺服器的使用者狀態更新訊息。
- LoginHandler部分是由XmppPump來負責的。主要調用XmppClient的connect和disconnect方法建立、中斷連線,監聽SignalStateChange事件來擷取串連資訊,類型為STATE_OPENED的事件表示串連成功。
3.Session Logic and management commponent模組。
所有p2p session邏輯相關的部分都放在了這個模組。可以session可能是處理檔案傳輸的串連,或者可能是視頻會話,或者音頻會話等等。
- 我們需要繼承SessionClient來處理每個Session相關具體任務,比如檔案傳輸Session:當接收對端用戶端建立一個檔案傳輸session的時候,如果此Session是新建立的,SessionManager對象會回調所有註冊的SessionClient的OnSessionCreate的介面,並以SessionManger建立的Session對象為參數穿進去;如果是已有的Session則會調用Session的OnIncomingMessage方法。
- Session對象則抽象了兩個peer之間的資料轉送介面。當收到OnSessionCreate回調時,SessionClient可以通過Session的方法Accept來接受建立,Reject來拒絕。
- 那怎麼讀寫p2p資料呢?
- 首先需要調用session的CreateChannel方法擷取TransportChannel對象指標,然後監聽TransportChannel的事件SignalReadPacket來接收資料,通過SendPacket方法來發送資料。
class TransportChannel: public sigslot::has_slots<> { public://...... // Attempts to send the given packet. The return value is < 0 on failure. virtual int SendPacket(const char *data, size_t len) = 0; // Signalled each time a packet is received on this channel. sigslot::signal3<TransportChannel*, const char*, size_t> SignalReadPacket;//......};
4.Peer to peer Component模組。
此模組才是libjingle核心,libjingle項目的初衷也是能夠把模組設計得完美,使得所有需要通過P2P傳輸資料的應用程式層調用libjingle時,不用擔心資料轉送的穩定性,可靠性,高效性。
- 剛才上面提到,當伺服器發送stanza時XmppEngine把Stanza發送到XmppTask過濾,在這個模組,SessionManagerTask代理SessionManager過濾session相關的stanza,並轉寄到SessionManager對象,如下:
class SessionManagerTask : public buzz::XmppTask { public: ...... virtual int ProcessStart() { const buzz::XmlElement *stanza = NextStanza(); if (stanza == NULL) return STATE_BLOCKED; session_manager_->OnIncomingMessage(stanza); return STATE_START; } protected: virtual bool HandleStanza(const buzz::XmlElement *stanza) { if (!session_manager_->IsSessionMessage(stanza)) return false; QueueStanza(stanza); return true; }} // namespace cricket
- SessionManager類在這裡起到串連上述3個模組的橋樑作用。
- 當上層調用SessionManager建立的Session對象的CreateChannel時,實際上是調用P2PTransport的CreateChannel方法。
- 上層通過P2PTransport建立P2pTransportChannel的類的。
- P2pTransportChannel繼承自TransportChannel,並建立多個不同的Connection,每個Connection代表一個TCP或者UDP或者SSL串連。上層傳輸資料最終是調到P2pTransportChannel的相關方法,當發送,接收資料時,P2pTransportChannel選擇表現最好的Connection進行傳輸。
5.其他
LigJingle提供了很多介面供我們繼承,用於特定的個人化Session,同時也提供了不少執行個體(如pcp,login,call)讓調用者更容易的理解架構思路。當需要著手研究libjingle時,如果能夠充分的利用已成的執行個體,對於縮短熟悉時間,很有協助。