在Vovida的基础上实现自己的SIP协议栈_第1页
在Vovida的基础上实现自己的SIP协议栈_第2页
在Vovida的基础上实现自己的SIP协议栈_第3页
在Vovida的基础上实现自己的SIP协议栈_第4页
在Vovida的基础上实现自己的SIP协议栈_第5页
已阅读5页,还剩103页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、在Vovida的基础上实现自己的SIP协议栈(一)卢政 2003/08/01写在前面的话不少通讯方面的同好已经读了我在去年岁末撰写的如何用OpenH323开发自己的H.323协议栈,大都给予了很高的评价,甚至可以说是好评如潮,说来惭愧,我只不过把十几个人的工作进行了整理和归纳而已,事实上我自己的代码只有很少的一部分(主要在H.245/H.235部分),后来很多朋友向我索要RTH323的测试版本一直未果,我在这里说明一下,由于该软件的使用和二次开发的权利已经被某欧洲公司所买断,所以我已经无权发布测试代码,如有不便敬请大家原谅.在我们开发RTH323之际,我已经开始注意SIP协议了,并且根据RFC

2、2543设计了不少实验代码,因为当时的开发有一个H.323-SIP的翻译网关的需求,不过后来这个计划又取消了,也从这个时候开始我逐渐对SIP有了比较浓厚的兴趣,只是并没有做什么实际的工作,仅仅初步了解了一下协议的整体构造.直到去年年底,我接触了Vovida的开放原码的SIP系统-Vocal以后,我决定开始系统的了解SIP的整个构造,我个人认为Vocal是一个非常典型的SIP系统,里面包含了所有构造电信级呼叫中心,区域网关以及中继网关的所有内容,而且代码清晰,比较易于改造;于是我花费了大概6个月的时间阅读了整个Vocal系统的代码,并在很多重要的地方做了标注.正好在今年的5月份,我的公司有构造一

3、个大型的Voice/Video IP企业呼叫中心的计划,我就把我对Vocal的研究成果提交给公司,方案得到通过,现在我的公司正在和国内某个知名大学合作准备在现有Vocal基础上改造一个企业级的视频电话呼叫平台不过我个人而言,对这个计划不是非常的满意,由于时间和金钱上的限制,大部分的电讯级补充服务第一阶段没有实现的,只可能在第二阶段去实现该计划,时间可能要持续很长,所以我也很希望有其他的开发公司参与完成这个方面的开发,当然我们也可以为有兴趣的公司提供技术咨询或者展开合作。我写这本文的目的在于公布我个人对Vocal这个开放原代码系统的一些研究成果,当然里面有很多地方没有说得非常清楚,本身要用文字来

4、阐述程序的设计思想就是一个非常困难的事情,所以我在里面大量的绘制了很多图表,来帮助读者阅读本文,这次公布的是有关UA端的内容,后续在本文中将介绍Vocal系统中的Provision Server; Marshal Server; Redirect Server; HeartBeat Server:Policy Server:CDR Server:Network Manager:Feature Server:以及各种协议的Translator,读者需要具备对SIP,H.323,MGCP,QoS的基本了解,以及对Java,XML, Call Processing Language,C+的知识,在以

5、下章节中不会对这些基本的知识做太详细的介绍。预计本文全部刊登完毕大概需近一年的时间,我希望有软件开发公司来和我合作完成这篇文章或者是共同从事Vocal系统的二次开发工作。从这半年的阅读Vocal的过程中我体会到Vocal系统的商业应用价值非常大,有人在论坛上和我讨论:改造别人的应用平台的难度和重新开发一个应用的难度哪个更大,双方都各置一词,就我个人觉得,大型的商业软件如果按照开放原代码来进行改造,特别是一些基础平台(例如操作系统),速度肯定是比从零开始要快很多的,RTH323就是一个成功的范例,这也是国内很多软件厂商的基本运行模式,特别对于一些人员,技术,资金都不是非常充裕的公司这可能也是唯一

