您的位置:首页 > 其它

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

2014-05-08 21:40 302 查看
本文主要总结至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?

//! 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;

};

[cpp]
view plaincopyprint?





//! 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;
};

//! 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 plaincopyprint?

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_;
};

[cpp]
view plaincopyprint?





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 plaincopyprint?

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;
//......
};

[cpp]
view plaincopyprint?





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;
//......
};

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 plaincopyprint?

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

[cpp]
view plaincopyprint?





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
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时,如果能够充分的利用已成的实例,对于缩短熟悉时间,很有帮助。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