http://blog.csdn.net/pylonion/article/details/2737431
Libjingle学习笔记:
Libjingle概念:Libjingle是一个开发库,由c++的源码和文档组成,这些文档可以使你设计应用程序,通过一个网络,能够连接和交换数据。值得注意的是这个源码需要一些外部的依赖,例如: 声音的聊天例子依赖于Linphone或者GIPS,这取决于你自己的平台。这个源码包含网络和代理服务器的协商层,XML的分析层,一个STUN的服务器,所有的源码需要发起一个连接和交换数据在两台电脑之间。通过使用ICE(Internet Communication Engine)的机制,STUN服务器,以及交换UDP或者TCP的数据包,这些连接的源码能够强行穿越NAT(网络地址转换)和防火墙的装置。你可以使用提供的源码,或者扩展它,以适合你自己特定的需要,根据Berkeley-style的许可。
Jingle和libjingle的区别:
Libjingle大概和jingle XMPP 扩展在同一时间被建立。Libjingle的团队建立了他们自己的协议去处理回话协商,后来和使用标准化的jingle(基于XMPP的标准)一起工作。尽管,jingle和libjingle是非常相似的,但是它们是不一样的,而且不能共同使用。现在libjingle的源码版本依然使用原始的网络协议,跟以前的稍微有些不同,而且无法兼容jingle的规范。不过它还是足够的接近jingle,所以学习jingle的说明书是值得的。类似的“接近但不是一样”,libjingle的视频内容描述(早期的jingle的视频内容描述格式XEP-0167),ICE的传输描述(早期的jingle的ICE传输XEP-0176),以及流的UDP描述(早期的jingle流UDP的传输描述XEP-0177).
学习libjingle的条件:
首先得熟悉XMPP协议,普通的网络概念,和C++。除此以外,它能帮助熟悉jingle提出的扩展(XEP-0166),和其他的相关的扩展。
文档学习:
重要的概念:
Signals:
Libjingle使用sigslot library ,使得在对象之间进行通信变得容易,sigslot是一个普通的框架,使得你在任一层去连接一个呼叫成员让它去接受功能变得非常简单,这种方法的工作方式如下:
1.发送层声明一个成员变量,叫做signal,使用一种特殊的模块类似于语法,这个信号定义了监听功能的参数。
2. 监听层实现了一个功能,像信号一样,使用同样的成员,类型,以及参数的队列。有时被称作接收者或者槽。
3.收听者能够连接这种信号通过呼叫信令的连接方法,经过一个指向在监听对象的实例,和实现层功能的地址。
4.发送者呼叫他的信号成员,好像它是一个功能,通过一个被声明的合适的参数类型
你能够连接尽可能多的信号如同一个普通的槽位,libjingle有时指派多个信号到一个信号槽以便于巩固它的信号处理。相反,几个对象声明了一个信号对象以便于广播常见的需要的信息,从一个信号点。Sigslot可以关注断开的回叫信号和废弃当对象被毁灭的时候。
下面的代码能够说明使用sigslot
// Class that sends the notification.
class Sender {
// The signal declaration.
// The '2' in the name indicates the number of parameters. Parameter types
// are declared in the template parameter list.
sigslot::signal2<string message, std::time_t time> SignalDanger;
//定义一个SignalDanger(string ,std::time_t)类型的方法! 让后他的调用会是这样一种形式SignalDanger(message, time);
// When anyone calls Panic(), we will send the SignalDanger signal.
void Panic(){
SignalDanger("Help!", std::time(0));
}
// Listening class. It must inherit sigslot.
class Receiver : public sigslot::has_slots<>{
// Receiver registers to get SignalDanger signals.
// When SignalDanger is sent, it is caught by OnDanger().
// Second parameter gives address of the listener function class definition.
// First parameter points to instance of this class to receive notifications.
Receiver(Sender sender){
sender->SignalDanger.connect(this, &Receiver.OnDanger);
}
// When anyone calls Panic(), Receiver::OnDanger gets the message.
// Notice that the number and type of parameters match
// those in Sender::SignalDanger, and that it doesn't return a value.
void OnDanger(string message, std::time_t time){
if(message == "Help!")
{
// Call the police
...
}
}
...
线程
Libjingle支持多线程,以便于改善程序的性能,libjingle组件使用一个或者两个全局变量的线程。
×信号线程常用来建立基本的组件,例如会话管理和控制,XMPP信息组件。
×工作者线程被点对点的组件对象使用来处理更多的资源强烈的过程,例如数据流。把这些放在一个线程里以防止数据流被阻塞或者被XMPP阻塞,或者是用户界面的组件。类使用工作者线程包括:ChannelManager, SocketMonitor, P2PTransportChannel, 和Port 对象。为了激活第二个线程,你必须建立和加入一个新的线程对象到SessionManager 构造器。
除此以外。Libjingle提供了一个基类叫做:SignalThread。扩展这个类来激活一个对象,该对象已经存在于自己的线程里,而且能够被实例化,启动,单独留下来完成和删除它自己当完成以后。
Note: Although libjingle supports multiple threads, only certain methods support thread safety by verifying the calling thread, and very few methods do any locking. The following snippet demonstrates how a method verifies which thread it is being called on:
Libjingle包括所有的线程,信令线程,工作者线程,和其他的线程,使用talk_base::Thread
对象,所有的线程对象被ThreadManager所管理,当请求时能够重新得到他们。SessionManager调用ThreadManager::CurrentThread 提供一个信号的线程,当被实例化的时候,XmppPump使用当前的线程作为它的信号线程、因此,你必须为信令线程建立一个线程对象,然后将其加入到ThreadManager的线程池中,在创建一个SessionManager 对象之前。或者在之前能期望XmppPump 开始工作。这里有两种方法来建立线程:
AutoThread This wraps the existing operating system thread with a libjingle Thread object and makes it the current thread in the ThreadManager object's thread pool (that is, will return the thread if Thread::CurrentThread is called).
Thread This creates and wraps a new thread to use, typically for a worker thread. In order to use this thread, 你必须建立一个新的线程对象,调用 ThreadManager::Add or ThreadManager::SetCurrent 增加到池中, 然后调用 Run来启动循环阻塞或者启动线程监听。
线程为信息在对象之间传递提供了一个管道。例如:SocketManager 发送一个消息给它自己在另外一个线程上以毁灭一个套接字,或者发到SessionManager, 当连接的等待者已经产生的时候。线程对象继承了MessageQueue ,能够一起公开的Send,Post 和其它的方法来使得发送信息的同步和异步。一个对象收到一个信息发送使用MessageQueue 必须继承和实现了MessageHandler 方法。MessageHandler 定义了OnMessage 的方法,它能够被调用,使用MessageQueue 的消息。
命名规则:
OnSomeMethod 以on开头的方法常常连接到一个信号,或者是一个对象。如果调用自同一个工程,它可能调用于其它的线程
SomeMethod_w 方法名以“with”结尾,存在于工作者线程。
SignalSomeName 这些信号能发送信息称作回调方法。
SSL Support
Libjingle支持两种类型的ssl,OpenSSl(for unix)Schannel(for windows)
使用SSL,你必须运行一下的steps;
#define FEATURE_ENABLE_SSL (in the Visual Studio project, this value is defined in the project settings, not in the code )
确保SSL_USE_OPENSSL 或者 SSL_USE_SCHANNEL 定义在ssladapter.cc. 这些定义是默认的,取决于你的操作系统的编译设置。
3.调用InitializeSSL 来初始化必要的组件,这些功能被定义在ssladapter.cc ,当应用程序关闭时,调用CleanuoSSL。你不必调用InitializeSSL 线程(它被InitializeSSL在内部使用)。
连接:
一个libjingle点对点的连接由两个通道组成:
会话协商信道(又被称作信令通道)是通信的连接装置常用来协商数据的连接。这种通道常用来请求一个连接,交换候选人,协商会话的细节(例如套接字的地址,需要的代码,文件的交换,连接改变请求,中断请求)。这是第一个连接在两台电脑间,当连接数据的通道被建立以后。
数据信道携带了真实的数据(音频,视频,文件等等)交换在点对点的会话中。数据信道的数据被包含在TCP和UDP的包中,取决于传输的协商,并且不能通过XMPP服务器。
会话协商信道首先应该被建立,在计算机协商数据信道的细节时。在数据连接被建立以后,大多数活动发生在数据通道里,除了偶尔的请求由于编码的改变,一个新的文件请求,一个中断的请求。
在上面这幅图中有两个可选的数据路径,不过在一次连接中只有一个数据通道能被激活,因为数据通道要么是一个直接的连接(92%d的连接是直接的)或者通过中间服务器(8%的服务器需要一个中间的服务器)。
上面的这个表格中显示了在一个点对点传输中的数据通道,当libjingle应用程序和远程计算机协商了一个连接,它会建立一系列潜在的连接点,称作候选人,在本地的计算机。本地的候选者被端口对象绑定,它能够被PortAllocator 的子类所分配。
候选人:
Libjingle的一种重要功能是它能够协调连接通过防火墙和NAT设备。Libjingle使用ICE程序来连接通过一个防火墙。Libjingle应用程序在尝试连接时的所做的第一步是为其它的计算机产生一系列的潜在的本地端口地址。每一个这样的潜在地址都称作候选人。候选人是IP:port配对,使得其它的计算机能够连接。Libjingle为找到有效的本地候选人提供了一个健壮的机制,其它的计算机都能进入,即使需要通过NAT设备或者防火墙。
为了提供尽可能多的潜在的连接地址给其他的计算机,libjingle产生了三种类型的本地候选人。
Local IP addresses 一个候选人是一个本地的IP地址在计算机上的,其它的计算机共享了同样的网络也应该能进入到这个地址中。
Global addresses 另一个候选人是一个外部的在NAT或者防火墙,它位于在两台计算机之间。如果这是一个外部的NAT设备,libjingle使用STUN来使得NAT绑定到你的计算机上,并且公开一个全局的地址。这个地址常被用来作为一个候选人来连接外部的NAT设备。
Relay server addresses 大概有8%的客户在通过防火墙时采用上面的方法尝试连接时会失败,这就有了第三种方法,通过一个转换的服务器,位于两个防火墙之间。
下面这幅图显示了两台计算机产生当地地址候选人(C1),外部NAT候选人(C2),转换服务器候选人(C3)
Libjingle储存了大量的潜在的候选人,以致于即使一个连接被建立以后,它能够迅速地转换到一个新的连接,如果现在的连接缓慢或者破坏了。
二:libjingle应用程序是怎样工作的
一个libjingle主要由三个主要的模块组成,再加上用户的界面。
应用程序:
在启动时,应用程序必须能够明确地创建SessionManager,一个PortAllocator 的子类,一个SessionClient 的子类,一个登录处理,和其它的来自会话逻辑管理的客户对象。
XMPP 消息组件
这个组件是一个位于网络和程序之间的网关,每次呼入都要通过这个组件,并且会路由到会话逻辑管理组件。每次呼出都被路由从会话逻辑管理组件到外部网络。这个组件被一个高级的任务对象所管理,在这显示为“登录处理”。样本应用程序使用XmppPump作为处理者。这个处理者管理XmppClient,它能够执行真正的发送和接收。XmppPump也管理其它的帮助者任务对象处理特殊的任务,例如监听通知和发送即时通知。呼入会话协商和信息节能通过SessionManagerTask 路由到SessionManager ,SessionManager 把他们放入合适的会话对象中去。
会话逻辑管理组件:
这个组件主要为每一个会话类型处理特殊的逻辑请求,这个组件主要针对不同的应用程序,大多数的客户类可能在这个组件中。这个组件能够将呼入的节点加入到点对点的组件中,它能够处理大多数的连接信息,能读和写数据从这个组件到文件,说话者,麦克风,或者其它的对象。被这个组件处理的细节任务包括:传输客户会话描述从XMPP到内部的结构,接受或者拒绝呼入连接请求,初始化连接请求,建立和管理捕获者/报告者以及其它的硬件和软件资源.
这个组件主要包括以下几个类:
SessionClient:理解高水平的任务对于所有的会话。你必须扩展这个基类来转换特殊的会话提供描述(例如证明,文件传输,或者是声音的编码),在XMPP和任何你所使用的内部的数据结构之间。
Resource Allocator/Resource Classes :是一种额外的特别针对会话类型的类。这些包括媒体描写和捕获类,管理或者监视类,是必须的。这些类中间的一个将对一个请求TransportChannel 来自Session的负责,使用它发送和接受数据。
Session: 是一个高水平的封装对于session negotiation channel 。每个Session对象有一个或多个传输对象,用来创建数据通道。你不必扩展这个类,但是你可能想要创建一个封装的对象。Session对象使用SessionManager来创建,为呼入连接请求,它们被自动的创建当SessionClient提出一个呼入连接的请求到SessionManager,为呼出请求,会话封装请求对象的建立。
Custom Session Wrappers 有时常被用来做处理那些单独会话的任务,文件共享的例子。
Peer-to-Peer Component
这个组件管理这个连接,在本地和远程计算机之间。它发送和接收对等的数据通过网络,产生本地的候选人,分配套接字,管理和监视连接的质量。数据通道通过P2PTransport对象来建立和管理,该对象提供一个P2PTransportChannel,它是一个数据发送和写入的终端。Libjingle提供了几个端口的类型包括TCP,UDP,SSL。P2PTransportChannel概括了数据的发送和接受到一个简单的读和写命令。