1、osip源代码框架详解Osip协议源代码框架详解Prepared byMao minghuaDate2009.09.25Reviewed by DateApproved byDateRevision HistoryVersionAuthorReviewed ByCommentsIssued Date0.1Mao minghua描述osip协议栈的源代码框架1 符号及缩写 42 整体描述 43 Osip包的源代码框架解析 53.1 osip的transaction的event的产生 53.1.1 定时器事件的产生过程 63.1.2 报文触发的事件 73.2 osip 的transaction的e
2、vent处理流程 73.2.1 ICT的处理流程 83.2.2 IST的处理流程 93.2.3 NICT的处理流程 93.2.4 NIST的处理流程 93.3 Osip报文的解析 103.3.1 sip协议报文的解析整理流程 103.3.2 Osip报文头的解析 123.3.3 uri的解析 143.3.4 添加一个新的协议header字段 153.4 osip的transaction的管理 163.5 osip中dialog的管理 184 Exosip包的源代码框架解析 194.1 Lib库的初始化和销毁 204.2 Lib库的主处理线程 234.2.1 2xx应答的重发处理机制 244.2
3、.2 Exosip_execute执行流程 244.2.2.1 Exosip_read_message的处理 264.2.2.2 eXosip_process_response_out_of_transaction的处理流程: 294.2.3 eXosip_automatic_action处理流程 294.3 Call的处理 304.3.1 创建Call的第一个INVITE 304.3.2 INVITE的ACK应答的创建和发送 324.3.3 dialog内的请求的创建和发送 334.3.4 Dialog内answer的创建和发送 334.4 Register的处理 344.4.1 向一个服务
4、器第一次注册 354.4.2 调整一个注册的注册超时时间 354.4.3 发送一个register注册 35Osip源代码框架详解1符号及缩写缩写英文全称中文名称ICTInvite Client Transaction Invite类型的客户端事务ISTInvite Server TransactionInvite类型的服务端事务NICTNot Invite Client Transaction 非Invite类型的客户端事务NISTNot Invite Server Transaction非Invite类型的服务端事务IMSIP Multimedia SubsystemIP多媒体子系统PSV
5、TPacket service video telephony分组域可视电话SIPSession Initiation Protocol会话初始协议UDPUser Datagram Protocol用户数据报协议URLUniform Resource Locator统一资源定位器2整体描述开源代码的osip协议栈分为两个源代码包,整个协议栈采用lib库的形式,在内部没有使用到任务,采取与TCP/IP协议栈一样的策略,所以在使用上需要上层管理任务直接调用lib库提供的接口。因为在Lib库内部没有使用到像定时器、发送队列等的任务,而同时需要使用到定时器,所以在lib库的内部采用轮训遍历的方式不停的
6、检查是否有定时器超时,这在某种程度上会浪费CPU的允许时间。同时整个lib库实现了对call, notify等的管理,为了实现重入,在应用启用多线程的条件下,内部启用的信号量和锁的使用,在下面的分析中不涉及到信号量和锁机制。Lib库按照sip协议栈的层次关系分为两个lib包,底层的osip lib包实现对单个请求、应答、ACK的处理,包括message的解析、拼装、内容set和get,单个请求形成的transaction相关操作以及通信两端形成的一个dialog的操作。Lib库上层的exosip lib在底层osip lib库的实现基础上,实现对sip协议整理逻辑上的管理。Exosip主要关注
7、的是sip协议的业务流程,包括call的整体管理,notify的整体管理, publish的管理,register的管理,option的管理,refer的管理和subscription的管理,其中最主要的为call和register的管理,这两个为sip协议栈必须实现的部分,另几个功能为sip协议栈扩展部分。从这几个业务的管理流程出发,在业务的底层它们会使用到相似的一些功能,如注册的认证,发送message,接收message,每个请求和应答形成的transaction,多个transaction组合而成的dialog。在message的处理方面,可以分为两类,一类为发送的message,因为
8、是主动发送,所以上层管理层知道是什么类型的message,lib库直接提供各类接口供使用。一类为接收到的message,因为不知道是哪种类型,所以需要根据解析出来的message的信息来进行处理,这部分的处理在udp.c文件中。整个lib库的初始化在exOsip中介绍。3Osip包的源代码框架解析在osip源代码包中最主要的包括了message的相关操作,其中最重要的为message的解析,即从获取到的一个message中解析生成一个能够被代码直接处理的message数据结构osip_message_t。与message结构相关的操作包括根据message数据结构的信息安装sip协议规范组装成
9、一个message字符串;message结构的初始化和释放;message结构的拷贝操作;以及从message结构中获取各种已经解析的成员变量的值和设置各个成员变量的值。在message的解析部分,除了message的头之外,还包括了body的解析,涉及到sdp协议,包括对每个sdp字段的解析。在osip源代码包中,设计了一个与同一个请求相关的所有message的集合transaction,在发送或接收到一个新的请求的时候就会生成一个transaction,其中ACK和CANCEL请求是比较特殊的,对于非2xx应答的ACK和初始INVITE请求是属于同一个transaction的,而对于2xx
10、的请求是属于单独的transaction的,所以其重传操作由UAC来控制,而不在INVITE的transaction内部进行控制。CANCEL的请求除了本身建立一个transaction外,根据协议它还会去匹配要CANCEL掉的请求的transaction,如果匹配成功会CANCEL掉相应的transaction。在osip包中同样设计了dialog相关操作,包括dialog的建立,dialog信息的保存,dialog的匹配及删除等操作。其它方面,包括多线程中使用到的锁和信号量及信号,内部使用到的链表,用于事件的队列(需要先进先出策略),一些平台无关的封装,定时器以及常量等的定义。这部分比较简
11、单,而且都是最底层函数,直接封装了系统调用层。3.1osip的transaction的event的产生transaction的状态变化是由事件来驱动的,当transaction上有事件产生时,根据事件的类型和当前transaction的状态来处理该event。Transaction上的事件分为两类:一为定时器事件,在设定的定时器超时时会产生相应的定时器事件;另一类为事件驱动事件,如发送一个请求、应答或接收到一个请求、应答,发送一个ACK和接收到一个ACK,即是由报文产生的事件。3.1.1定时器事件的产生过程ICT、IST、NICT和NIST的定时器的事件产生流程都一样,对于每一个transac
12、tion,其定时器是有顺序的,ICT流程中TIMEOUT_B的优先级最高,TIMEOUT_B定时器触发后,会触发kill transaction的操作。当transactionff队列中有未处理的事件时,不处理定时器,直接返回,所以在transactionff队列中总的事件的数量是不多的。所有的定时器函数调用底层同一个定时器检查函数_osip_transaction_need_timer_x_event。该函数会先检查该定时器是否启动,判断条件为(timer-tv_sec = -1),如果启动,检查当前时间是否超过定时器中设定的时间,如果是,则产生新的定时器事件。因为定时器没有一个单独的任务,
13、所以是采样轮训的方式检查是否有新的定时器事件产生,而不是根据系统时钟中断进行检测,因此会比较占用系统资源。定时器的启动和修改使用接口osip_gettimeofday和add_gettimeofday。只需要设定定时器的超时时间,即设定了一个新的定时器。取消一个定时器,只需要修改定时器的timer-tv_sev为-1。3.1.2报文触发的事件包括一个新的invite、response、ack的发送或接收,除了对非2xx的应答ack外,其他的请求和应答都会产生一个新的transaction,并且产生一个新的sipevent事件。3.2osip 的transaction的event处理流程在sip
14、协议栈中为了更快更好的处理transaction,根据协议栈的描述,划分为四种不同的transaction,分别为ICT、IST、NICT和NIST。四种不同的transaction会有不同的处理流程和状态转换表,以及使用到不同的定时器。ICT、IST、NICT和NIST的状态转换采样注册函数处理方式,为便于管理和使用注册函数,源码中使用了四个全局变量管理四种不同类型transaction的转换表:ict_fsm、ist_fsm、nist_fsm和nist_fsm。osip结构如下:struct osipvoid *application_context; /* User defined Po
15、inter */* list of transactions for ict, ist, nict, nist */osip_list_t osip_ict_transactions; /* list of ict transactions */osip_list_t osip_ist_transactions; /* list of ist transactions */osip_list_t osip_nict_transactions; /* list of nict transactions */osip_list_t osip_nist_transactions; /*300时,cl
16、ient端发送ACK,当重复接收到该invite的response时,重发该ACK,确保server端在kill tranction前能接收到ACK。3.2.2IST的处理流程同ICT的处理流程,处理osip中的osip_ist_transaction链表。IST的相关event的注册处理函数在ist_fsm.c文件和ist.c文件。IST使用了定时器TIMEOUT_G、TIMEOUT_H和TIMEOUT_I。使用方式与ICTL类似,详细见协议栈说明。3.2.3NICT的处理流程同ICT的处理流程,处理osip中的osip_nict_transaction链表。NICT的相关event的注册处
17、理函数在nict_fsm.c文件和nict.c文件。NICT使用了定时器TIMEOUT_E、TIMEOUT_F和TIMEOUT_K。3.2.4NIST的处理流程同ICT的处理流程,处理osip中的osip_nist_transaction链表。NIST的相关event的注册处理函数在nist_fsm.c文件和nist.c文件。NIST使用了定时器TIMEOUT_J。3.3Osip报文的解析3.3.1sip协议报文的解析整理流程当接收到一个message的时候,需要解析该message,生成一个代码能够处理的数据结构,该结构定义为struct osip_message,该结构定义的一个messa
18、ge的全部相关信息,这些信息主要是供transaction和dialog及dialog的更上一层如call,notify等的使用。对一个message的解析流程如下图所示:在接收到一个message时,调用函数osip_message_parse进行message的解析。首先调用函数osip_util_replace_all_lws替换掉message中的连续出现的 rnt、rt、nt、rn 、r 、n 为空格,message是以0为结束标志的,message的headers和body之间的分界是以rnrn为标志的,替换只替换到rnrn为止,即只替换headers部分出现的t、r、n。由于si
19、p协议栈规定,每个headers都是起新行,而且新行的头一个字符不为空格或t,所以两个header之间的rn不会被替换掉,替换的只是一个允许multi合并项的header的内部多个值之间的“rnt”或“rn ”。举例如下:有两个header,其中Subject只允许单个值出现, Route允许有多个值出现,而且允许分行,但是分行必须以空格或t开头,而Subject和Route行必需顶格开始,前面是没有空格或t的,osip_util_replace_all_lws函数将Route header value中的两行间的rnt转化为空格,即在逻辑上就成为一行了。Subject: Lunch Rout
20、e: , 一个message由三部分组成,首先是message的startline部分,该行指明这是一个sip的message,包括sip标志,请求或应答说明,状态值,然后以rn做为和headers的分隔符。该rn不会被osip_util_replace_all_lws替换为空格,如请求的INVITE sip:bob SIP/2.0或应答的SIP/2.0 200 OK,在三个属性之间有且仅有一个空格。起始行的解析由_osip_message_startline_parse进行解析,解析得到message的类型,message的sipversion以及message的status_code,当s
21、tatus_code为初始化值0时,该message为一个请求,否则为应答。请求的startline由_osip_message_startline_parsereq进行解析,得到请求的request_uri;应答的startline由_osip_message_startline_parseresp进行解析。Startline部分的解析是严格安装出现的三个属性的顺序进行解析的,并将解析结果保存在osip_message的结构成员变量中。然后解析messge的headers部分,调用函数 msg_headers_parse。说明见osip的header报文头解析。如果message中在head
22、ers之后不是结束符0,则继续解析message的负载部分,调用函数msg_osip_body_parse进行解析。Message的body解析首先查询headers头解析中保存的content即body的属性:content_type,如果content_type中的type不为multipart,即不支持多种mime方式的content,说明body中就一个编码方式,直接将整个body解析为一个内容;如果type为multitype,说明有多个编码方式的body组合在一起形成一个整体的body,则以”-”为分隔符解析body,将body分为多个mime编码方式的字符串,每个解析后的body
23、内容保存在osip_message结构中的bodies结构成员中。3.3.2Osip报文头的解析在解析message的header的时候,因为前面的osip_util_replace_all_lws已经转化了单个header内部出现的r、n和t为空格,所以每个header之间可以使用rn做为分隔符进行分隔。如果字符串开头start_of_header已经到达结束符”0”,则全部header解析完毕,返回成功;调用_osip_find_next_crlf找到这个header的结束字符并保存在end_of_header中;如果start_of_header为r或n,则已经解析到rnrn即heade
24、rs的结束字符串,则返回成功,并且保存start_of_header到body中,即body是从rn字符串开始解析的,所以在body解析时,需要跳过rn及之后的空格部分;根据header内部分隔符“:”,取出header的hname和hvalue,其中hvalue在某些hname的情况下是允许为空的,然后调用osip_message_set_multiple_header来解析该header的hvalue字符串;解析成功后,置start_of_header为已经解析完的header的end_of_header,开始解析下一个header。在osip_message_set_multiple_h
25、eader中,将headers分为两类,一类如上面例子中的Subject,只允许一个值,则直接调用osip_message_set_header进行解析;一类如上面例子中的Router,允许多个值,根据sip协议,每个值之间以“,”进行分隔,所以需要查询整个hvalue字符串,根据”,”将hvalue分隔成多个值,每个值调用osip_message_set_header进行解析并保存解析结果到osip_message的数据成员变量中。因为hvalue允许使用引号将值引起来,所以需要特别处理“,”是否出现在引号内部的问题。只有在引号外部的“,”才是header值的分隔符,而内部的“,”只是一个h
26、eader值的一部分。osip源码中osip_message_set_header对于message headers的解析采用注册函数的方式实现,采用这种方式能够在后继版本很方便的进行新的header的添加,并且不会影响到整个源代码的框架流程。Osip_parser_cfg.c文件中定义了header头解析所使用到的全局管理变量:static _osip_message_config_t pconfigNUMBER_OF_HEADERS;_osip_message_config_t的结构定义如下:typedef struct _osip_message_config_t char *hname
27、; int (*setheader) (osip_message_t *, const char *); int ignored_when_invalid;_osip_message_config_t;hname为sip协议定义的头字段的字符串,这些字符串定义在osip_const.h文件中;函数指针setheader为该协议header的对应的解析函数;ignored_when_invalid为是否忽略该header解析错误的标志,该标志值为1时,在解析该协议header发送错误时,忽略该错误,除sip协议规定的几个必要header之外,其他头应该采用忽略方式。为了更快的根据header的hname,找到对应的setheader解析函数,采用了hash表的查询方式,根据hname生成一个hash值,并且需要保证没有两个不同的hname对应到同一个hash值中,以提高查询的速度。调用_osip_message_is_known_header (hname)获取到在数组中的index, 调用_osip_message_call_method (my_index, sip, hvalue)解析协议header,并且解析的结果保存在结构osip_message_t * dest,中。每一个header都包含几个通用的操作:hea
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1