6、的方案,但是开放原代码的软件大部分的效率非常低下,而且代码冗余,注释比较少,可读性非常差,甚至还有一些致命错误(Vocal中的Feature Server中就有这样的错误,往往可以造成系统崩溃)所以这样做的前提条件就是能把握住工作的重点,能及时发现并排除问题,这样才可能把开放原代码改造成高效率的商业应用。目 录一楔子二H.323和SIP之间的差异三本文的主要内容1User Agent的简介2UA部分主要程序部分的介绍21 主程序:SIPUAUA.cxx22 创建一个User Agent的实体2. 3 HeartLessProxy的创建24 让User Agent Run起来2. 5 Heart

7、LessProxy Run方法的实现2. 5. 1 WorkerThread的Run方法2.5.1.1 processSipEvent2. 5. 1. 2 processUaDeviceEvent2.5.1.3 processUaDigitEvent 2. 5. 2 SipThread的Run方法2. 6 在User Agent中的四个重要实例的Run方法2. 6. 1 媒体设备启动2.6.2 启动RTP线程,用于对RTP/RTCP包的接收和发送管理;2. 6. 3 合法用户列表的获取(Redirection Server专用)2. 6. 4 监测线程:2. 6. 5 自动呼叫3.开

8、始一个呼叫和等待对方呼叫:3. 1 系统创建StateIdle状态:3. 2 开始一个呼叫:3. 2. 1 OpStartCall主程序部分:3. 2. 2 取得键盘的事件3. 2. 3 状态机(State)对各个操作(Operator)的处理过程:3. 2. 4 开始一个呼叫所经历的各种操作(Operator)3. 2. 5 如何进入待机状态(Idle状态)3. 2. 6 如何开始拨号并且开始一个呼叫:3. 2. 6. 1 OpStartDialTone本地发送拨号音; 3. 2. 6. 2 OpAddDigit输入电话号码开始拨号:3. 2. 6. 3 OpStopDialTon

9、e;3. 2. 6. 4 OpInviteUrl建立一个INVITE消息并且发送到被叫;3.2.7 进入Trying状态3. 2. 7. 1 OpStartTimer启动每个事件的定时器:3. 2. 7. 2 挂机事件的检测机制3. 2. 7. 3 OpStartRingbackTone向被叫进行铃声回放。3.2.7.4 OpReDirect进行重定向服务的操作3.2.7.5 授权检查3.2.7.6 OpFarEndAnswered处理接收到的OK回应3.2.7.7 在Vocal中如何实现RSVP资源预留协议 3.2.8用户处于通话的StateInCall状态:3. 2. 8. 1

10、OpStartAudioDuplex主叫打开RTP通道3. 2. 8. 2 处理RTP/RTCP包:3.2.8.3 ACK消息的处理过程OpAck3. 2. 8. 4 OpConfTargetOk多方会议检测:3.2.9 呼叫等待3. 2. 9. 1 呼叫等待的详细描述:3. 2. 9. 2 操作之间存在的竞争3. 2. 9. 3 呼叫中所涉及模块介绍3.3 等待对方的呼叫3.3.1 OpRing等待对方的振铃消息3. 3. 2 OpStartRinging开始响铃3. 3. 3 OpRingingInvite处理又一个INVITE消息(呼叫等待)3. 3. 4 OpAnswerCall被叫打

11、开媒体通道开始通讯3.3.5 回到StateInCall状态4.如何在改造现有的终端使之能传递视频流。4.1一个H.261+的Codec的基本构造4. 2 增加视频能力所需要做的工作一引言在各种的IP网络多媒体通讯协议中,当前在市场上占据主流位置的应当算是ITU的H.323和IETF的SIP两个协议,目前在单纯的话音市场,MGCP协议由于有大规模的用户扩容能力应用也正呈现上升的趋势,在2000年以前市场上占主流的主要是H.323协议,然而SIP协议由于它避免了复杂的原语(ASN.1)分析,它的应用也在2000年以后也得到高速的普及,甚至有超过H.323的趋势,成为H.323最有力的竞争对手,当

