:
time_ttime>SignalDanger;
//任何对Panic()的调用将会发送SignalDanger信号
voidPanic(){
SignalDanger("Help!
",std:
:
time(0));
}
//监听类必须继承自sigslot
classReceiver:
publicsigslot:
:
has_slots<>{
//接收注册以得到SignalDanger信号。
//当SignalDanger信号发送,它被OnDanger()捕获。
//第二个参数给地址的侦听器函数类定义。
//第一个参数指出这个类的实例来接收通知。
Receiver(Sendersender){
sender->SignalDanger.connect(this,&Receiver.OnDanger);
}
//任何对Panic()的调用,将使Receiver:
:
OnDanger获得消息.
//注意,数量和类型的参数匹配
voidOnDanger(stringmessage,std:
:
time_ttime){
if(message=="Help!
")
{
//Callthepolice
...
}
}
...
}
代码中的许多类发送信号以通知重要事件的监听器,例如,例如当你发送或收到一个连接请求时Call:
:
SignalSessionState发送通知。
你的应用程序必须连接这些信号与适当的处理过程。
在libjingle代码中的一般惯例是在信号的名称前加Signal,例如SignalStateChange,SignalSessionState,SignalSessionCreate,监听方法如。
SignalStateChange,SignalSessionState,SignalSessionCreate。
用来连接到信号的监听器方法通常在前面加上On。
例如:
OnPortDestroyed(),OnOutgoingMessage(),OnSendPacket()。
看到sigslot文档<>为更多的细节。
二、Threads
为了提高应用程序的性能libjingle支持多线程。
libjingle组件使用一个或两个全局可用的线程:
一、signalingthread
signalingthread是一个用于创建所有基本组件的线程,例如SessionManager和控制以及XMPP消息传递组件。
二、workerthread
workerthread(在代码中有时称为channelthread)被对等组件对象(PeertoPeercomponentobjects)来处理更多的资源密集型处理,比如数据流。
把这些放在一个单独的线程中可以防止数据流阻塞或被XMPP或用户接口组件阻塞。
使用工作线程的类包括ChannelManager,SocketMonitor,P2PTransportChannel,以及Port对象。
为了启用第二个线程,您必须创建一个新的线程对象并传递到SessionManager构造函数。
(如果没有线程被传递,那个创建SessionManager的线程将被用作工作线程)。
CallClient:
:
InitPhone演示了如何创建一个工作线程的底层组件。
此外,libjingle现在提供了一个叫做SignalThread的基类。
扩展这个类能使一个类对象存在于自己的线程中,可以被实例化,开始,独自完成和在完成后删除自己。
关于更多信息看signalthread.h/.cc。
注意:
尽管libjingle支持多个线程,只有某些方法通过验证调用线程支持线程安全,并且很少有方法做任何锁。
下面的代码片段演示了一个方法怎样验证它正在哪个线程中调用:
//检查我们被调用的通道(如.,worker)线程。
ASSERT(talk_base:
:
Thread:
:
Current()==channel_thread_);
channel_thread_->Clear(this);
ibjingle使用talk_base:
:
Threadobject(oraderivedobject)封装了所有线程,信号线程,工作线程,和任何其他线程,。
所有线程对象由ThreadManager来管理,ThreadManager针对请求检索这些线程。
SessionManager调用ThreadManager:
:
CurrentThread以提供给它一个signalingthread(和一个工作线程,如果没有被提供)当它被实例化;XmppPump使用当前线程用于其信号线程。
因此,在创建一个SessionManager对象之前或者期望XmppPump开始工作之前您必须创建一个线程对象(或派生对象)信号线程并放入ThreadManager的线程池,(参见注册服务器例如代码。
)有两种方法可以创建一个线程:
三、AutoThread (自动线程)
ThiswrapstheexistingoperatingsystemthreadwithalibjingleThreadobjectandmakesitthecurrentthreadintheThreadManagerobject'sthreadpool(thatis,willreturnthethreadifThread:
:
CurrentThreadiscalled).
用一个libjingle线程对象封装现有的操作系统线程,并使它成为在ThreadManager线程池中的当前线程(即,如果Thread:
:
CurrentThrad被调用将返回这个线程)。
四、Thread
Thiscreatesandwrapsanewthreadtouse,typicallyforaworkerthread.Inordertousethisthread,youhavetocreateanewThreadobject,callThreadManager:
:
AddorThreadManager:
:
SetCurrenttoaddittothepool,andcallRuntostartitinablockingloop,orStarttostartthethreadlistening.
这将创建和封装一个新的线程,用于,通常为一个工作线程。
为了使用这个线程,您必须创建一个新的线程对象,调用ThreadManager:
:
Add或ThreadManager:
:
SetCurrent将它添加到池中,并调用Run以启动它在一个阻塞循环中,或者Start以启动线程监听。
Threadsprovideaconduitformessagesbetween(orwithin)objects.Forinstance,SocketManagersendsamessagetoitselfonanotherthreadtodestroyasocket,ortoSessionManagerwhenconnectioncandidateshavebeengenerated.TheThreadobjectinheritsMessageQueue,andtogethertheyexposeSend,Post,andothermethodsforsendingmessagessynchronouslyandasynchronously.AnobjectthatwillreceivemessagessentusingMessageQueuemustinheritandimplementMessageHandler.MessageHandlerdefinestheOnMessagemethod,whichiscalledwiththeMessageQueuemessages.
线程在对象之间(或对象内部)为消息提供了一个渠道。
例如,SocketManager给它自己发送一个消息在另一个线程中摧毁一个套接字,或者当连接的候选人(candidates)被生成时给SessionManager发送消息。
线程对象继承MessageQueue,和他们一起暴露Send、Post其他方法用于同步和异步发送消息。
一个要收到MessageQueue发送的消息的对象必须继承和实现MessageHandler。
MessageHandler定义了OnMessage方法,在收到MessageQueue发送的消息时被调用。
Youcansendmessagestoanyobjectthatinheritstalk_base:
:
MessageHandleroveranythread.However,ifsendingamessagetoperformaresource-intensivethread,youshouldsendthemessageovertheworkerthread.YoucangetahandletotheworkerthreadbycallingSessionManager:
:
worker_thread().YoucangetahandletothesignalingthreadbycallingSessionManager:
:
signaling_thread().
Anobjecthasseveralwaystoaccessaspecificthread:
itcanrequestandstoreathreadpointerasaninputparameter;itcanassumethatthecurrentthreadwhenitiscreated(accessedbyThreadManager:
:
CurrentThreadinitsconstructor)isaparticularthreadandcacheamemberpointertoit;itcancallSessionManger:
:
signal_thread()orSessionManager:
:
worker_thread()toretrievethreads.Allthreetechniquesareusedinlibjingle.
你能够给任何继承自talk_base:
:
MessageHandler的对象跨越任何线程发送消息,然而,如果发送一个消息给一个执行资源密集型的线程,你应当发送消息到工作线程。
你能够通过调用SessionManager:
:
worker_thread()以得到一个工作线程的句柄。
你能够通过调用SessionManager:
:
signaling_thread()获得一个信号线程的句柄。
一个对象有几种方式去访问一个具体的线程:
:
它可以请求并存储一个线程指针作为输入参数,它可以假定当前线程创建时(在其构造函数中访问ThreadManager:
:
CurrentThread)是一个特定的线程并使用成员指针指向这个线程,它可以调用SessionManger:
:
signal_thread()或SessionManager:
:
worker_thread()来检索线程。
所有三个技术都可以在用于libjingle。
Becauseanobjectcanbecalledonanythread,anobjectmayneedtoverifywhichthreadamethodisbeingcalledfrom.Todothis,callThread:
:
Current(whichretrievesthecurrentthread)andcomparethatvalueagainstaknownthread--thiscanbeoneofthethreadsexposedbySessionManager,ortheobjectcanstoreapointertoitsinitialthreadintheconstructor.Hereisamoreextendedexampleofcallingamethodinthesameobjectonanotherthread.
因为一个对象能够在任何线程中被调用,一个对象需要验证被哪一个线程的哪个方法中调用,要做到这一点,调用Thread:
:
Current(检索当前线程)并且根据一个已知的线程—可能通过SessionManager或者在构造函数中存储的线程初始化时的指针来进行值的比较。
这是一个较为扩展的示例,在不同的线程上同一个对象中调用一个方法。
//请注意,工作线程是没有初始化,直到有人
//调用PseudoTcpChannel:
:
连接
//Alsonotethatthismethod*is*thread-safe.
boolPseudoTcpChannel:
:
Connect(conststd:
:
string&channel_name){
ASSERT(signal_thread_->IsCurrent());
CritScopelock(&cs_);
if(channel_)
returnfalse;
ASSERT(session_!
=NULL);
worker_thread_=session_->session_manager()->worker_thread();
...
}
voidPseudoTcpChannel:
:
SomeFunction(){
...
//Postamessagetoyourselfovertheworkerthread.
worker_thread_->Post(this,MSG_PING);//<-Goesinhere....
...
}
//Handlequeuedrequests.
voidPseudoTcpChannel:
:
OnMessage(Message*pmsg){
if(pmsg->message_id==MSG_SORT)
OnSort();
elseif(pmsg->message_id==MSG_PING)//->Andcomesouthere!
//Checkthatwe'reintheworkerthreadbeforeproceding.
ASSERT(worker_thread_->IsCurrent());
OnPing();
elseif(pmsg->message_id==MSG_ALLOCATE)
OnAllocate();
else
assert(false);
}
五、NamingConventions
libjinglehassomenamingconventionsthatitisusefultobeawareof:
OnSomeMethod Methodsbeginningwith"On"areoftenconnectedtoasignal,eitherfromthisoranotherobject.Ifcalledfromthesameobject,itisprobablycalledonadifferentthread.
SomeMethod_w Methodsendingwith"_w"existinthe"workerthread"andarecalledfromanotherthread
SignalSomeName Thesearethesignalsthatsendmessagestocallbackmethods.
Libjingle有一些命名约定,知道这些是有用的:
OnSomeMethod方法以“On”开始,通常连接一个这个对象或另一个对象的信号,如果从同一个对象调用,它可能是调用一个不同的线程。
SomeMethod_w方法以“_w“结尾存在于工作线程中并且从另一个线程被调用。
SignalSomeName,这些是发送消息到回调方法的信号。
六、SSLSupport
libjingle支持两种类型的SSL:
OpenSSL(UNIX)
SChannel(对于Windows)
使用SSL,您必须执行以下步骤:
#defineFEATURE_ENABLE_SSL(在VisualStudio项目中,这个值是项目中定义的设置,而不是代码中).
EnsurethateitherSSL_USE_OPENSSLorSSL_USE_SCHANNELare#definedinssladapter.cc.Oneoftheseshouldbedefinedbydefault,dependingonthebuildsettingsforyouroperatingsystem.
确保SSL_USE_OPENSSL或SSL_USE_SCHANNEL被定义在ssladapter.cc中定义。
默认情况下其中一个应该被定义,根据构建设置您的操作系统。
CallInitializeSSLtoinitializerequiredcomponents.Thisfunctionisdefinedinssladapter.cc.Whentheapplicationclosesdown,callCleanupSSL.YoudonotneedtocallInitializeSSLThread(itisusedinternallybyInitializeSSL).
InitializeSSL调用初始化所需的组件。
该函数定义在ssladapter.cc中。
当应用程序关闭时调用CleanupSSL。
你不需要调用InitializeSSLThread(它被用于内部由InitializeSSL使用)。
七、Connections
Alibjinglepeer-to-peerconnectionactuallyconsistsoftwochannels:
Thesessionnegotiationchannel(alsocalledthesignalingchannel)isthecommunicationlinkusedtonegotiatethedataconnection.Thischannelisusedtorequestaconnection,exchangecandidates,andnegotiatethedetailsofthesession(suchas