libjingle源码解析--libjingle是怎么运作的?

来源:互联网 发布:linux oracle删除监听 编辑:程序博客网 时间:2024/05/22 10:34

本文主要总结至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类定义如下:

[cpp] view plaincopyprint?
  1. //! Callback to deliver stanzas to an Xmpp application module. 
  2. //! Register via XmppEngine.SetDefaultSessionHandler or via 
  3. //! XmppEngine.AddSessionHAndler.   
  4. class XmppStanzaHandler { 
  5. public
  6.   virtual ~XmppStanzaHandler() {} 
  7.   //! Process the given stanza. 
  8.   //! The handler must return true if it has handled the stanza. 
  9.   //! A false return value causes the stanza to be passed on to 
  10.   //! the next registered handler. 
  11.   virtual bool HandleStanza(const XmlElement * stanza) = 0; 
  12. }; 
[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. //! Callback to deliver stanzas to an Xmpp application module.  
  2. //! Register via XmppEngine.SetDefaultSessionHandler or via  
  3. //! XmppEngine.AddSessionHAndler.     
  4. class XmppStanzaHandler {  
  5. public:  
  6.   virtual ~XmppStanzaHandler() {}  
  7.   //! Process the given stanza.   
  8.   //! The handler must return true if it has handled the stanza.  
  9.   //! A false return value causes the stanza to be passed on to  
  10.   //! the next registered handler.   
  11.   virtual bool HandleStanza(const XmlElement * stanza) = 0;  
  12. };  

  • 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里:

[cpp] view plaincopyprint?
  1. class PresencePushTask : public XmppTask { 
  2. public
  3.   PresencePushTask(XmppTaskParentInterface* parent, CallClient* client) 
  4.     : XmppTask(parent, XmppEngine::HL_TYPE), 
  5.       client_(client) {} 
  6.   virtual int ProcessStart(); 
  7.  
  8.   sigslot::signal1<const Status&> SignalStatusUpdate; 
  9.   sigslot::signal1<const Jid&> SignalMucJoined; 
  10.   sigslot::signal2<const Jid&, int> SignalMucLeft; 
  11.   sigslot::signal2<const Jid&, const MucStatus&> SignalMucStatusUpdate; 
  12.  
  13. protected
  14.   virtual bool HandleStanza(const XmlElement * stanza); 
  15.   void HandlePresence(const Jid& from,const XmlElement * stanza); 
  16.   void HandleMucPresence(buzz::Muc* muc, 
  17.                          const Jid& from,const XmlElement * stanza); 
  18.   static void FillStatus(const Jid& from,const XmlElement * stanza, 
  19.                          Status* status); 
  20.   static void FillMucStatus(const Jid& from,const XmlElement * stanza, 
  21.                             MucStatus* status); 
  22.  
  23. private
  24.   CallClient* client_; 
  25. }; 
[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. class PresencePushTask : public XmppTask {  
  2.  public:  
  3.   PresencePushTask(XmppTaskParentInterface* parent, CallClient* client)  
  4.     : XmppTask(parent, XmppEngine::HL_TYPE),  
  5.       client_(client) {}  
  6.   virtual int ProcessStart();  
  7.   
  8.   sigslot::signal1<const Status&> SignalStatusUpdate;  
  9.   sigslot::signal1<const Jid&> SignalMucJoined;  
  10.   sigslot::signal2<const Jid&, int> SignalMucLeft;  
  11.   sigslot::signal2<const Jid&, const MucStatus&> SignalMucStatusUpdate;  
  12.   
  13.  protected:  
  14.   virtual bool HandleStanza(const XmlElement * stanza);  
  15.   void HandlePresence(const Jid& from, const XmlElement * stanza);  
  16.   void HandleMucPresence(buzz::Muc* muc,  
  17.                          const Jid& from, const XmlElement * stanza);  
  18.   static void FillStatus(const Jid& from, const XmlElement * stanza,  
  19.                          Status* status);  
  20.   static void FillMucStatus(const Jid& from, const XmlElement * stanza,  
  21.                             MucStatus* status);  
  22.   
  23.  private:  
  24.   CallClient* client_;  
  25. };  

    • 这里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方法来发送数据。

[cpp] view plaincopyprint?
  1. class TransportChannel: public sigslot::has_slots<> { 
  2. public
  3. //...... 
  4.  
  5.   // Attempts to send the given packet.  The return value is < 0 on failure. 
  6.   virtual int SendPacket(constchar *data, size_t len) = 0; 
  7.   // Signalled each time a packet is received on this channel. 
  8.   sigslot::signal3<TransportChannel*, const char*, size_t> SignalReadPacket; 
  9. //...... 
  10. }; 
[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. class TransportChannel: public sigslot::has_slots<> {  
  2.  public:  
  3. //......   
  4.   
  5.   // Attempts to send the given packet.  The return value is < 0 on failure.  
  6.   virtual int SendPacket(const char *data, size_t len) = 0;  
  7.   // Signalled each time a packet is received on this channel.  
  8.   sigslot::signal3<TransportChannel*, const char*, size_t> SignalReadPacket;  
  9. //......   
  10. };  

4.Peer to peer Component模块。

此模块才是libjingle核心,libjingle项目的初衷也是能够把模块设计得完美,使得所有需要通过P2P传输数据的应用层调用libjingle时,不用担心数据传输的稳定性,可靠性,高效性。

  • 刚才上面提到,当服务器发送stanza时XmppEngine把Stanza发送到XmppTask过滤,在这个模块,SessionManagerTask代理SessionManager过滤session相关的stanza,并转发到SessionManager对象,如下:

[cpp] view plaincopyprint?
  1. class SessionManagerTask : public buzz::XmppTask { 
  2. public
  3.   ...... 
  4.  
  5.   virtual int ProcessStart() { 
  6.     const buzz::XmlElement *stanza = NextStanza(); 
  7.     if (stanza == NULL) 
  8.       return STATE_BLOCKED; 
  9.     session_manager_->OnIncomingMessage(stanza); 
  10.     return STATE_START; 
  11.   } 
  12.  
  13. protected
  14.   virtual bool HandleStanza(const buzz::XmlElement *stanza) { 
  15.     if (!session_manager_->IsSessionMessage(stanza)) 
  16.       return false
  17.     QueueStanza(stanza); 
  18.     return true
  19.   } 
  20.  
  21.  
  22. // namespace cricket 
[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. class SessionManagerTask : public buzz::XmppTask {  
  2.  public:  
  3.   ......  
  4.   
  5.   virtual int ProcessStart() {  
  6.     const buzz::XmlElement *stanza = NextStanza();  
  7.     if (stanza == NULL)  
  8.       return STATE_BLOCKED;  
  9.     session_manager_->OnIncomingMessage(stanza);  
  10.     return STATE_START;  
  11.   }  
  12.   
  13.  protected:  
  14.   virtual bool HandleStanza(const buzz::XmlElement *stanza) {  
  15.     if (!session_manager_->IsSessionMessage(stanza))  
  16.       return false;  
  17.     QueueStanza(stanza);  
  18.     return true;  
  19.   }  
  20.   
  21.   
  22. }  // 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时,如果能够充分的利用已成的实例,对于缩短熟悉时间,很有帮助。

0 0