12、然,由于SIP协议的一些固有缺陷(下面将会详细介绍这些缺陷),这种情况在未来的几年可能不会出现,不过对于中等规模的多媒体通讯业务(每小时接入60000门)应用而言,采用SIP不失为一个方便,快捷的开发策略。在各种的VOIP开放原码的开发项目中,Vovida的基于SIP协议的VoCAL(Vovida Open CommunucAtion Library)不仅仅是在基于SIP的开放原代码协议栈中是最为庞大而且完善的,甚至在所有的原码开放的多媒体通讯协议栈中同样也是完善而且全面的,目前发布的VOCAL1.4.0主要支持RFC2543,据称在新版本的Vocal1.5.0将支持RFC3261协议;Voc

13、al提供了基本的SIP呼叫控制和切换,例如:用户注册和登记,呼叫初始化,修改呼叫特性,或者重新定义呼叫特性,终止呼叫;以及一些用户的基本呼叫特性:例如呼叫前转,呼叫等待,呼叫阻塞,呼叫转移,语音邮件等等。对于一个Vocal系统的用户而言,Vocal同样为其提供了以下的一些能力:1 通过Web来配置整个Vocal系统;2 使用SNMP网管来检测整个系统和呼叫组网的状态;3 可以定义一个用户的呼叫特性列表(相当于H.323系列中的H.450补充协议部分);4 授权检查;5 广告信息;6 基于RSVP的简单QoS保证。同时,VOCAL也提供了详细的文档和SDK包以给用户做二次开发,用户可以在C+,以

14、及Call Processing Language(CPL),Java Telephony API上开发自己的应用。二H.323和SIP之间的差异:这一节和本文的内容似乎没有太大的关系,不过笔者认为作为市场主流的H.323和SIP之间需要做一个相关性的比较,以免使很多读者在选择协议的时候陷入歧途。虽然Vocal在SIP的应用上已经可以算是一个成功的实例,所以目前单纯以Vocal作为主体开发SIP的多媒体通讯系统从理论上是可行的,但是事实上目前所有的VOIP商业系统都是以H.323为主体,兼容SIP协议,似乎还没有一个厂商在实际上支持SIP(Cisco好象有类似的产品,不过应用前景似乎不是非常明

15、朗),首先从市场上来看,H.323的系统已经有大量的投资,应用也非常普遍,SIP相对比较新,似乎不够成熟;从市场上来看,越来越多的附加服务将成为应用的主流,SIP领域相对来说比H.323能够提供更多,更灵活的服务,而且在信令的互通性上有更加多的优势,当然H.323也能够保证其他解决方案之间的互用性;但是,目前MGCP协议已经得到了大量的工业支持,简单的终端和更加复杂完善的呼叫控制方式让它得到了更多的应用,很可能会成为SIP的潜在竞争者。其次我们从ITU和IETF的条款保证上来看,IETF所制定的草案一开始都阐述一个让人失望的观点:本草案作为参考资料是不合适的,除非在"制定和完善中&q

16、uot;,这样在协议成熟和完全理解期间必然会把一些工作引入误区,特别是某些协议被更新和升级期间。相对而言,作为官方实体的ITU制定的协议一旦实施就不再轻易的改变,因此在发布协议前,已经对协议作了很长时间的互通性测试。最后从技术上来看,SIP和H.323在技术实现上有很大的不同:a开发速度:SIP当然的优于H.323协议太简单了,不过如果H.323原语部分可以比较好的解析的话,事实上两者开发速度相差不大。b多播:在这个方面IETF具有优势,有非常强大的应用经验的,SIP已经设计在很多多播的骨干网络上,h.323v1,v2要使用多单播同时进行的方式才能完成,不过H.323V3版本多播的支持就已经非

17、常不错了。c地址的运用上SIP使用Url上的机制非常灵活,这样可以让SIP以一种非常灵活的方式重定向到非SIP服务器上去,被另外一个SIP呼叫的SIP终端也能重定向到某个网页或者是电子邮件地址。对于H.323而言,命名的机制就非常混乱了,从ASN.1的文件我们可以看到有h323-ID,url-ID,transport-ID,email-ID,partynumber等等。d对于SIP而言,所有的消息都采用文本编码,所以SIP消息非常简单,这样在开发的时候简单的网络检测就可以调试,反观H.323协议采用了PER或者BER的二进制编码方式,信令不是非常直观。e.系统资源的消耗上,SIP可以说是开销惊

