本文主要总结至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 plain copy print ?
-
-
-
- class XmppStanzaHandler {
- public:
- virtual ~XmppStanzaHandler() {}
-
-
-
-
- virtual bool HandleStanza(const XmlElement * stanza) = 0;
- };
//! 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里:
[cpp] view plain copy print ?
- 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_;
- };
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方法来发送数据。
[cpp] view plain copy print ?
- class TransportChannel: public sigslot::has_slots<> {
- public:
-
-
-
- virtual int SendPacket(const char *data, size_t len) = 0;
-
- sigslot::signal3<TransportChannel*, const char*, size_t> SignalReadPacket;
-
- };
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对象,如下:
[cpp] view plain copy print ?
- 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;
- }
-
-
- }
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时,如果能够充分的利用已成的实例,对于缩短熟悉时间,很有帮助。
http://blog.csdn.net/leehark/article/details/7234884