已阅读5页,还剩9页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
oSIP协议栈(及eXoSIP,Ortp等)使用入门2007年09月26日 星期三 14:16一直没空仔细研究下oSIP,最近看到其版本已经到了3.x版本,看到网上的许多帮助说明手册都过于陈旧,且很多文档内容有点误人子弟的嫌疑下的编译使用应该是很简单的,其说明文档里也介绍的比较清楚,本文主要就在平台下开发环境下的使用作出描述。虽然的开发人员也说明了,只使用了标准开发库,但许多人在下使用时,第一步就被卡住了,得不到的库和库,也就没有办法将使用到自己的程序中去,所以第一步,我们将学习如何得到的静态和动态链接库,以便我们自己的程序能够使用它们来成功编译和执行我们的程序。第一阶段:先创建新工程,网上许多文档都介绍创建一个动态链接库工程,我们这里也一样,创建一个空白的工程保存。同样,将版本 src目录下的目录下的所有文件都拷到我们刚创建的工程的根目录下,在上操作: 将所有的源程序和头文件都加入到工程内,保存工程。这时,我们可以尝试编译一下工程,你会得到许多错误提示信息,其内容无非是找不到osipparser2/xxxxx.h头文件之类。处理:在下,我们一般是将头文件,库都拷到/usr/inclue;/usr/lib之类的目录下,源程序里直接写i nclude 时,能直接去找到它们,在里,同样的,最简单的方法就是将源码包中的目录下的osipparser2目录直接拷到我们的下默认包含目录即可,这个目录在的里设置,(当然,如果你知道这一步,也可以不用拷贝文件,直接在这里把源码包所在目录加进来就可以了),默认如果装在盘,目录则为C:Program FilesMicrosoft Visual StudioVC98Include。这时,我们再次编译我们的工程,顺利编译,生成osipparser2.dll,这时,网上很多文档里可能直接就说,这一步也会生成libs目录,里面里osipparser2.lib文件,但我们这里没有生成:)最简单的方法,不用深究,直接再创建一个工程,同上述创建动态链接库方法,创建一个静态链接库工程,直接编译,即可得到osipparser2.lib。上面,我们得到了的解析器开发库,下面再编译完整的协议栈开发库,同样照上述方法,分别创建动态链接库工程和静态链接库工程,只是要拷的文件换成src下的osip目录下文件和include下的osip目录,得到osip2.dll和osip2.lib。在编译osip2.dll这一步可能会再次得到错误,内容含义是找不到链接库,所以,我们要把前面编译得到的osipparser2.lib也拷到osip工程目录下,并在中操作:中的: kernel32.lib user32.lib . xxx.lib之类的内容最后增加: osipparser2.lib保存工程后再次编译,即可成功编译osip2.dll。至此,我们得到了完整的开发库,使用时,只需在我们的程序里包含的头文件,工程的链接参数里增加osipparser2.lib和osip2.lib即可。下面我们验证一下我们得到的开发库,并大概了解一下的语法规范。在VC里创建win32控制台程序工程,将libosip源码包的SRC目录下的Test目录内的C源程序随便拷一个到工程时,直接编译(工程设置里照前文方法在link选项里增加osip2.lib,osipparser2.lib引用我们之前成功编译得到的静态库文件)就可以运行(带参数运行,参数一般为一个文本文件,同样从Test目录的res目录里拷一个与源文件同名的纯文本文件到工程目录下即可)。该目录下的若干文件基本上是测试了Osip的一些基本功能函数,例如URI解析之类,可以大概了解一下oSIP的语法规范和调用方法,同时也能校验一下之前编译的OSIP开发库能否正常使用,成功完成本项工作后,可以进入下一步具体的oSIP的使用学习了。由于是比较底层的协议栈实现,新手较难上手,而官方的示例大都是一些伪代码,需要有实际的例子程序参考学习,而最好的例子就是同样官方发布的的扩展开发库exosip,使用可以很方便地快速创建一个完整的程序(只针对性地适用于SIP终端开发用,所以我们这里只是用它快速开发一个SIP终端,用来更方便地学习oSIP,要想真正掌握SIP的开发,需要掌握oSIP并熟读RFC文档才行,exoSIP不是我们的最终学习目的),通过成功编译运行一个自己动手开发出的程序,再由浅入深应该是初学都最好的学习方法通过对使用exosip开发库的使用创建自己的程序,熟悉后再一个函数一个函数地深入学习exosip提供的接口函数,就可以深入理解osip 了,达到间接学习的目的,同时也能从eXoSIP中学习到正确使用的良好的编程风格和语法格式。而要成功编译,似乎许多人被难住了,直接在上,用,虽然你使用了推荐的winsock2.h,但是会得到一个sockaddr_storage结构不能识别的错误,因为vc6自带的开发库太古董了,需要升级系统的,下载地址如下:/msdownl . PSP2FULLInstall.htm (的支持已经停止,这是能使用的最新)成功安装后编译前需加OSIP_MT宏,以启用线程库,否则在程序中使用eXoSIP库时会出错,而编译时也会得到许多函数未定义的Warning提示,编译得到exosip2.lib供我们使用,当然,在此之前需要成功编译了osip2和osipparser2,而在之后的实际使用时,发现也需要增加宏,否则调用的线程库时会出错,所以我们需要重新编译了:),因为是基于的(同上方式创建静态和动态链接库工程,并需在中手工添加和的lib库)。创建新工程,可以是任意工程,我们从最简单的控制台程序开始,为了成功使用,我们需要引用相关库,调用相关头文件,经过多次试验,发现需要引用如下的库: exosip2.lib osip2.lib osipparser2.lib WSock32.Lib IPHlpApi.Lib WS2_32.Lib Dnsapi.lib其中,除了我们上面编译得到的三个oSIP库外,其它库都是系统库,其中有一些是新安装的所新提供的。至此,我们有了一个简单的开发环境了,可以充分利用网上大量的以为基础的代码片段和官方说明文档开始具体函数功能的测试和使用了:)我们先进行一个简单的纯信令(不带语音连接建立)的的终端的程序开发的试验(即一个只能作为主叫不能作为被叫的的软电话模型),我们创建一个应用程序,对话框模式,照上面的说明,设置工程包含我们上面得到的的相关开发库及的一些开发库,并且由于默认的冲突,需要排除MSVCRTD开发库(其中代表模式下,没有表示模式下),直接使用的几个主要函数就可以创建一个基本的软电话模型。其主要流程为:初始化库启动事件监听线程向注册向某终端(电话号码)发起呼叫建立连接结束连接初始化代码: int ret = 0; ret = eXosip_init (); eXosip_set_user_agent(#YouToo0.1); if(0 != ret) AfxMessageBox(Couldnt initialize eXosip!n); return false; ret = eXosip_listen_addr (IPPROTO_UDP, NULL, 0, AF_INET, 0); if(0 != ret) eXosip_quit (); AfxMessageBox(Couldnt initialize transport layer!n); return false; 启动事件监听线程: AfxBeginThread(sip_uac,(void *)this);向注册: eXosip_clear_authentication_info(); eXosip_add_authentication_info(uname, uname, upwd, md5, NULL); real_send_register(30);/* 自定义函数代码请见源码 */发起呼叫(构建假的描述,实际软电话使用它构建媒体连接): osip_message_t *invite = NULL; /* 呼叫发起消息体 */ int i = eXosip_call_build_initial_invite (&invite, dest_call, source_call, NULL, # YouToo test demo!); if (i != 0) AfxMessageBox(Intial INVITE failed!n); char localip128; eXosip_guess_localip (AF_INET, localip, 128); snprintf (tmp, 4096, v=0rn o=josua 0 0 IN IP4 %srn s=conversationrn c=IN IP4 %srn t=0 0rn m=audio %s RTP/AVP 0 8 101rn a=rtpmap:0 PCMU/8000rn a=rtpmap:8 PCMA/8000rn a=rtpmap:101 telephone-event/8000rn a=fmtp:101 0-11rn, localip, localip, 9900); osip_message_set_body (invite, tmp, strlen(tmp); osip_message_set_content_type (invite, application/sdp); eXosip_lock (); i = eXosip_call_send_initial_invite (invite); eXosip_unlock ();挂断或取消通话: int ret; ret = eXosip_call_terminate(call_id, dialog_id); if(0 != ret) AfxMessageBox(hangup/terminate Failed!); 可以看到非常简单,再借助于和开发库,来快速为我们的软电话增加和与系统语音接口交互及语音编码功能,即可以快速开发出一个可用的软电话,关于和的相关介绍不是本文重点,将在有空的时候考虑增加相应使用教程,后面跟贴中可以下载基本可用的完整软电话的源码工程文件供参考使用,完全,欢迎转载,但请在转载时注明作者信息,谢谢!第二阶段:得到了一个软电话模型后,我们可以根据软电话的实际运行表现(结合用Ethereal抓包分析)来进行代码的分析,以达到利用eXoSIP来辅助我们学习的最终目的(如要快速开发一个可用的软电话,请至前面提到的论坛去下载使用和快速搭建的一个基本完整可用的软电话#YouToo 0.1版本的源码工程文件作参考)。现在从eXosip的初始化函数开始入手,来分析oSIP的使用,这是第二阶段,第三阶段就是深入学习oSIP的源码了,但大多数情况下应该没有必要了,因为在第二阶段就有部分涉及到第三阶段的工作了,而且的源码也就大多是一些数据的语法解析和状态机的实现,能深入理解了协议后,这些只是一种实现方式,没必要完全去接受,而是可以用自己的方式和风格来实现一套,比如,更轻量化更有适用目的性的方式,则只起参考作用了。eXosip_init()是的初始化函数,我们来看看它的内部实现:首行是定义的osip_t *osip,这在oSIP的官方手册里我们看到,所有使用oSIP的程序都要在最开始处声明一个osip_t的指针,并使用osip_init(&osip)来初始化这个指针。我们可以在代码中看到很多OSIP_TRACE,这是调试输出宏调用了函数osip_trace,可以用ENABLE_TRACE宏来打开调试以方便我们开发调试。其它就是很多的eXosip_t的全局变量eXosip的一些初始化操作,包括最上面的memset (&eXosip, 0, sizeof (eXosip)完全清空和下面的类似eXosip.user_agent = osip_strdup (eXosip/ EXOSIP_VERSION)的exosip变量的一些初始值设置,其中有一个eXosip.j_stop_ua = 0应该是一个状态机开关,后面可以看到很多代码检测这个变量来决定是否继续流程处理,默认置成了0表示现在exosip的处理流程是就绪的,即ua是not stop的。osip_set_application_context (osip, &eXosip)是比较有意思的,它让下面的eXosip_set_callbacks (osip)给osip设置大量的回调函数时,能让osip能访问到eXosip这个全局变量中设置的大量程序运行时交互的信息,相当于我们在下开启一个线程时,给线程传入的一个指针指向我们的应用程序的当前dialog对象实例,可以用void *osip_get_application_context (osip_t * osip)这个函数来取出指针来使用,不过好象exosip中并没有用到它,可能是留给个人自已扩展的吧 还能看到初始化代码前面有一段平台下的的初始化代码,可以知道eXosip是用的原生的winsock api函数,也就是我们可能以前学过的用VC和WINAPI写sock程序时(不是),用到的那段初始代码,还有一段有意思的代码,就是jpipe()函数,它们返回的是一个管道,一个有个整型数值的数组(一个进一个出),查看其代码发现,非WIN32平台是直接使用的pipe系统函数,而下则是用一对的本地连接来模拟的管道,一个写一个读,这段代码是比较有参考价值的:)j = 50;while (aport+ & j- 0)raddr.sin_port = htons (short) aport);if (bind (s, (struct sockaddr *) &raddr, sizeof (raddr) transactionid);即,只是打印一下调试,并没有完整实现什么功能,我们学习时,完全可以用相同的方法,定义一大堆回调函数,并不忙想怎么完全实现,先都是只打印一下调试信息,看具体的应用逻辑根据抓包测试分析和看调试看程序走到了哪一步,调用了哪一个回调,来明白具体回调函数要实现什么用途,再来实现代码就方便多了,当然,如果看透了文档,应该从字面就能知道各个回调函数的用途了,这是后话,不是谁都能快速完全看懂的,所以我们要参考eXosip:)我们对其中的重要的回调函数进行逐个的分析:osip_set_cb_send_message (osip, &cb_snd_message)消息发送回调函数这个函数可能是最重要的回调函数之一,消息发送,包括请求消息和回应消息,一般情况下,状态机的状态就是由它控制的,发起一个消息初始化一个状态机,回应一个消息对状态机修改,终结消息发送结束状态机看cb_snd_message的函数实现,要以发现,其主要代码是对参数中的要发送的消息osip_message_t * sip进行分析,找出消息要发送的真实char *host,int port的值(这些参数可以省略,但要发送消息肯定需要host和port,所以要从sip中解析),最后根据sip中解析出的传输方式是TCP还是UDP选择最终进行消息发送处理的函数cb_udp_snd_message,cb_tcp_snd_message处理(它们的参数一致,即本函数只是补全一些省略的参数并对消息进行合法性检查)。*毕竟eXosip是一个通用的开发库,它考虑了要支持TCP,UDP,TCPs,IPV4,IPV6,WIN32,*nix,WINCE等等多样化的复杂环境,所以,我们可以略过我们暂时不需要的部分,比如,相关的代码实现等。由于我们大多数情况下是用的,所以先来看一下cb_udp_snd_message的实现,它从全局变量exosip中获取可用的sock,并尽最大能力解析出host和port(?难道前面的函数还不够解析彻底?如最终仍无port信息则默认设置为5060),使用osip_message_to_str (sip, &message, &length)函数将要发送的格式化的消息转换成能用传输的简单数据并发送即完成消息发送,代码中有许多复杂的环境探测和错误控制等等等等,我们可以暂时不用过多关注,可以继续向下,结尾处有一个keeplive相关代码,从代码字面分析,可能是的Register消息的自动重发相关代码,可以在后面再细化分析。cb_tcp_snd_essage的函数实现要比上文的udp的实现简单很多,主要是环境探测错误控制方面,因为毕竟tcp是稳定连接的,对比一下代码,可以看到主要流程还是将消息转换后,发送到从消息中解析出的host和port对应的目标。看完两个函数,可以知道,eXosip需要有两个sock,是一个数组,0是给用的,是给用的,要用当然要初始化,就是下文要介绍的eXosip的网络相关的初始化了,上面的exosip_init可以看成是这个开发库的系统初始化吧:)至些,我们应该知道了开发的应用程序的消息是从哪里发出的吧,对了,就是从这个回调函数里,所谓万事开头难,就象开发应用程序时,找到了程序的main函数入口下面的工作就好办了,下面就都是为一些事件消息开发对应的处理函数而已了:)osip_set_kill_transaction_callback事务终结回调函数对应,客户/服务器注册/非注册事务状态机的终结,主要是使用osip_remove_transaction (eXosip.j_osip, tr)将当前tr事务删除,再加上一系列的清理工作,其中,NICT即客户端的非事务的清理比较复杂一些,要处理的内容也比较多,可以根据实际应用的情况进行有必要的清理工作:)cb_transport_error传输失败处理回调对应于上面说到的四种事务状态机,如果它们在处理时失败,则在这时进行统一处理。从代码可知,只是在NOTIFY,SUBSCRIBE,OPTION操作失败才进行处理,其它错误可直接忽略。osip_set_message_callback消息发送处理回调根据type不同,表示不同的消息发送状态OSIP_XXX_AGAIN 重发相关消息OSIP_ICT_INVITE_SENT 发起呼叫OSIP_ICT_ACK_SENT回应OSIP_NICT_REGISTER_SENT发起注册OSIP_NICT_BYE_SENT发出OSIP_NICT_CANCEL_SENT发出OSIP_NICT_INFO_SENT,OSIP_NICT_OPTIONS_SENT,OSIP_NICT_SUBSCRIBE_SENT,OSIP_NICT_NOTIFY_SENT,OSIP_NICT_UNKNOWN_REQUEST_SENT我们可以看到,eXosip没有对它们作任何处理,我们可以根据自己需要,比如,重发2xx消息前记录一下日志之类的,扩展一下retransmission的处理方式,发起Invite前记录一下通话日志等等。OSIP_ICT_STATUS_1XX_RECEIVEDuac收到1xx消息,一般是表示对端正在处理中,这时,主要是设置一下事务状态机的状态值,并对会话中的osip的一些参数根据返回值进行相应设置,里面有许多条件判断,但我们常用的一般是100,180,183的判断而已,暂时可以忽略里面复杂的判断代码。OSIP_ICT_STATUS_2XX_RECEIVED uac收到2xx消息,这里主要跟踪一下Register情况下的2xx,表示注册成功,这时会更新一下exosip的注册字段值,以便让eXosip能自动维护uac的注册,的回应是终结消息,Invite的2xx回应,则主要是初始化一下会话相关的数据,表示已成功建立连接。其它,则分别是对应的处理,根据实现情况进行概要的查看即可。report_event (je, sip)是代码中用来进行事件处理的一个函数,跟踪后发现,其最终是使用了我们上文提到的管道,以便在状态机外实时观测状态机内的处理信息。OSIP_NIST_STATUS_XXX_SENT即对应于上面的uac的处理,这里是uas的对应的消息处理,相比较于uac简单一点。前面简单介绍了一下大量的回调函数及它们的概要处理逻辑,可能会比较混乱,暂时不用管它,只需要记得一个大概的形象,知道一个处理程序是通过osip_set_cb_send_message回调函数来实现真实地发送各种消息,并且的标准事务模型是由实现好了,我们只需要给不同的事务状态设置不同的回调处理函数来处理事务,具体的状态变化和内部逻辑不用管就可以了。下面来说一下消息处理回调函数用到的的初始化函数,即我们上面说的除了系统初始化外的网络初始化函数eXosip_listen_addr:从上文知道了,系统将初始化两个,一个一个,但查看代码发现还有第三个,的,但好象还不能实用,现在不管它,代码首先是根据传输是还是来设置对应的数组值,并且如果没有提供地址和端口号,系统会自动取出本机网络接口并创建可用的(http_port的方式暂不用考虑)。初始化后,如何开始事务的呢?看到这个调用eXosip.j_thread = (void *) osip_thread_create (20000, _eXosip_thread, NULL),对的,这里启用了一个线程,即,是调用的线程函数(没用系统提供的线程函数,是为了跨平台)进行事务处理的状态机逻辑是在一个线程中处理的,这样就明白了为什么一直没能看到顺序执行下来的程序启动代码了,接下去看,线程实际处理函数是_eXosip_thread,这里面的代码中,我们看到了上文提到的状态机控制开关变量while (eXosip.j_stop_ua = 0),即,当j_stop_ua设置为1时,osip_thread_exit ()结束事务处理即程序终结,再接下去看,_eXosip_execute是最终的处理函数了,而且它在程序未终结情况下是一直逻辑在执行。看到_eXosip_execute的代码中有很多时间函数和变量,仔细看,调用了osip_timers_gettimeout而不是系统为了跨平台,来实现定时器功能,除去一些控制代码,主要处理函数是eXosip_read_message (1, lower_tv.tv_sec, lower_tv.tv_usec),即取出消息,1表示只取出一条消息,其代码量非常的大,但同样的,其中也许多的控制代码和错误检测代码,我们在查看时可以暂时忽略掉它们。根据jpipe_read (eXosip.j_socketctl, buf2, 499),我们可以估计,buf2中应该是保存的我们的控制管道的数据,具体作用至些还没有表现出来,应该是用来反映一些状态机内部的警示之类的信息,实际的的处理的状态机的数据是存放在buf中,使用_eXosip_recvfrom获取的,获取后sipevent = osip_parse (buf, i)解析,使用osip_find_transaction_and_add_event (eXosip.j_osip, sipevent)来查询事件对应的事务状态机,找到后就如同其注解所说明的,/* handled by oSIP ! */,即我们上文设置的那一大堆回调函数,至此,我们知道了整个应用所处理的大概流程了。如果没有找到事务状态机呢?直接丢弃吗?不是的,如果这是一个回应消息,但没有事务状态机处理它,那它是一个错误的,要进行清理后才能丢弃,而如果是一个请求,那更不能丢弃了,因为UAS事务状态机要由它来启动创建的。先来看错误的回应消息的处理函数eXosip_process_response_out_of_transaction,可以看到其代码就是一大堆的赋值语句,XXX= NULL,即将一大堆的运行时变量清空,再调用osip_event_free清空事件,或者就是一些复杂的情况下,需要通过解析现在的运行时数据,从中分析出“可能”的正在等待回应的对端,并发送相关终结通知消息等等,可以根据实际需要进行简化。请求事件处理eXosip_process_newrequest,首先是对事件进行探测,MSG_IS_INVITE、MSG_IS_ACK、MSG_IS_REQUEST,对事件进行所属状态机分类,随后使用_eXosip_transaction_init (&transaction,(osip_fsm_type_t) tx_type,eXosip.j_osip, evt-sip)根据探测结果进行状态机初始化,实际调用的是osip_transaction_init,初始化后即将事件入状态机osip_transaction_add_event (transaction, evt),由状态机自动处理后调用相应回调函数处理逻辑了。线程调用的事件处理函数代码最后是if (eXosip.keep_alive 0)_eXosip_keep_alive ();这段代码印证了上文提到了,keep_alive是用来设置是否自动重新注册,由_eXosip_keep_alive函数来实现自动将eXosip全局变量中
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 物业树木维护合同范本
- 网络维修协议合同范本
- 物业代售蔬菜合同范本
- 物业大门改造合同范本
- 违规店铺转让合同范本
- 高价采购废铁合同范本
- 租房打理服务合同范本
- 美工设计转让合同范本
- 购房合同补充车位协议
- 食堂员工安全合同范本
- 2025年宠物市场细分需求分析报告:宠物食品行业品牌建设与产品创新竞争力研究
- 隧道施工机械设备配置方案
- 医药行业耗材保障及应急措施
- 简易提升机安全知识培训课件
- 水利工程标准化管理制度的构建与实施效果评估
- 学堂在线 研究生素养课-积极心理与情绪智慧 章节测试答案
- 工器具使用培训课件
- 体检安全知识培训内容课件
- 学堂在线 精确制导器术道 章节测试答案
- 2025年综合部人员笔试题及答案
- 2025年广东省税务系统遴选面试真题带答案详解
评论
0/150
提交评论