18、人,每次服务器发出通告的时候,都需要建立一个监听套接字,这样的结果势必造成大量的闲置套接字,假设在建立一个完整的Proxy/Register/RTP Gateway/三者和而为一的园区出口网关的时候,资源上势必会非常的紧张,这个是不能不予以考虑的问题。相反H.323在打开逻辑通道的情况下(OpenLogicalChannel消息)只建立一个套接字。f. SIP没有会议控制能力,所以仅仅只能做到点对点的媒体通讯,而H.323一开始就考虑了会议功能,其中还包含了H.332会议控制协议。(Vocal提供了一个Conferencing Server可以做普通的会议控制)。g. 基于无线的网络而言,H.

19、323有很大优势,由于信令采用了二进制编码,所以比较适合手持设备实现,而SIP由于采用了文本方式就没有这样的能力。三本文的主要内容和RTH323的介绍一样,我在下面将会尽量详细的分析一下Vocal的整个原代码,当然不可能做到完全系统地向大家展示Vocal的精妙之处,其实我自己对这个协议栈还是有相当多不了解的地方,希望大家能对我们的研究提出宝贵意见,后续的文章将会以连载的方式对Vocal进行介绍,顺序如下:3.1 Vovida User Agent:3.2 Vovida Provision Server:3.3 Marshal Server;3.4 Redirect Server;3.5 Hea

20、rtBeat Server:3.6 Policy Server:3.7 CDR Server:3.8 Network Manager:3.9 Feature Server:3.10 Translator Server:我们现在可以开始进入Vovida的第一个实体的介绍-User Agent(点击放大)User Agent1User Agent的简介:User Agent是描述一个普通的用户终端,用户代理,以下都简称UA端。本身来说UA端的代码在Linux或者是Windows上都可以编译运行。在Vocal中资料最详细是User Agent的介绍了,有关UA描述的所有的代码部分部分集中在SIPUA

21、目录下面,SIP的Stack软件主要集中在SIPSIPSTACK,SIP消息和状态的基类描述主要集中在SIPBASE;大家如果对SIP的状态和命令不是非常熟悉的话,可以进入SIPUA目录下浏览以下的几个线图:1 UaOverView.gif:对于UA中全部的主要类的关系描述,主要是展现了一些比较重要的基类。2 UaSimpleStatesComplet.gifUA端的一个简单的呼叫和应答的全部命令和状态的交互示意图。3 Ua-States.gifUA状态迁移的示意图。另外在UA中我们会把基本的SIPStack的一些调用做一下详细的介绍,所以篇幅可能会比较长。下面是一些所使用到的SIP基本的类的

22、介绍:HeartlessProxy :创建了一个容纳呼叫的"容器",和SIP的消息堆栈,以及WorkThread和SipThread(用于对SIP消息的队列处理),由HeartlessProxy:Run()方法调用这写线程的Run方法,使他们启动。该类的初始化是用一个Builder的基本类对它进行实例化。BasicProxy:由源自HeartlessProxy,它让系统使用HeartBeat机制,在这个类中创建了三个HeartBeat类型的线程:HeartbeatTxThread,HeartbeatRxThread,HouseKeepingThread,不过暂时在Ua中都没

23、有应用到,一般是用在HeartBeat Server中(注:HeartBeat的机制就是指在Vocal的Server集群通过多播端口向HeartBeat Server发送HeartBeat数据报,如果在指定的时间内没有收到该数据报,那么认为该服务器处于Down状态,由HeartbeatServer发送状态消息到SNMP网管,同时启动备份设备,这个机制类似于BGP,EIGRP协议中的后备路由方案)。SipThread:SipThread源自ThreadIf(Thread Interface),主要作用是接收并且在sipstack对所收到的SIP消息排队,并且对接收的SipMsg(SIP消息)产生

