exosip2协议栈学习总结1、exosip2协议栈介绍eXosip是Osip2的一个扩展协议集,它部分封装了Osip2协议栈,使得它更容易被使用。
使用 sip 协议建立多媒体会话是一个复杂的过程,exosip 库开发的目的在于隐藏这种复杂性。
正如它的名称所表示的,eXosip2 - the eXtended osip Library,它扩展了osip 库,实现了一个简单的高层API。
通过使用exosip,我们可以避免直接使用osip 带来的困难。
需要注意,exosip 并不是对osip 的简单封装包裹,而是扩展。
Osip 专注于sip 消息的解析,事务状态机的实现,而exosip 则基于osip 实现了call、options、register、publish 等更倾向于功能性的接口。
当然,这些实现都是依赖于底层osip 库已有的功能的。
2、exosip的模块构成2.1 底层连接管理extl.c、extl_udp.c、extl_tcp.c、extl_dtls.c、extl_tls.c 是与网络连接有关的文件。
实现了连接的建立,数据的接收以及发送等相关的接口。
其中,extl_udp.c 为使用UDP 连接的实现,extl_tcp.c 为使用TCP 连接的实现。
Extl_dtls.c 以及extl_tls.c 都是使用安全socket 连接的实现。
2.2 内部功能模块实现Jauth.c、jcall.c、jdialog.c、jevents.c、jnotify.c、jpublish.c、jreg.c、jrequest.c、jresponse.c、jsubscribe.c 等文件实现了内部对一些模块的管理,这些模块正如其文件名所表示的,jauth.c主要是认证,jcall.c 则是通话等等。
2.3 上层API 封装实现Excall_api.c、exinsubsription_api.c、exmessag_api.c、exoptions_api.c、expublish_api.c、exrefer_api.c、exregister_api.c、exsubsribtion_api.c 这几个以api 为后缀的文件,实现各个子模块的管理。
应用程序可以调用这里提供的接口,方便的构造或者发送sip 消息。
2.4 其他Inet_ntop.c 实现ip 地址的点分十进制与十六进制表示之间的转换。
Jcallback.c 实现一堆回调函数,这些回调函数就是用来注册到osip 库的。
我们使用exosip 库,就是避免直接使用osip 库,因为一些工作exosip 已经帮我们做了,所以这样一来,可以简化上层的实现。
Udp.c 文件主要用来对通过UDP 连接接收到的消息进行分类处理。
Exutilis.c 文件实现一些杂项的函数。
有ip 地址到字符串之间的转换,域名的解析等一些辅助的功能函数。
Exconf.c 文件实现了exosip 的初始化相关的接口,包括后台任务的实现。
实际上是“configuration api”的实现。
Exosip.c 文件实现了与exconf.c 文件相似的功能。
比如管道的使用,exosip 上事务的创建和查找,register 和subscribe 的更新,认证信息的处理等。
3、exosip关键数据结构及其说明3.1 eXtl_protocoleXtl_protocol 是为实现网络通信专门定义的一个数据结构,包括了变量和方法两部分。
其中,变量包括了建立网络连接过程中使用的ip 地址、端口等;方法部分封装了网络socket编程常用的系统调用接口。
代码中定义了四个该数据结构体的全局变量:eXtl_udp、eXtl_tcp、eXtl_tls 以及eXtl_dtls。
分别针对使用UDP、TCP 以及安全加密连接进行了实现。
3.2 eXosip_call_tExosip_call_t 定义了call 相关的信息,包括call 的id,call的dialogs,call 上incoming的事务和outgoing 的事务。
另外,还包括了前向和后向指针,所以,所有的call 可以通过该结构体串接起来。
3.3 eXosip_dialog_texosip_dialog_t 包含了dialog 相关的信息。
3.4 eXosip_reg_t用来管理Register模块3.5 eXosip_subscribe_t用来管理subscribe模块3.6 eXosip_pub_t用来管理publish模块3.7 eXosip_notify_t用来管理notify模块3.8 jinfo_t这个结构体关联了dialog、call、subscribe以及notify几个结构体3.9 eXosip_event_t与 event 有关的结构体。
这个结构体主要用来在应用层和exosip 之间通信。
Exosip 在处理事务的过程中,如果需要将结果反馈给上层应用,则会生成如上结构类型的事件,并将其放到exosip 的事件队列中。
应用层会不断循环从事件队列中读取事件,然后进行应用层的处理。
3.10 eXosip_texosip_t 是exosip 中最重要的结构体之一。
从图可以看出,这个结构体比较大,其中包含了exosip 中用到的各个子模块的结构。
比如call、reg、pub 等等。
代码中定义了一个该结构类型的全局变量,通过该全局变量,就可以对exosip 前的状态进行掌控(许多相关的信息要么包含在该结构上,要么可以通过该结构找到)。
Extl 是eXtl_protocol 类型的指针,保存了网络接口类。
J_osip 保存了osip 初始化时返回的osip 结构体。
J_transactions 一般是等待释放的事务。
在事务经过osip 处理完后,不再需要时,exosip会将其放在j_transactions 上,等待释放。
4、exosip的初始化Exosip 的初始化有两部分组成,这主要是从使用exosip 的角度看。
4.1 对exosip 全局结构体变量的配置这步通过调用接口eXosip_init 完成。
主要完成工作如下:4.1.1 初始化条件变量和互斥信号量。
4.1.2 调用osip_init 初始化osip 库,并将生成osip 结构体给exosip,同时也让osip 的application_contexgt 指针指向exosip,也就是二者相互指向。
4.1.3 调用eXosip_set_callbacks 设置osip 的回调函数,所以回调函数都是exosip 自己实现。
4.1.4 调用jpipe 创建通信用的pipe,之前已经说了,对于windows 平台,是通过socket 接口模拟实现的。
4.1.5 初始化其上的事务和事件队列。
主要,这不同于osip 的事务和事件队列。
4.1.6 调用extl 指向的结构体的init 函数指针,初始化网络接口。
4.2在socket 接口上进行监听这步通过调用eXosip_listen_addr 接口完成。
主要完成工作如下:4.2.1 将eXosip 全局变量的eXtl 指针指向eXtl_udp 全局变量。
4.2.2 根据参数,配置extl_protocol 和exosip 上有关ip 端口地址等信息。
另外,调用extl_udp的tl_open 函数指针,完成在本机指定的端口上监听连接的工作。
需要注意的是,虽然是监听,但是使用的UDP 来建立连接的,所以消息的recv 和发送在同一个socket 上完成。
在osip中设置的out_socket 并不会起作用。
4.2.3 调用osip_thread_create 创建exosip 后台任务,用于驱动osip 的状态机。
(在osip 中,在发送sip 消息部分,提到将9 个函数放到一个线程中执行,exosip 就是这样做的)下面展示了初始化的示例代码:include <eXosip2/eXosip.h>int i;TRACE_INITIALIZE (6, stdout);i=eXosip_init();if (i!=0)return -1;i = eXosip_listen_addr (IPPROTO_UDP, NULL, port, AF_INET, 0);if (i!=0){eXosip_quit();fprintf (stderr, "could not initialize transport layer\n");return -1;}... then you have to send messages and wait for eXosip events...这样,在初始化完成后,我们基本上完成了对内存中所用数据结构的配置,同时启动了一个后台任务负责osip 状态机的驱动。
5、数据收发整体框架5.1 接收过程在初始化过程中我们创建了一个后台任务,现在可以看看这个后台任务都做了哪些操作。
任务的执行函数为_eXosip_thread,在该接口中,循环不断的调用eXosip_execute。
在每一次的eXosip_execute 执行中,完成如下的工作:a. 首先计算出底层osip 离当前时间最近的超时时间。
也就是查看底层所有的超时事件,找出其中的最小值,然后将其与当前时间做差,结果就是最小的超时间隔了。
这步是通过调用接口osip_timers_gettimeout 完成的。
主要检查osip 全局结构体上的ict、ist、nict、nist 以及ixt 上所有事务的事件的超时时间。
如果ict 事务队列上没有事件,则说明没有有效的数据交互存在,返回值为默认的一年,实际上就是让后面的接收接口死等。
如果有事务队列上的事件的超时时间小于当前值,则说明已经超时了,需要马上处理,此时将超时时间清为零,并返回。
b. 调用eXosip_read_message 接口从底层接收消息并处理。
如果返回-2,则任务退出。
c. 执行osip 的状态机。
具体为执行osip_timers_ict(ist|nict|nist)_execute 和osip_ict (ist|nict|nist)_execute 这几个函数。
最后还检查释放已经终结的call、registrations 以及publications。
d.如果keep_alive 设置了,则调用_eXosip_keep_alive 检查发送keep_alive 消息。
这样,当远端的终端代理发送sip 消息过来时,会被之前创建的监听端口捕获(sip 协议默认的端口为5060)。
在调用eXosip_read_message 接口时会将其接收上来。