24、相应的Sip本地处理事件SipEvent,并且把他们放置在一个Fifo队列中等待处理,SipThread:thread()是循环处理的线程。WorkerThread:和SipThread一样,它也是源自ThreadIf,主要作用在于接受并且列队处理上面SipThread收到的Sip本地处理事件SipEvent。Builder:是一个基本类,它由WorkerThread调用,在这个类中包含了针对用户代理的CallContainer(包含所收到的各种呼叫信息)类的指针,从代码上看Builder在HeartLessProxy/BasicProxy被创建的时候创建。Feature: Feature是一

25、个状态(State)容器,用于装载各种状态,从所有的状态上来说,Feature是所有State的集合,Feature,State,Operator之间的关系是一种容器包含的关系,在Feature:process()中会调用State:process()来完成各个状态的处理,它会返回下一个在容器内要处理的状态(Sate)。.State: State是Operator的集合容器, State:process() 调用 Operator:process().和上面所描述的一样Operator返回在容器内的下一个操作(Operator)。Operator : Operator则是一个基本类,Opera

26、tor:process() 是一个虚函数,他需要其他的Operator子类对它进行实例化,它实质上也是描述各种操作的一个基本类。SipProxyEvent: 这个是一个基本类,用于描述各种SIP的事件信息,包括各种SIP消息和各种本地的设备消息,同时它包含了SIP消息的输出队列指针,使用的时候,可以把他载入输出的FIFO中。SipEvent: 从SipProxyEvent上继承,用于描述各种SIP消息,使用中当SipThread收到一个SipMsg的时候创建一个SipEvent,同样SipEvent也会安装在输出的FIFO中(Outputfifo)。DeviceEvnet: 从SipProxy

27、Event上继承,用于描述本地设备事件。TimerEvent:从SipProxyEvent上继承,在设定的时钟超时的时候产生该事件。CallContainer: 是CallInfo类的容器类。CallInfo: 是一个基本类,用于对呼叫的各种信息的描述集合,任何一个SipProxyEvent都包含了一个CallInfo。CallProcessingQueue: 一个用于装载各种SipProxyEvent消息的FiFO队列,在构造HeartLessProxy的它被创建,WorkerThread对他中间的消息进行排队处理。FeatureThread:被Marshal Server调用用于发送接受s

28、ubscribe/Notify消息对,得到合法的用户的列表和呼叫特性(在后面介绍marshal和Feature Server的时候会详细介绍)。ResGwDeviece: 所有设备的基本类,用于描述所有的设备,当然它中间的很多属性需要具体的设备进行实例化。(点击放大) 下面介绍一下在UA端所使用到的基本类:UserAgent: 用于描述一个基本的用户代理,它通过Run方法启动以后打开了用户端的RTP通道和设备媒体设备处理进程,并且启动了HeartlessProxy的Run方法,开始启动SIP消息处理线程。DeviceThread: 用于处理各种媒体设备,以及输入输出设备的线程,将收到

29、的设备消息放在CallProcessingQueue队列中。RTPThread: 用于处理RTP/RTCP会话。SubScribemanager: 用于MS端发送subscribe消息到RS端,以及接收Notify消息,处理用户的呼叫特性列表。UaCallContainer: 继承CallContainer类,主要在Ua端使用,定义了UA呼叫的各种信息的集合。SipTransceiver: SIP消息的发送和接收器的描述,包含有一个接收缓冲队列和发送缓冲队列。UaBuilder: Builder类在Ua端的描述,继承了Builder的各种描述,这个是UA端的一个重要的类,它负责构建各种SIP消

30、息事件,并且在这里包含了UA的注册和各种状态机的初始化和实例化过程。UaConfiguration: CFG文件的描述类。UaCallInfo:属于CallInfo的子类,包含了UA的各种工作状态,可以让不同状态下的所有所有的Call操作(发送和接收),使用一个相同的状态机。RegisterManager:用于跟踪处理UA端的注册。LoadGenThread:检测线程,用于大量的呼叫时候对系统的检测.(点击放大) 其实这几个线图对UA的描述还是非常粗糙,如果大家对UA端的代码没有任何的阅读的话,看他们是完全看不懂的,这些只能是一些内部开发人员专用文档而已,下面我们开始对UA原代码部分

31、做详细的介绍:2UA部分主要程序部分的介绍:21 主程序:SIPUAUA.cxx主程序部分主要是根据CFG文件中的定义建立本地的呼叫和等待接收进程。main( int argc, char* argv ) /是否把当前的UA设置为守护进程。由CFG文件确定。if ( UaCommandLine:instance()->getBoolOpt( "daemon" ) )/ TODO set cpLog to use syslogassert( Daemon() >= 0 ); if (UaCommandLine:instance( ) -> getIntOpt

32、( "retransmit" )SipTransceiver:reTransOn();elseSipTransceiver:reTransOff();if (UaCommandLine:instance()->getIntOpt("retransinitial") != 500 |UaCommandLine:instance()->getIntOpt("retransmax") != 4000 )SipTransceiver:setRetransTime(UaCommandLine:instance()->getIn

33、tOpt("retransinitial"),UaCommandLine:instance()->getIntOpt("retransmax");/这里是打开配置文件,我们在这里使用的配置文件暂时定为Ua1001.cfg(以下均相同)const string cfgStr = UaCommandLine:instance()->getStringOpt( "cfgfile" );FILE *cfgFile = fopen( cfgStr.c_str(), "r");if ( cfgFile = 0 )c

34、err << "can not open " << cfgStr << endl;cerr << "Usage: " << argv0 << " " << appUsage << endl;exit( 0 );elsefclose( cfgFile );UaConfiguration:instance( cfgStr );/ if the config file has a log level, do something about iti

35、f(UaConfiguration:instance()->getLogFilename() != "")int retval = cpLogOpen(UaConfiguration:instance()->getLogFilename().c_str();if(retval = 0)cpLog(LOG_ALERT, "Could not open %s", UaConfiguration:instance()->getLogFilename().c_str();/创建一个Uabuilder类,它在类状态图中的位置可以参考Ua

36、overview.gif中它的位置,Sptr < UaBuilder > uaBuilder = new UaBuilder;/在这里创建一个用户代理,我们要确定它在本地的侦听和发送的端口,我们同样从CFG文/件中得到。UserAgent ua( uaBuilder, Data( UaConfiguration:instance()->getLocalSipPort() ).convertInt() );ua.run();if ( UaCommandLine:instance()->getBoolOpt( "voicemail" ) )/下面的两个R

37、un我们暂时不定义,在SNMP网管的时候将介绍HearterBeat的时候在做/详细的阐述(所谓的HeartBeat技术是在多播口上定时发送heartbeat消息,以通知目前/端点的状态,类似于BGP协议中的Hello消息。#if defined(HAS_VOICEMAIL)cpLog( LOG_DEBUG, "UA is running as voicemail front end" );if( !UaCommandLine:instance()->getBoolOpt("no_heartbeat") )#if defined(HAS_HEART

38、BEAT)/ Create and start heartbeat transmit threadSptr < HeartbeatTxThread > heartbeatThread = new HeartbeatTxThread(sipPort,500,(const char*)"226.2.2.5",6000);heartbeatThread->run();heartbeatThread->join();#endif #elsecpLog( LOG_ERR, "UA is NOT compiled to run as

39、 voicemail front end" );#endif /加入UA端到本地的运行队列里面ua.join();return 0; / ua main()看完了UA的主程序,我们可以看到,目前在UA端的部分的主要工作就是创建一个User Agent的实体UserAgent然后调用Run方法让它运行,那么我们看一下UserAgent这个关键类的一些基本情况:(点击放大) 22 创建一个User Agent的实体:在创建一个User Agent的实体的同时还有一个非常重要的实体HeartLesProxy,用于处理SIP的各种消息,并且开启后台工作线程;它的创建过程我

40、们稍后做详细介绍。UserAgent:UserAgent( Sptr uaBuilder, unsigned short sipPort, Data appName ): HeartLessProxy( uaBuilder, sipPort, appName )const char* useDevice = "NULL" / default to NULL_HARDWARE /用什么声音设备?在这里我们暂时定为Sound Card好了,如果要是用Quicknet来集成/各种压缩算法当然更好,不过价格也高了一些,下面都以Sound Card作为标准介绍。if( Ua

41、CommandLine:instance()->getBoolOpt( "soundcard" ) ) useDevice = "SOUNDCARD"  /LOAD GENERATION是一个检测线程,可以在屏幕上打印各种命令的往复消息,以及系统的各/种统的状态。if(! UaConfiguration:instance()->getLoadGenOn()/ Create devices only if load gen is turned OFF./设备实例化,并且向带入设备名称以及所要处理的消息队列,在实例化

42、的过程中会/打开一个声卡设备,并且将这个声卡设备绑定两个输入的命令,输出命令的FIFO /队列当中,(inputQ,和outputQ)详细可以参看SoundCardDevice的建构函数,它/阐述了如何绑定这两个队列inputQ,outputQ,并且初始化ResGwDevice(所有的声音/设备的父类)。UaDevice:instance( useDevice, myCallProcessingQueue );if( ! (strcmp(useDevice, "NONE") = 0) )cpLog( LOG_DEBUG, "Create RTP Thre

43、ad" );/创建一个RTP包处理线程用于对RTP Packet的处理rtpThread = new RtpThread( UaDevice:instance() );assert( rtpThread != 0 );/这里调用了sound Card的设备消息处理线程的创立,用于处理与声卡设备相关的各种消/息.deviceThread = new DeviceThread( UaDevice:instance() );assert( deviceThread != 0 );cpLog( LOG_DEBUG, "Create SubscribeManager" );

44、Sptr subManager = new SubscribeManager( mySipStack );if ( UaConfiguration:instance()->getSubscribeOn() )cpLog( LOG_DEBUG, "Create Feature Thread" );/这里建立一个向FS发送消息的线程,关于这个部分的内容在Feature /Server的部分再做详细介绍.featureThread = new FeatureThread( subManager );assert( featureThread != 0 );uaBu

45、ilder->setSubscribeManager( subManager );else  / 是否打开重传机制?if (UaCommandLine:instance( ) -> getBoolOpt( "retransmit" ) )SipTransceiver:reTransOn();elseSipTransceiver:reTransOff();/ 定义接收代理服务器(Proxy)发出的消息所储存的容器myCallContainer = new UaCallContainer;assert( myCallContainer != 0 );/绑定

46、容器到用户端uaBuilder->setCallContainer( myCallContainer );/设置SIP的消息堆栈uaBuilder->setSipStack( mySipStack );/开始向注册服务器发送注册(Register)消息。uaBuilder->startRegistration();23 HeartLessProxy的创建:HeartLessProxy( const Sptr < Builder > builder,unsigned short defaultSipPort,Data applName,bool filte

47、rOn, bool nat,SipAppContext aContext)myCallContainer = new CallContainer;myBuilder = builder;myBuilder->setCallContainer(myCallContainer);/这里创建了一个消息的输出队列,在前面的创建一个UserAgent的实体的过程中已经/阐述过会把它绑定到相关的设备上去myCallProcessingQueue = new Fifo < Sptr < SipProxyEvent > >/这里创建一个WorkThread线程在该线程中

48、的myBuilder->process(nextEvent)/检查消息队列myFifo中的返回消息(调用Uabuilder->process进行检查),从而/得到返回的消息。/很明显,这里新创建了一个 myWorkerThread工作线程,我们等一下就会看到如何把它Run/起来myWorkerThread = new WorkerThread(myCallProcessingQueue, myBuilder);/创建一个SIP消息收发器的实体,在这个实体的构建里主要是把收发SIP消息的TCP/UDP/的收发通道创建(SipUdpConnection和SipUdpConnection

49、)。同时会构造一个SNMP的/SipAgent.他的主要作用是向SNMP网关发送SNMP消息,描述网络的运行状态if ( filterOn = true )mySipStack = new SipTransceiverFilter(applName, defaultSipPort, nat, aContext);elsemySipStack = new SipTransceiver(applName, defaultSipPort, nat, aContext);myBuilder->setSipStack(mySipStack);/创建一个SIP消息的解析线程 。mySipThread

50、 = new SipThread(mySipStack, myCallProcessingQueue);  24 让User Agent Run起来:构建User Agent的工作已经完毕,现在应该让调用它的Run方法了;从下面的程序可以看到,Run方法的调用,让整个程序进入一种"Idle"的状态,等待命令输入和状态的产生,这个过程我们可以看到在Ua.CXX的Main程序中调用(ua.run())。Void UserAgent:run()/调用HeartLessProxy的Run方法,稍后做详细的介绍HeartLessProxy:run();  devi

51、ceThread->run(); /调用SoundcardDevice:hardwareMain(0)  rtpThread->run();/调用SoundCardDevice:processRTP()进行RTP流的处理 /在这里向FS发送队列(myQ = new Fifo < Sptr < SubscribeMsg > >)中的各种消息,不/过在Ua1001.cfg中,参数Subscribe_on设置为OFF所以,本章我们对FS暂不予以分析,/在最后一章详细分析FS的时候回着重分析它.featureThread->run();/调用subs

52、cribeManager:subscribeMain() /后台监测线程开启.loadGenThread->run();/调用LoadGenMonitor:lgMain()/ User TimerEvent to kick start the load generator / UserAgent:run在Vovida的基础上实现自己的SIP协议栈(二)卢政 2003/08/042 5 HeartLessProxy Run方法的实现HeartLessProxy:run()myWorkerThread->run();mySipThread->run();通过上面可以看到有两个Ru

53、n方法的调用,第一个是WorkThread的Run方法,它的主要作用是处理UaBuilder的Process方法,主要用来处理Sptr < Fifo < Sptr < SipProxyEvent > > > myFifo中的各种事件,前面已经详细的介绍了SipProxyEvent类的作用,这个类已经在前面介绍了,其实简单的说,它就是一个本地的各种事件的集合。现在我们来看一下两个Run方法的实现:2.5.1 WorkerThread的Run方法:UaBuilder:process( const Sptr < SipProxyEvent > next

54、Event )/处理以下的四种事件/ SipEventSptr < SipEvent > sipEvent;sipEvent.dynamicCast( nextEvent );if ( sipEvent != 0 )/处理本地的SIP的事件,包括对状态机的设置和命令/状态队列返回的操作在下面将/对它做详细的介绍if( processSipEvent( sipEvent ) )return;/向消息队列myCallContainer中插入相应的事件信息。sendEvent( nextEvent );return;/ UaDeviceEventSptr < UaDeviceEve

55、nt > uaDeviceEvent;uaDeviceEvent.dynamicCast( nextEvent );if ( uaDeviceEvent != 0 )/处理本地的设备事件,最主要的就是处理摘机信号;if( processUaDeviceEvent( uaDeviceEvent ) )return;sendEvent( nextEvent );return;/ UaDigitEventSptr < UaDigitTimerEvent > uaDigitEvent;uaDigitEvent.dynamicCast( nextEvent );if ( uaDigit

56、Event != 0 )/处理在规定的时间间隔(Kickstart)主动呼叫事件的触发。if( processUaDigitEvent( uaDigitEvent ) )return;sendEvent( nextEvent );return;/ UaTimerEventSptr < UaTimerEvent > uaTimerEvent;uaTimerEvent.dynamicCast( nextEvent );if ( uaTimerEvent != 0 )/在各种SIP命令的回应产生了超时事件后,系统的事件触发。例如:/在StateTrying()中addEntryOpera

57、tor( new OpStartTimer )在myEntryOperators队列中加入/该Operator(指一个操作,例如呼叫或者是进入等待),这里我们这个Operator在时间到达/以后户会被OpTimeout:process的方法检测到(isTimeout(event)进行检测,对StateTrying/整个状态进行检测,也就是Trying事件),最后如果UaTimerEvent事件被触发,那么,/就会调用:stateMachine->findState( "StateError" )这个状态,进入错误状态,实施错误的/处理机制,同时向myEntryOperators队列中加入一个新的Operator-OpStartErrorTone,/从而被processUaTimerEvent过程扑捉到,最后通过

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论