osip源代码框架详解

Q/CT XXXX.1-2008

Osip协议源代码框架详解

Prepared by Reviewed by Approved by Mao minghua Date Date Date 2009.09.25

中国IMS网络SIP协议规范总体技术要求 - 1 -

Q/CT XXXX.1-2008

Revision History

Version 0.1 Author Mao minghua Reviewed By Comments 描述osip协议栈的源代码框架 Issued Date

中国IMS网络SIP协议规范总体技术要求 - 2 -

Q/CT XXXX.1-2008

目录

1 2 3 3.1 3.1.1 3.1.2 3.2 3.2.1 3.2.2 3.2.3 3.2.4 3.3 3.3.1 3.3.2 3.3.3 3.3.4 3.4 3.5 4 4.1 4.2 4.2.1 4.2.2 4.2.2.1 4.2.2.2 4.2.3 4.3 4.3.1 4.3.2 4.3.3 4.3.4 4.4 4.4.1 4.4.2 4.4.3

符号及缩写 ................................................................................................................... 4 整体描述 ....................................................................................................................... 4 OSIP包的源代码框架解析 ......................................................................................... 5 OSIP的TRANSACTION的EVENT的产生 ...................................................................... 5 定时器事件的产生过程 ............................................................................................... 6 报文触发的事件 ........................................................................................................... 7 OSIP 的TRANSACTION的EVENT处理流程 ................................................................. 7 ICT的处理流程 ........................................................................................................... 8 IST的处理流程 ............................................................................................................ 9 NICT的处理流程 ........................................................................................................ 9 NIST的处理流程 ......................................................................................................... 9 OSIP报文的解析 ........................................................................................................ 10 sip协议报文的解析整理流程 ................................................................................... 10 Osip报文头的解析 .................................................................................................... 12 uri的解析 ................................................................................................................... 14 添加一个新的协议header字段 ................................................................................ 15 OSIP的TRANSACTION的管理 .................................................................................... 16 OSIP中DIALOG的管理 ............................................................................................... 18 EXOSIP包的源代码框架解析 .................................................................................. 19 LIB库的初始化和销毁 .............................................................................................. 20 LIB库的主处理线程 .................................................................................................. 23 2xx应答的重发处理机制 .......................................................................................... 24 Exosip_execute执行流程 .......................................................................................... 24 Exosip_read_message的处理 .................................................................................... 26 eXosip_process_response_out_of_transaction的处理流程: ................................... 29 eXosip_automatic_action处理流程 ........................................................................... 29 CALL的处理 ............................................................................................................... 30 创建Call的第一个INVITE ...................................................................................... 30 INVITE的ACK应答的创建和发送 ........................................................................ 32 dialog内的请求的创建和发送 .................................................................................. 33 Dialog内answer的创建和发送 ................................................................................ 34 REGISTER的处理 ........................................................................................................ 35 向一个服务器第一次注册 ......................................................................................... 35 调整一个注册的注册超时时间 ................................................................................. 35 发送一个register注册 ............................................................................................... 36

中国IMS网络SIP协议规范总体技术要求 - 3 -

Q/CT XXXX.1-2008

Osip源代码框架详解

1

符号及缩写 缩写 ICT IST NICT NIST IMS PSVT SIP UDP URL 英文全称 Invite Client Transaction Invite Server Transaction Not Invite Client Transaction Not Invite Server Transaction IP Multimedia Subsystem Packet service video telephony Session Initiation Protocol User Datagram Protocol Uniform Resource Locator 中文名称 Invite类型的客户端事务 Invite类型的服务端事务 非Invite类型的客户端事务 非Invite类型的服务端事务 IP多媒体子系统 分组域可视电话 会话初始协议 用户数据报协议 统一资源定位器 2 整体描述

开源代码的osip协议栈分为两个源代码包,整个协议栈采用lib库的形式,在内部没有

使用到任务,采取与TCP/IP协议栈一样的策略,所以在使用上需要上层管理任务直接调用lib库提供的接口。因为在Lib库内部没有使用到像定时器、发送队列等的任务,而同时需要使用到定时器,所以在lib库的内部采用轮训遍历的方式不停的检查是否有定时器超时,这在某种程度上会浪费CPU的允许时间。同时整个lib库实现了对call, notify等的管理,为了实现重入,在应用启用多线程的条件下,内部启用的信号量和锁的使用,在下面的分析中不涉及到信号量和锁机制。

Lib库按照sip协议栈的层次关系分为两个lib包,底层的osip lib包实现对单个请求、应答、ACK的处理,包括message的解析、拼装、内容set和get,单个请求形成的transaction相关操作以及通信两端形成的一个dialog的操作。

Lib库上层的exosip lib在底层osip lib库的实现基础上,实现对sip协议整理逻辑上的管理。Exosip主要关注的是sip协议的业务流程,包括call的整体管理,notify的整体管理, publish的管理,register的管理,option的管理,refer的管理和subscription的管理,其中最主要的为call和register的管理,这两个为sip协议栈必须实现的部分,另几个功能为sip协议栈扩展部分。从这几个业务的管理流程出发,在业务的底层它们会使用到相似的一些功能,如注册的认证,发送message,接收message,每个请求和应答形成的transaction,多个

中国IMS网络SIP协议规范总体技术要求 - 4 -

Q/CT XXXX.1-2008

transaction组合而成的dialog。

在message的处理方面,可以分为两类,一类为发送的message,因为是主动发送,所以上层管理层知道是什么类型的message,lib库直接提供各类接口供使用。一类为接收到的message,因为不知道是哪种类型,所以需要根据解析出来的message的信息来进行处理,这部分的处理在udp.c文件中。

整个lib库的初始化在exOsip中介绍。 3

Osip包的源代码框架解析

在osip源代码包中最主要的包括了message的相关操作,其中最重要的为message的解析,即从获取到的一个message中解析生成一个能够被代码直接处理的message数据结构-——osip_message_t。与message结构相关的操作包括根据message数据结构的信息安装sip协议规范组装成一个message字符串;message结构的初始化和释放;message结构的拷贝操作;以及从message结构中获取各种已经解析的成员变量的值和设置各个成员变量的值。在message的解析部分,除了message的头之外,还包括了body的解析,涉及到sdp协议,包括对每个sdp字段的解析。

在osip源代码包中,设计了一个与同一个请求相关的所有message的集合——transaction,在发送或接收到一个新的请求的时候就会生成一个transaction,其中ACK和CANCEL请求是比较特殊的,对于非2xx应答的ACK和初始INVITE请求是属于同一个transaction的,而对于2xx的请求是属于单独的transaction的,所以其重传操作由UAC来控制,而不在INVITE的transaction内部进行控制。CANCEL的请求除了本身建立一个transaction外,根据协议它还会去匹配要CANCEL掉的请求的transaction,如果匹配成功会CANCEL掉相应的transaction。

在osip包中同样设计了dialog相关操作,包括dialog的建立,dialog信息的保存,dialog的匹配及删除等操作。

其它方面,包括多线程中使用到的锁和信号量及信号,内部使用到的链表,用于事件的队列(需要先进先出策略),一些平台无关的封装,定时器以及常量等的定义。这部分比较简单,而且都是最底层函数,直接封装了系统调用层。 3.1 osip的transaction的event的产生

transaction的状态变化是由事件来驱动的,当transaction上有事件产生时,根据事件的类型和当前transaction的状态来处理该event。

Transaction上的事件分为两类:一为定时器事件,在设定的定时器超时时会产生相应的定时器事件;另一类为事件驱动事件,如发送一个请求、应答或接收到一个请求、应答,发送一个ACK和接收到一个ACK,即是由报文产生的事件。

中国IMS网络SIP协议规范总体技术要求 - 5 -

Q/CT XXXX.1-2008

3.1.1 定时器事件的产生过程

osip_timers_ict_execute (osip_t * osip)__osip_ict_need_timer_b_eventHave timer b?noyes__osip_ict_need_timer_a_eventHave timer a?yes添加event到transaction的transactionff队列no__osip_ict_need_timer_b_eventyesHave timer b?no退出ICT、IST、NICT和NIST的定时器的事件产生流程都一样,对于每一个transaction,其定时器是有顺序的,ICT流程中TIMEOUT_B的优先级最高,TIMEOUT_B定时器触发后,会触发kill transaction的操作。当transactionff队列中有未处理的事件时,不处理定时器,直接返回,所以在transactionff队列中总的事件的数量是不多的。

所有的定时器函数调用底层同一个定时器检查函数__osip_transaction_need_timer_x_event。该函数会先检查该定时器是否启动,判断条件为(timer->tv_sec == -1),如果启动,检查当前时间是否超过定时器中设定的时间,如果是,则产生新的定时器事件。

因为定时器没有一个单独的任务,所以是采样轮训的方式检查是否有新的定时器事件产生,而不是根据系统时钟中断进行检测,因此会比较占用系统资源。

定时器的启动和修改使用接口osip_gettimeofday和add_gettimeofday。只需要设定定时器的超时时间,即设定了一个新的定时器。取消一个定时器,只需要修改定时器的timer->tv_sev为-1。

中国IMS网络SIP协议规范总体技术要求 - 6 -

Q/CT XXXX.1-2008

3.1.2 报文触发的事件

_eXosip_transaction_initosip_new_outgoing_sipmessage (invite)osip_transaction_add_event(transaction, sipevent) 包括一个新的invite、response、ack的发送或接收,除了对非2xx的应答ack外,其他的请求和应答都会产生一个新的transaction,并且产生一个新的sipevent事件。

3.2 osip 的transaction的event处理流程

在sip协议栈中为了更快更好的处理transaction,根据协议栈的描述,划分为四种不同的transaction,分别为ICT、IST、NICT和NIST。四种不同的transaction会有不同的处理流程和状态转换表,以及使用到不同的定时器。

ICT、IST、NICT和NIST的状态转换采样注册函数处理方式,为便于管理和使用注册函数,源码中使用了四个全局变量管理四种不同类型transaction的转换表:ict_fsm、ist_fsm、nist_fsm和nist_fsm。

osip结构如下:

struct osip {

void *application_context;

/* list of transactions for ict, ist, nict, nist */ osip_list_t osip_ict_transactions; osip_list_t osip_ist_transactions; osip_list_t osip_nist_transactions; ?? }

/**< list of ict transactions */ /**< list of ist transactions */ /**< list of nist transactions */ /**< User defined Pointer */

osip_list_t osip_nict_transactions; /**< list of nict transactions */

整体简单处理流程如下图:

中国IMS网络SIP协议规范总体技术要求 - 7 -

Q/CT XXXX.1-2008

osip_ict_execute (osip_t * osip)osip_ist_execute (osip_t * osip)osip_nict_execute (osip_t * osip)osip_nict_execute (osip_t * osip)osip_transaction_executeKill event?Yosip_free (evt)Nofsm_callmethod

图 3-1:transaction的event处理流程

3.2.1

ICT的处理流程

如上图,ICT事件处理时:

1)

检查osip管理结构中的osip_ict_transactions链表,如果没有链表元素,直接返回OSIP_SUCCESS

2) 3)

获取链表中元素个数,并保存transaction到临时局部数组

遍历所有transaction,osip_fifo_tryget顺序获取每个transaction中的事件,调用osip_transaction_execute处理每个事件,直到所有transaction中的所有事件被处理完毕,然后返回。

Osip_transacton_execute执行时,根据传入的参数osip_transaction_t * transaction中的transactionde 类型获取到状态转移表的全局管理变量ict_fsm,并且根据event的type和transaction的状态调用fsm_callmethod——在文件fsm_misc.c,是状态转移注册函数的总入口——查询找到event的处理函数,并调用处理函数进行event的处理。

ICT的相关event的注册处理函数在ict_fsm.c文件和ict.c文件。ICT使用到了3个定时器:

中国IMS网络SIP协议规范总体技术要求 - 8 -

Q/CT XXXX.1-2008

TIMEOUT_A、TIMEOUT_B和TIMEOUT_D。

在client端发送Invite而需要创建新的ICT的transaction时,TIMEOUT_B被启动,时长为64*DEFAULT_T1(DEFAULT_TI为500ms),TIMEOUT_B为整个transaction的生命周期时长,如果超过这个时间,transaction会被结束。如同传输层使用的是没有传输保证的UDP,则设置TIMEOUT_A,TIMEOUT_D的间隔时间为DEFAULT_T1和64* DEFAULT_T1。如果传输层使用的是面向连接的TCP及相关协议,则直接使用TCP内部的重传机制,不在SIP协议层提供传输的保护机制,所以不启动TIMEOUT_A和TIMEOUT_D。TIMEOUT_A管理Invite的传送,在Invite被发送时,启动定时器TIMEOUT_A,并且在超时时间内还没接收到response的时候,重发该Invite。TIMEOUT_D用于管理ACK,当接收到的response不是>=300时,client端发送ACK,当重复接收到该invite的response时,重发该ACK,确保server端在kill tranction前能接收到ACK。 3.2.2

IST的处理流程

同ICT的处理流程,处理osip中的osip_ist_transaction链表。IST的相关event的注册处理函数在ist_fsm.c文件和ist.c文件。

IST使用了定时器TIMEOUT_G、TIMEOUT_H和TIMEOUT_I。使用方式与ICTL类似,详细见协议栈说明。 3.2.3

NICT的处理流程

同ICT的处理流程,处理osip中的osip_nict_transaction链表。NICT的相关event的注册处理函数在nict_fsm.c文件和nict.c文件。

NICT使用了定时器TIMEOUT_E、TIMEOUT_F和TIMEOUT_K。 3.2.4

NIST的处理流程

同ICT的处理流程,处理osip中的osip_nist_transaction链表。NIST的相关event的注册处

中国IMS网络SIP协议规范总体技术要求 - 9 -

Q/CT XXXX.1-2008

理函数在nist_fsm.c文件和nist.c文件。

NIST使用了定时器TIMEOUT_J。 3.3 Osip报文的解析 3.3.1

sip协议报文的解析整理流程

当接收到一个message的时候,需要解析该message,生成一个代码能够处理的数据结构,该结构定义为struct osip_message,该结构定义的一个message的全部相关信息,这些信息主要是供transaction和dialog及dialog的更上一层如call,notify等的使用。

对一个message的解析流程如下图所示:

osip_message_parse_osip_message_parseosip_util_replace_all_lws__osip_message_startline_parsemsg_headers_parsemsg_osip_body_parse 在接收到一个message时,调用函数osip_message_parse进行message的解析。首先调用函数osip_util_replace_all_lws替换掉message中的连续出现的 ‘\\r\\n\\t’、‘\\r\\t’、‘\\n\\t’、‘\\r\\n ’、‘\\r ’、‘\\n ’为空格,message是以‘\\0’为结束标志的,message的headers和body之间的分界是以’\\r\\n\\r\\n’为标志的,替换只替换到’\\r\\n\\r\\n’为止,即只替换headers部分出现的\\t、\\r、\\n。由于sip协议栈规定,每个headers都是起新行,而且新行的头一个字符不为空格或\\t,所以两个header之间的\\r\\n不会被替换掉,替换的只是一个允许multi合并项的header的内部多个值之间的“\\r\\n\\t”或“\\r\\n ”。

中国IMS网络SIP协议规范总体技术要求 - 10 -

Q/CT XXXX.1-2008

举例如下:有两个header,其中Subject只允许单个值出现, Route允许有多个值出现,而且允许分行,但是分行必须以空格或\\t开头,而Subject和Route行必需顶格开始,前面是没有空格或\\t的,osip_util_replace_all_lws函数将Route header value中的两行间的\\r\\n\\t转化为空格,即在逻辑上就成为一行了。

Subject: Lunch

Route: ,

一个message由三部分组成,首先是message的startline部分,该行指明这是一个sip的message,包括sip标志,请求或应答说明,状态值,然后以\\r\\n做为和headers的分隔符。该\\r\\n不会被osip_util_replace_all_lws替换为空格,如请求的INVITE sip:bob@biloxi.com SIP/2.0或应答的SIP/2.0 200 OK,在三个属性之间有且仅有一个空格。起始行的解析由__osip_message_startline_parse进行解析,解析得到message的类型,message的sipversion以及message的status_code,当status_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中在headers之后不是结束符’\\0’,则继续解析message的负载部分,调用函数msg_osip_body_parse进行解析。Message的body解析首先查询headers头解析中保存

中国IMS网络SIP协议规范总体技术要求 - 11 -

Q/CT XXXX.1-2008

的content——即body――的属性:content_type,如果content_type中的type不为multipart,即不支持多种mime方式的content,说明body中就一个编码方式,直接将整个body解析为一个内容;如果type为multitype,说明有多个编码方式的body组合在一起形成一个整体的body,则以”--”为分隔符解析body,将body分为多个mime编码方式的字符串,每个解析后的body内容保存在osip_message结构中的bodies结构成员中。 3.3.2

Osip报文头的解析

msg_headers_parseYes,解析下一个headerheader解析成功,body为NULL返回成功,body=start_of_headerStart_of_header[0]为0NoYes__osip_find_next_crlf获取下一个\\r\\n,保存位置到end_of_headerSuccessYes,解析下一个headerStart_of_header为\\r或\\nnoosip_message_set_multiple_headerYesfail返回errorheader解析success, start_of_header = end_of_headerno

在解析message的header的时候,因为前面的osip_util_replace_all_lws已经转化了单个header内部出现的\\r、\\n和\\t为空格,所以每个header之间可以使用\\r\\n做为分隔符进行分隔。如果字符串开头start_of_header已经到达结束符”\\0”,则全部header解析完毕,返回成功;调用__osip_find_next_crlf找到这个header的结束字符并保存在end_of_header中;如果start_of_header为\\r或\\n,则已经解析到\\r\\n\\r\\n即headers的结束字符串,则返回成功,并且保存start_of_header到body中,即body是从\\r\\n字符串开始解析的,所以在body解析时,需要跳过\\r\\n及之后的空格部分;根据header内部分隔符“:”,取出header的hname和hvalue,其中

hvalue

在某些

hname

的情况下是允许为空的,然后调用

中国IMS网络SIP协议规范总体技术要求 - 12 -

Q/CT XXXX.1-2008

osip_message_set_multiple_header来解析该header的hvalue字符串;解析成功后,置start_of_header为已经解析完的header的end_of_header,开始解析下一个header。

在osip_message_set_multiple_header中,将headers分为两类,一类如上面例子中的Subject,只允许一个值,则直接调用osip_message_set__header进行解析;一类如上面例子中的Router,允许多个值,根据sip协议,每个值之间以“,”进行分隔,所以需要查询整个hvalue字符串,根据”,”将hvalue分隔成多个值,每个值调用osip_message_set__header进行解析并保存解析结果到osip_message的数据成员变量中。因为hvalue允许使用引号将值引起来,所以需要特别处理“,”是否出现在引号内部的问题。只有在引号外部的“,”才是header值的分隔符,而内部的“,”只是一个header值的一部分。

osip源码中osip_message_set__header对于message headers的解析采用注册函数的方式实现,采用这种方式能够在后继版本很方便的进行新的header的添加,并且不会影响到整个源代码的框架流程。

Osip_parser_cfg.c文件中定义了header头解析所使用到的全局管理变量:static __osip_message_config_t pconfig[NUMBER_OF_HEADERS];

__osip_message_config_t的结构定义如下: typedef struct ___osip_message_config_t {

char *hname;

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,并且解

中国IMS网络SIP协议规范总体技术要求 - 13 -

Q/CT XXXX.1-2008

析的结果保存在结构osip_message_t * dest,中。

每一个header都包含几个通用的操作:header字符串的解析函数,即上段讲到的osip_message_set_xxx解析函数;header解析后的结构的获取函数,osip_message_get_xxx函数;根据header解析后的结构生成字符串的函数:osip_xxx_str;header解析后的结构的copy函数osip_xxx_clone;header解析后的结构的是否函数:osip_xxx_free;以及header解析结构的初始化函数:osip_xxx_init。

对每个header的几个相关操作最终目的是提供协议的整个header的整体操作,包括osip_message_init,osip_message_free,osip_message_clone和osip_message_parse。 3.3.3

uri的解析

绝大部分的header的解析都是相识的,只有其中有参数的部分的header的解析会比较复杂,最主要的有from、to、contact等,因为除了本身就有参数之外,其值中的request_uri本身也可以包含有参数,而这两种参数之间是有区别的。

Sip协议栈规定header的表示分为 header’s name, header’s value和header’s parameter。其中name和value之间用“:”分隔,value与parameter之间用“;”分隔,parameter之间也使用“;”相分隔。

在结构定义中header的value根据具体header包含的信息进行结构变量的定义,而如果包含parameter则直接定义一个gen_params的链表,所有的parameter都保存在这个链表中。

如下面from的定义,包含有from的名称及一个url,及相关的parameter:

struct osip_from {

char *displayname; /**< Display Name */ osip_uri_t *url; /**< url */

osip_list_t gen_params; /**< other From parameters */

中国IMS网络SIP协议规范总体技术要求 - 14 -

Q/CT XXXX.1-2008

};

对应parameter的解析直接调用__osip_generic_param_parseall,该函数解析header的单个hvalue字符串中包含的所有parameter,在函数内部会根据“;”将字符串划分为几个parameter,然后解析每个parameter,将解析结果保存在gen_params链表中。Parameter的格式为pname=pvalue类型,等号两边允许空格。

From、to、contact以及via中间都可能出现url。url的解析接口为osip_uri_parse,输入为url的字符串,解析的结构保存在结构osip_uri_t之中。url包含有三部分内容:url的基本信息,url的header头部分和url的参数部分。开始部分与header头部分用“?”进行分隔,header头之间用”&”进行分隔,header头部分与参数部分用”;”进行分隔,参数之间也使用“;”进行分隔。Header部分调用函数osip_uri_parse_headers进行解析,结果保存在osip_uri_t结构中的url_headers成员变量中;parameter部分调用函数osip_uri_parse_params进行解析,其结果保存在osip_uri_t的url_params成员变量中。

在from、to、contact等包含url的header中,如果url中包含parameter,则整个url必需使用“<” “>”括起来,以表示一个完整url部分。所以解析from等header时需要检查是否包含”<”字符。 3.3.4 1)

添加一个新的协议header字段

需要添加多个一个对该字段进行解析的文件,包含一个header常用到的几个基本通用操作,如果该header有特殊的地方需要处理,需要增加相关的处理函数,文件名一般定义为osip_xxx.c和osip_xxx.h

2) 需要在parser_init中注册新的header的解析函数,需要修改static __osip_message_config_t

pconfig[NUMBER_OF_HEADERS]

NUMBER_OF_HEADERS宏值。

3)

在osip_const.h中添加新的header的宏定义,osip的相关的常量宏定义都定义在该文件

4) 在osip_message.c文件额osip_message_init函数中添加对该header相关结构的初始化操作。在osip_message_free函数中同样添加对该header的相关释放操作,在osip_message_clone中添加对该header的clone相关操作。

中国IMS网络SIP协议规范总体技术要求 - 15 -

Q/CT XXXX.1-2008

5) 在osip_message_to_str.c文件中的_osip_message_to_str函数中添加该header转化为string的函数注册。

6) 如果该header不允许重复多个出现,即不允许multiple header,则在osip_message_parse.c文件的 osip_message_set_multiple_header函数中添加对该header的处理。

7) 8)

在osip_message.h的头文件中的osip_message结构中添加对该header字段的结构。 在osip_headers.h文件中添加新的header的头文件引用。

3.4 osip的transaction的管理

transaction的操作主要包括transaction的初始化、transaction的free、transaction的匹配、从transaction中获取信息和设置transaction信息。

根据sip协议描述一个transaction由5个必要部分组成:from、to、topvia、call-id和cseq,这5个部分一起识别某一个transaction,如果缺少任何一部分,该transaction就会设置失败。

所以对每个部分的设置都会有一个设置函数:__osip_transaction_set_topvia用于设置topvia,对于发送端topvia为自己的via,对于接收端topvia为将message转发到自己的最后一个sip-proxy服务器,__osip_transaction_set_from用于设置message的发送端,__osip_transaction_set_to用于设置message的接收端,__osip_transaction_set_call_id用于设置一个dialog的标识值,该值是随机生成的,算法保证很长一段时间内生成的cal_id是不相同的,__osip_transaction_set_cseq用于设置cseq值,该值在同一个dialog内部是一直保持增长的,即同一个dialog的后面的transaction的cseq会比前面的transaction的值大,按照sip协议其初始值可以是随机数,代码实现中如果是非register请求,从1开始,如果是register请求的dialog,从20开始。

Transaction的初始化发生在接收到一个新的请求或发送一个请求的时候,该请求以及经过解析成为一个可以直接使用请求信息的结构osip_message_t。其初始化具体过程如上面所述,在设置完那5个部分后,还需要初始化event的队列,以及根据osip_message_t的type初始化使用到的定时器结构,如ICT的ict_context。其它部分的初始化在exosip源代码中实现,相关的如your_instance、in_socket、out_socket和record都是未了方便exosip中对transaction的管理而设置的。

Transaction中的event的相关操作在如前面所述。

中国IMS网络SIP协议规范总体技术要求 - 16 -

Q/CT XXXX.1-2008

在transaction的匹配中,根据RFC3261的最新sip协议的描述,由topvia的branch_id是否相同来匹配,如果相同,就是同一个transaction的请求和应答及ACK,为兼容旧版本的transaction的匹配规则,同时支持根据call_id, cseq, from_tag, to_tag来匹配transaction。如下图,为便于管理的transaction,所有的transaction保持在osip_t结构的四条链表中,按照处理流程的不同分为发送出去的INVITE类型请求和应答、其它类型请求和应答,接收到的INVITE请求和应答、其它请求和应答。

struct osip {

void *application_context; /**< User defined Pointer */

/* 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; /**< list of nist transactions */ ……

#if defined(HAVE_DICT_DICT_H)

dict *osip_ict_hastable; /**< htable of ict transactions */ dict *osip_ist_hastable; /**< htable of ist transactions */ dict *osip_nict_hastable; /**< htable of nict transactions */ dict *osip_nist_hastable; /**< htable of nist transactions */ #endif };

osip_find_transaction_and_add_eventosip_find_transaction__osip_find_transaction根据message的cseq中的method和status确定在osip的哪条transaction链表中进行匹配osip_transaction_find是否添加event到transaction队列no退出Yes添加event到transaction队列 中国IMS网络SIP协议规范总体技术要求 - 17 -

Q/CT XXXX.1-2008

在transaction的匹配过程中,如果是发出的请求,因为本地的transaction都会分配一个不重复的transaction_id,所以只需要比配transaction_id即可;对于incoming的message,如果是request,则匹配branch_id或者为兼容前面版本进行transaction的比配,按照协议RFC3261的17-2-3节的方式进行匹配;如果incoming的message是response,则匹配branch_id或根据RFC3261的17-1-3节的规则进行匹配。

osip_transaction_findEVT_IS_INCOMINGREQYesnoHAVE_DICT_DICT_HYesEVT_IS_INCOMINGRSPYesno__osip_transaction_matching_request_osip_to_xist_17_2_3遍历transaction匹配transactionidnoTop_via中有branch_idHAVE_DICT_DICT_HnoyesInvite or ACKno__osip_transaction_matching_response_osip_to_xict_17_1_3YesnoTop_via中有branch_idyesInvitenoyesdict_search(osip->osip_ist_hastable,...)dict_search(osip->osip_nist_hastable,...)yesnodict_search(osip->osip_nist_hastable,...)dict_search(osip->osip_ist_hastable,...)

3.5

osip中dialog的管理

dialog的相关的管理操作包括dialog的初始化建立过程,dialog的销毁free过程,以及dialog的匹配。此外dialog中保存了相关的路由信息和cseq信息。

Dialog结构如下,由call_id、local_tag和remote_tag唯一确定一个dialog:

struct osip_dialog {

char *call_id; /**< Call-ID*/ char *local_tag; /**< local tag */ char *remote_tag; /**< remote tag */ osip_list_t route_set; /**< route set */

中国IMS网络SIP协议规范总体技术要求 - 18 -

Q/CT XXXX.1-2008

int local_cseq; /**< last local cseq */ int remote_cseq; /**< last remote cseq*/ osip_to_t *remote_uri; /**< remote_uri */ osip_from_t *local_uri; /**< local_uri */ osip_contact_t *remote_contact_uri; /**< remote contact_uri */

int secure; /**< use secure transport layer */

osip_dialog_type_t type; /**< type of dialog (CALLEE or CALLER) */

state_t state; /**< DIALOG_EARLY || DIALOG_CONFIRMED || DIALOG_CLOSED */ void *your_instance; /**< for application data reference */ };

在dialog的初始化时,需要根据是client端或server端来确定dialog结构中的call_id、local_tag和remote_tag的值。根据是client端或server端来确定dialog的type,并且设置dialog的状态。当做为client端,并且是在接收到发出的request的response时,调用osip_dialog_init_as_uac进行初始化dialog;如果是接收到server端发送过来的request,则调用osip_dialog_init_as_uac_with_remote_request进行dialog的初始化。如果是server端,调用osip_dialog_init_as_uas进行初始化dialog。

在dialog的匹配时,当是client端时,调用osip_dialog_match_as_uac进行匹配。检查接收到的response和dialog中的call_id,to_tag和from_tag是否匹配,如果全部匹配,则匹配到了该dialog。为兼容前面的版本,在dialog的to或者接收到的message的to header没有tag的情况下,比较dialog和message的from_uri, to_uri。如果匹配,则同样匹配的dialog。

当是server端时,调用osip_dialog_match_as_uas进行匹配。其匹配方法与client端的匹配方法相同。

对from_tag和to_tag的匹配处理,在transaction的匹配过程中同样使用到。 4

Exosip包的源代码框架解析

在exosip源代码包中包含了提供给上层管理软件调用的关于call、message、option、refer、register、subscription、publish和insubscription的API,这些API的实现都在ex开头的c文件中。

中国IMS网络SIP协议规范总体技术要求 - 19 -

Q/CT XXXX.1-2008

为这些接口进行服务的函数,包括和osip lib库进行通信的部分的实现在以j开头的源文件中如jcall.c,其中jrequest.c和jresponse.c实现了request和response的message的通用构造实现。

同时exosip实现了传输层的四种不同的传输方式供上层的sip协议栈进行选择,分别为extl_dtls.c、extl_tcp.c、extl_tls.c和extl_udp.c,它们以注册的方式在Lib库启动的时候注册到lib库的钩子中。

为了支持多线程间的通信,在两个线程间采用pipe的方式进行实现,如果没有使用多线程,这部分源代码在编译时会被屏蔽掉。

对接收到的message进行的逻辑处理在文件udp.c中,这部分是整个协议栈逻辑比较复杂的地方。需要根据sip协议栈的标识描述和代码实现框架进行整理的把握。 4.1

Lib库的初始化和销毁

整个sip 的lib库有一个总的管理结构struct exosip_t eXosip,该全局变量在lib库被使用之前需要初始化,初始化函数为exconf.c文件的eXosip_init函数。Exosip_t结构如下:

struct eXosip_tt {

struct eXtl_protocol *eXtl; char transport[10]; char *user_agent;

eXosip_call_t *j_calls; /* my calls */ #ifndef MINISIZE

eXosip_subscribe_t *j_subscribes; /* my friends */ eXosip_notify_t *j_notifies; /* my susbscribers */ #endif

osip_list_t j_transactions;

eXosip_reg_t *j_reg; /* my registrations */ #ifndef MINISIZE

eXosip_pub_t *j_pub; /* my publications */ #endif

#ifdef OSIP_MT void *j_cond; void *j_mutexlock; #endif

osip_t *j_osip; int j_stop_ua; #ifdef OSIP_MT

中国IMS网络SIP协议规范总体技术要求 - 20 -

Q/CT XXXX.1-2008

void *j_thread; jpipe_t *j_socketctl; jpipe_t *j_socketctl_event; #endif

osip_fifo_t *j_events;

jauthinfo_t *authinfos;

int keep_alive; int learn_port; #ifndef MINISIZE int http_port; char http_proxy[256];

char http_outbound_proxy[256]; int dontsend_101; #endif

int use_rport;

char ipv4_for_gateway[256]; char ipv6_for_gateway[256]; #ifndef MINISIZE

char event_package[256]; #endif

struct eXosip_dns_cache dns_entries[MAX_EXOSIP_DNS_ENTRY];

struct eXosip_account_info account_entries[MAX_EXOSIP_ACCOUNT_INFO]; struct eXosip_http_auth http_auths[MAX_EXOSIP_HTTP_AUTH];

CbSipCallback cbsipCallback;

p_access_network_info *p_a_n_i; digest_cave_response *cav_v; };

在该结构中,最重要的几个成员变量如下:

1) int j_stop_ua; 协议栈启动和停止的控制参数,当j_stop_ua为0时,lib库启动,为非

0时,lib库停止。

2) eXosip_call_t *j_calls; j_calls用于管理全部的通话,所有的call在这个结构中形成一

个链表结构。Call结构如下:

struct eXosip_call_t {

int c_id;

eXosip_dialog_t *c_dialogs;

中国IMS网络SIP协议规范总体技术要求 - 21 -

Q/CT XXXX.1-2008

osip_transaction_t *c_inc_tr; osip_transaction_t *c_out_tr;

int c_retry; /* avoid too many unsuccessfull retry */ void *external_reference;

eXosip_call_t *next; eXosip_call_t *parent; };

其中的c_id为分配的call_id,c_dialogs为同一个call中的dialog的集合。c_inc_tr和c_out_tr为创建该call时的初始化的transaction,一个终端对于一个call不是client端就是server端,所以c_inc_tr和c_out_tr只可能有一个是有transaction的,其中有一个为NULL。

3) eXosip_reg_t *j_reg; j_reg管理sip的注册服务,所有register相关的transaction在j_reg

中形成一个链表。根据sip协议,新的一次的注册必需等到前一次主次完成后才能进行,所以同一个register不会像call一样,同时有多个transaction存在。

4) struct eXtl_protocol *eXtl; 为使用的传输层的协议,在exosip的初始化中需要设置,

在传输层现在了4种不同的传输实现方式,它们的实现以一个结构的形式存在,在exosip_t初始化时注册到exosip全局变量的extl成员变量中。

5) osip_list_t j_transactions;用于管理全部的删除但是还没有系统回收的transaction。这

些transaction不属于call或register或者是call、register中删除的transaction。

6) osip_t *j_osip; 为osip lib库的管理结构,osip Lib的结构在一个任务中也只会有一个

变量存在,用于管理全部的sip协议栈中出现的transaction。根据上面的描述,同一个transaction同时被j_osip管理和call/reg管理,所以当一个transaction被删除时,需要从j_osip中先将该transaction从管理结构中删除,同时从call或者reg中删除,然后加入到j_transaction链表中。在j_osip初始化时,初始化了4条不同类型的transaction的管理链表,同时初始化了message header的解析函数的全局注册变量,在函数increase_ref_count中初始化。同时osip lib库为了实现重入,对被初始化的次数进行了计数。

7) 下面几个字段与协议的认证有关,在register时获取到认证信息,在call时需要将认

证信息添加到http_anth或proxy_anth中以便在进行call时能通过认证。

p_access_network_info *p_a_n_i; digest_cave_response *cav_v; jauthinfo_t *authinfos;

struct eXosip_dns_cache dns_entries[MAX_EXOSIP_DNS_ENTRY];

struct eXosip_account_info account_entries[MAX_EXOSIP_ACCOUNT_INFO];

struct eXosip_http_auth http_auths[MAX_EXOSIP_HTTP_AUTH];

中国IMS网络SIP协议规范总体技术要求 - 22 -

Q/CT XXXX.1-2008

8) 在OSIP_MT内部的是为了支持多线程而需要的信号量、锁和线程间的通信方式的

pipe。其中pipe经过了封装,在类unix系统中直接采用pipe,在windows中采用socket通信来模拟pipe。

9) 在MINISIZE内部的为扩展版本,当需要publish,notify等功能时,可以启用。

初始化的流程如下,即初始化全局管理变量exosip的成员变量的值:

设置exosip中的几个常量 event_package, ipv4_for_gateway, ipv6_for_gateway, user_agent, 设置exosip为允许状态:exosip.j_stop_ua = 0初始化多线程使用到的信号量和信号及线程通信方式pipe初始化exosip中的osip,用于管理osip lib库初始化osip的callback注册函数初始化传输层的4种不同的传输方式

在初始化中,osip lib库中的发送message操作、接收message操作、transaction的kill操作、定时器超时操作等会影响到exosip中对transaction、dialog、call、register等的影响,这些操作由上次的exosip决定,所以在进行这些操作时会回调exosip注册在osip变量中的回调函数。在初始化exosip全局变量时会注册这些回调函数,在eXosip_set_callbacks函数中实现,这些注册函数的实现在jcallback.c文件中实现。

exosip lib库的销毁调用函数eXosip_quit,其操作与exosip_init的操作相反,其先置位

j_stop_ua为飞0,使处理线程停止处理sip协议上的rejister和call。然后释放所有申请的内存,释放exosip上保存的所有的call、register和里面使用到的dialog及transaction。 在exosip全局变量的初始化中,部分和业务相关的字段在exosip的外部进行初始化。如部分字段的初始化在函数eXosip_listen_addr,eXosip_masquerade_contact,eXosip_set_user_agent,eXosip_add_authentication_info中实现,参见sip_reg.c文件的winmain,该函数是register的启动测试函数,里面会初始化register使用到的几个值,Sip_reg.c是对lib库应用的一个简单实例。 4.2

Lib库的主处理线程

在exosip lib库初始化成功之后,如果启用多线程,则在新的线程中一直执行

exosip_execute,在主线程中执行eXosip_automatic_action;如果没有启用多线程,则在单线程中每次获取一个exosip lib库上报的事件,然后执行exosip_execute和

中国IMS网络SIP协议规范总体技术要求 - 23 -

Q/CT XXXX.1-2008

eXosip_automatic_action。在主线程中每次都会去获取exosip Lib包上报的exEvent,在处理exEvent时会处理call中200应答的重发机制。 4.2.1

2xx应答的重发处理机制

在eXosip_retransmit_lost200ok中对2xx的应答进行重发控制,根据sip协议标准,因为2xx的重传是需要sip协议栈进行控制的,当第一次发送2xx应答后,在规定时间内没有接收到ACK应答,则重传2xx应答。其重传的时间间隔第一次为1s,以后每次翻倍,增长到4秒时每次都间隔为4s。

2xx的重发的停止发生在两种情况:一为2xx应答发送超时,在发送9次之后仍然没有接收到对端发送的ACK应答,则停止重发,说明call的通话出现的了通信错误,则结束该call;二为在发送2xx应答之后,接收到了对端的ACK应答,该ACK应答匹配了这个dialog,则只需要停止2xx的发送,同时释放dialog中保存的2xx消息,并置dialog中的2xx重复参数为停止。

4.2.2 Exosip_execute执行流程

exosip_execute的执行流程比较简单,因为在线程没有被teminate的情况下,线程会一直循环执行exosip_execute,所以在exosip_execute内部只需要顺序执行相关的操作即可。其执行流程如下:

中国IMS网络SIP协议规范总体技术要求 - 24 -

Q/CT XXXX.1-2008

eXosip_read_messageosip_timers_ict_executeosip_timers_nict_executeosip_timers_ist_executeosip_timers_nist_executeosip_nist_executeosip_nict_executeosip_ist_executeosip_ict_executeeXosip_release_terminated_callseXosip_release_terminated_registrationseXosip_release_terminated_publications_eXosip_keep_alive

在每次执行eXosip_execute时,先会去读取message,所以运行线程一直在监听是否有消息发送到本客户端,对于要发送的message,都是管理程序主动调用接口进行发送的。在处理接收到的message时可能会创建新的call、新的transaction,生成新的transaction的event,还有exosip的event。其中exosip的event是上报给管理程序的,在管理程序中根据具体的实际情况进行处理。其能够参数的各种event的定义在文件exosip.h的枚举typedef enum eXosip_event_type中。

在读取完一个message并做了预处理之后(也可能没有新的message需要处理),

exosip_execute开始处理osip中4条transaction链表中的定时器,如果定时器超时,则产生新的定时器事件并放入transaction的事件队列中,然后开始执行4条transaction链表中的每个transaction的事件,其中包括对message部分的后半部处理。这两部分的代码分析在osip lib包分析部分已经有分析。

在处理完上面部分后,开始释放已经结束的call、registration和publication。其中在registration和publication的释放的时候,只是将其中的transation从registration和publication的结构中删除,然后放在exosip全局变量的j_transaction链表中。该链表中的transaction最终会在eXosip_release_terminated_calls中释放。

同时,如果传输层使用的是UDP协议,则需要调用_eXosip_keep_alive为registration发送报文保存UDP的通信,防止UDP超时该端口被系统关闭。

中国IMS网络SIP协议规范总体技术要求 - 25 -

Q/CT XXXX.1-2008

4.2.2.1 Exosip_read_message的处理

Exosip_read_message根据使用的传输层协议,调用传输层的tl_read_message函数从TCP/IP协议栈底层读取message。如果从传输层读取message成功,则交给

_eXosip_handle_incoming_message进行处理。在_eXosip_handle_incoming_message中,首先解析该message,如果解析成功,在解析完之后,检查message中的必要字段call_id number。如果管理程序在exosip中注册了message的消息处理函数,则回调注册该函数。然后根据message的类型,检查合法性并确定该message的产生的transaction的event的类型,因为是接收到message,所以类型全部为RCV_XXX类型,在发送message时,产生SND_XXX的event。因为是新接收到的message,有三种可能,一是能匹配已经存在的transaction,即是某个请求的应答或ACK;如果不能匹配,根据message中的状态码,如果是0,说明是一个请求,而且这个请求不能匹配已经存在的transaction,所以是一个新请求,对新请求的处理在函数eXosip_process_newrequest中;如果status不为0,则是一个response,因为response是对一个request的回应,而发送request的时候在本端肯定已经建立了新的transaction,如果逻辑处理正确,该response应该匹配到某一个存在的transaction,现在没有匹配到,说明该response是一个错误发送的response,对response的处理在函数

eXosip_process_response_out_of_transaction中。Exosip_read_message的处理流程如下:

中国IMS网络SIP协议规范总体技术要求 - 26 -

Q/CT XXXX.1-2008

eXosip_read_messageeXtl_udp.tl_read_messageeXtl_tcp.tl_read_messageeXtl_dtls.tl_read_messageeXtl_tls.tl_read_message_eXosip_handle_incoming_messageosip_message_initosip_message_parseeXosip.cbsipCallbackosip_message_fix_last_via_headerosip_find_transaction_and_add_eventFind transaction successNoMSG_IS_REQUESTYesNoeXosip_process_response_out_of_transactionYeseXosip_process_newrequest返回success

eXosip_process_newrequest的处理流程如下:

a) 根据message的类型,获取到ctx_type的类型,因为是接收端,所以本端为

server,如果message既不是INVITE,也不是ACK,同时不是其它REQUEST的情况,则直接释放这个message。

b) 如果是ACK,则肯定是对200的response的一个回应。不需要建立新的

transaction。

c) 如果是CANCEL,则直接转eXosip_process_cancel进行处理。

d) 查看该message是否属于某一个dialog,因为匹配dialog会比匹配transaction

的条件简答,多个transaction可以属于同一个dialog。如果匹配到某一个dialog,则检查该新的message的cseq_number和dialog中保存的cseq_number的大小,如果没有大于dialog中保存的remote cseq number,说明接收到的message是一个错误的request,则释放该message并返回。

中国IMS网络SIP协议规范总体技术要求 - 27 -

Q/CT XXXX.1-2008

e) 如果是INVITE并且没有定义最小化该协议栈操作的情况下,则先发送一个

100的临时应答。

f) 如果这个message匹配到某一个dialog

i.

并且不是ACK和BYE,则检查这个dialog是否已经结束,既该dialog已经发送或接收到过BYE请求,则根据sip协议标准发送一个481的错误提示应答。

如果不是ACK,因为已经通过协议的合法性检查,同时匹配到一个dialog,所以需要根据该message更新dialog的remote cseq。 如果message是INVITE

1. 检查这个dialog中最近的一个接收到的INVITE是否已经到结束状

态,如果没有,则根据sip协议标准将这个new INVITE删除,并发送一个500的提示错误应答。

2. 接着检查这个dialog中最近的一个发送出去的INVITE是否已经到结

束状态,如果没有,则根据sip协议标准将这个new INVITE删除,并发送一个491的提示错误应答。

3. 否则该INVITE是个合法的请求,则更新dialog的route set,因为该

INVITE并不是创建该dialog的第一个请求,所以调用eXosip_process_reinvite处理该INVITE请求。

如果message是BYE请求

1. 先检查该BYE请求的参数合法性,是否包含to tag,因为本端发送的

response里面肯定包含有to tag,所以对端发送的BYE应该是一个包含to tag的合法的message。

2. 检查dialog中是否已经接收到BYE,如果已经接收到BYE,说明对

方重发了BYE请求,直接回复500错误提示应答 3. 否则调用eXosip_process_bye处理BYE请求。 v. 如果是ACK,直接调用eXosip_process_ack处理该ACK请求 vi. 如果是其它请求,则调用eXosip_process_message_within_dialog处理该请

求。

g) 否则,没有匹到某一个dialog,说明是全新的一个请求 i. 如果是ACK,说明该200的ACK没有匹配到任何dialog,所以是一个错

误的ACK,直接释放message即可,因为是ACK,所以并没有建立transaction,不需要对transaction进行操作。 ii. 如果是INFO,直接回复481应答。 iv.

ii.

iii.

中国IMS网络SIP协议规范总体技术要求 - 28 -

Q/CT XXXX.1-2008

iii.

iv. v.

如果是INVITE,调用eXosip_process_new_invite处理这个新请求,如果该请求合法,则会生成一个新的call,并且在call上生成一个dialog,该dialog是服务端的dialog,因为本端是UAS。

如果是BYE请求,则和ACK一样,该BYE请求没有匹配到dialog,回复一个481的错误提示应答通知对端需要结束的dialog不存在。

如果是其它类型请求,则因为是不符合sip标准的请求方式,所以将创建的transaction添加到待删除的队列中即可。

4.2.2.2 eXosip_process_response_out_of_transaction的处理流程:

a) 因为message为应答,而且没有匹配到transaction,所以肯定是一个错误的应

答。先检查message本身的合法性,如果不合法,直接释放并返回

b) 查询所以的call的dialog,查看该response是否匹配到dialog或者是还没有建

立dialog的call

c) 如果没有匹配到某一个call,说明该response与call无关,直接释放。

d) 如果#ifndef MINISIZE,且匹配到某一个dialog,说明重复接收了200应答,

可能原因是对方还没有接收到本端发送的ACK,但是本端的transaction在接收到200应答时已经被kill了,所以没有匹配到transaction,但是匹配到了dialog。如果接收到200应答的cseq和本端发送的cseq的number相等,则重新发送ACK应答。处理完上述情况后,释放该message并返回。

e) 如果只是匹配到了call,说明dialog还没有建立,但是给200应答是一个错误

的应答,否则会匹配到call的c_out_tr transaction。则为该200应答临时建立一个dialog并发送ACK回应,然后发送一个BYE请求结束该call,因为该call已经发送错误了。

4.2.3 eXosip_automatic_action处理流程

该函数用于处理哪些认证失败的call、register、notify、publish等,在接收到认证服务器回应为401或407或需要转发的情况下,进行重新尝试。

a) 遍历所有的call: i. 如果c_id < 1则不用处理,call的id小于1的都是被删除的但是还没有被

清理出j_calls链表的call。 ii. 如果该call的dialog还没有建立起来,说明是本端发送第一个INVITE请

求建立的新call,如果是对端发送的第一个INVITE,则本端要回应一个response,在回应response的时候,dialog就被建立起来了。

1. 检查建立该call的第一个INVITE请求建立起来的transaction的状态,

如果已经终结,并且该call还没有到结束超时时间120秒,且接收到的回复的状态码为401或407,则重发送请求,并且从回复中提取认

中国IMS网络SIP协议规范总体技术要求 - 29 -

Q/CT XXXX.1-2008

证信息。重发次数最多为3次,如果3次都失败,则等待直到该call超时结束。

2. 同上,如果回复应答的status为[300, 399],则重新发送请求,并且转

换发送目的地,重复发送次数也限制为3次。这两种情况全部调用_eXosip_call_retry_request进行处理。

iii. 遍历call中所有的dialog,对于已经dialog信息存在的dialog进行处理,

处理方式同上,也是检查两类情况,一为应答status为401或407的认证失败错误提示,一为[300, 399]server端地址需要更改的转发提示。

b) 遍历所有的register,只处理r_id >=1 且有transaction的register,r_id < 1或者

没有last transaction的register是已经被删除的register,不需要处理。 i. 如果重发时间不为0,既该register需要一直重新发送注册,且注册时间

已经超时,该register从注册完到现在已经超过900秒,则调用函数eXosip_register_send_register进行重注册。 ii. 如果注册时间到现在为止 大于规定的重注册时间间隔-60秒,也发起重

注册,既如果设置了重注册时间间隔,必需在重注册时间间隔到达之前的60秒就开始发起重注册。

iii. 或者如果需要重注册,而且离上次注册时间已经超过120秒,并且没有接

收到注册服务器的应答或者应答不是成功注册的应答,则也发起重注册。

iv. 如果还未设置需要重注册,即第一次注册失败,且是因为认证失败而引起

的注册失败,则检查注册类型。如果是WPDIF注册方式,则检查回复中的认证码nonce是否和上一次注册失败时保存的nonce值相同,如果相同则发起重注册;如果注册方式不是WPDIF,则直接发起重注册。为了保证WPDIF注册方式的成功,在第一次注册失败时,需要提取当次服务器端的回复中的nonce值,以便在确定是否发起重注册的时候判断条件为真。

c) Notify、subscription、pub的处理同call。

4.3

Call的处理

在exosip lib库中除了一直在运行的处理对端发送过来的message的线程外,还提供了本端做为发送端发送各种request、ACK。

因为所有的message最终都属于某个transaction,而且对message的处理最终都放在transaction的event队列中进行处理,所以所有提供的包括call、notify、publish、register、subscription、refer等功能都是通过生成一个transaction上的event事件与一直运行的处理线程进行联系,当生成event之后,处理线程在轮询所有transaction时会处理到这些event,当某个event需要立即处理的时候,则可以手工启动event的处理线程,而不需要等到该处理线程处理到该transaction。

Exosip lib库提供了call的4类接口:第一次建立一个call的initial invite创建及发送接口;在dialog中创建及发送的其它request;在dialog中创建及发送对从对端发送过来的request的response;在invite请求中回应对端response的ACK的创建和发送。 4.3.1

创建Call的第一个INVITE

在excall_api.c文件中的eXosip_call_build_initial_invite和eXosip_call_send_initial_invite提供了本端发起一个新的call时的接口。

中国IMS网络SIP协议规范总体技术要求 - 30 -

Q/CT XXXX.1-2008

eXosip_call_build_initial_invite的流程如下:

入口参数的合法性检查,特别是发送目的端地址to的检查generating_request_out_of_dialog_eXosip_dialog_add_contactosip_message_set_subjectosip_message_set_expires

在创建一个新的INVITE时,而且该INVITE是call的第一个INVITE,则需要检查必要参数目的端地址to的合法性。生成一个INVITE message需要使用到的信息大部分在exosip lib库启动的时候就设定了,包括sip协议的版本,from tag 的产生机制等。需要特别指定的只有发送目的端to。

在检查合法性之后,调用通用的请求构造函数generating_request_out_of_dialog构造生成一个request message,在构造参数中指定要构建的是一个INVITE。因为是在dialog创建之前构建INVITE 请求,所以调用的接口为out_of_dialog的,即不需要从dialog中获取信息;如果是dialog内构造的新的请求,则根据sip协议,其新的request的 local cseq number必须大于dialog中的local cseq number,并且因为属于一个dialog,所以其call_id, from tag, to tag 必须同dialog相同。同时如果不是dialog中的新的INVITE请求用于更改route set,则还要使用dialog中的route set用于该请求中。

在创建成功通用的dialog外的请求后,添加INVITE相关部分字段,包括contact,subject和超时时间expires。其中contact之间使用本端内部设置的IP地址。

eXosip_call_send_initial_invite的流程:

中国IMS网络SIP协议规范总体技术要求 - 31 -

Q/CT XXXX.1-2008

eXosip_call_init_eXosip_transaction_initosip_new_outgoing_sipmessageosip_transaction_add_event 首先initial invite是用于创建一个新的call的,所以在发送一个initial invite时,创建一个new call,并且为该invite创建一个新的transaction,该transaction类型为ICT,同时将该transaction做为这个new call的c_out_tr,因为整个call是有该transaction创建的,当该transaction到达complete状态的时候该call就建立起来了。但是此时并不建立dialog,因为dialog是两端通信协商后的结果,只有收到了对端的非100的1xx应答或>200的应答才会建立一个dialog。

在创建完call和transaction之后,根据要发送的INVITE生成一个transaction上的event,将该event添加到该transaction的event队列中;并将call添加到exosip管理的call链表中,然后给call分配一个call_id,最后唤醒处理线程对transaction上的event进行处理。

在两个线程的交互方面是通过transaction的event队列来完成的,eXosip_call_send_initial_invite只是将要发送的invite生成一个event添加到event队列,真正的发送在处理线程处理该event时才会进行。 4.3.2

INVITE的ACK应答的创建和发送

eXosip_update对端发送过来的INVITE等请求的处理在 exosip_read_message中已经进行了处理,对request的应答是sip协议栈自动完成的,同时对非2xx的应答也自动完成。当接收到的是2xx的应答,在transaction层的处理会上报一个EXOSIP_CALL_ANSWERED事件给UAC层,此时需要管理程序处理该事件,创建一个ACK应答并发送该ACK应答,同时如果在超时时间内继续接收到该2xx应答,只需要重新发送该ACK应答即可。

客户端接收到2xx应答的处理参见Jcallbakck.c文件的cb_rcv2xx函数。

eXosip_call_build_ack的处理流程:

a) 根据所属的dialog,查询得到所属的call 和dialog的结构。

b) 获取该dialog中还未处理完的transaction,如果该transaction不是INVITE的

transaction则返回错误,因为ACK只会出现在INVITE的请求transaction中。

中国IMS网络SIP协议规范总体技术要求 - 32 -

Q/CT XXXX.1-2008

c) 调用函数_eXosip_build_request_within_dialog构建ACK message 并且设置

ACK的联系地址contact和INVITE相同。

d) 设置ACK的cseq number同INVITE的cseq number,ACK是一个特殊的请求,

其method与对应的INVITE不同,但是使用相同的cseq number,用于对端确定该ACK是哪个INVITE的ACK。

e) 同时设置ACK的认证信息anthorization为INVITE的anthorization。

eXosip_call_send_ack的处理流程:

a) 参数检查,需要确定要发送的ACK所属的dialog是否正确。并查询得到call

和dialog的结构地址。

b) 检查第一个路由项,如果没有 ”lr” 标识,说明是前一版本的路由设置规则,

根据协议的向后兼容性,需要调整发送目的request_uri为第一个路由的request_uri,并且保存原发送目的request_uri为路由集合中的最后一条路由。 c) 直接发送该ACK,不做为一个event添加到transaction是防止中间的消耗时间

过长,导致对端的2xx应答重发。

d) 保存该ACK,在对端重发2xx应答时重发该ACK。

4.3.3

dialog内的请求的创建和发送

eXosip_call_build_request处理流程:

因为是dialog已经建立完成,所以创建的新的dialog内的请求只需要知道request的类型即可以。创建一个新的request所需要的信息在dialog内部已经保存,包括call_id、from、from tag、to、to tag、cseq number、request_uri以及认证信息anthentication information。

在创建一个新的请求的时候,需要检查是否有未处理完的请求存在,因为按照sip协议标准,一个call内的请求是要按照顺序进行处理的,即上一个请求没有处理完,下一个请求不应该被发送出去。其中INVITE请求比较特殊,只要没有INVITE请求没有处理完,就可以发送下一个INVITE请求,而不需要等待像notify、option等的请求。

eXosip_call_send_request处理流程:

a) 检查要发送的request的合法性,并且检查所属的dialog的合法性。 b) 检查该dialog上是否有transaction没有处理完毕,如果有,则返回错误,不允

许多个请求同时在一个call上处理。这个检查和build时是一样的。 c) 为新请求创建一个transaction,并且将该transaction加入到dialog的d_out_tr

链表中,因为是请求发送方,所以本端为client端,所以创建的transaction为

中国IMS网络SIP协议规范总体技术要求 - 33 -

Q/CT XXXX.1-2008

NICT或ICT,并且是属于本端发送出去的transaction。

d) 根据该请求生成一个event,加入到该transaction的event队列中。 e) 最后唤醒处理线程对该event进行处理,即将该request发送出去。

4.3.4

Dialog内answer的创建和发送

在接收到dialog内部的request时,需要发送response。 eXosip_call_build_answer的处理流程:

a) 根据transaction id查询得到 call、dialog、transaction的结构。因为是接收到一

个请求,所以在处理请求的时候已经创建了新的transaction,所以查询在正常情况下不会失败。

b) 如果是INVITE的请求,则调用_eXosip_answer_invite_123456xx进行response

的message的构建,根据传入的最后一个参数0标识只构建message不发送该message。

c) 如果不是INVITE,则直接调用eXosip_build_response_default生成一个通用的

应答message,如果回复的应答状态为(100, 399],则最终本端和对端会建立dialog,则调用函数complete_answer_that_establish_a_dialog从request中获取部分信息。

eXosip_call_send_answer的流程:

a) 参数的合法性检查,如果回应的status不在[100,699]之间,或者transaction id

<0,则返回错误提示。

b) 根据transaction id查询得到call、dialog、transaction的结构,如果没有找到,

则返回错误。

c) 检查该transaction的合法性,如果其状态已经结束,则返回错误。

d) 如果answer还没有创建,并且是INVITE的应答,且应答status为2xx应答,

则返回错误。

e) 如果要发送的answer还没有创建,且是INVITE的应答,则调用

_eXosip_answer_invite_123456xx创建并发送应答。如果不是INVITE的应答,则返回错误。 f)

如果是INVITE的应答,且应答的status为2xx,并且dialog已经创建,则保存该2xx的应答到dialog中,并且置dialog的状态为confirmed,在未收到ACK的情况下,该2xx应答会被重新发送。

g) 所有合法性检查通过,则生成一个transaction上的event事件,并且添加到

中国IMS网络SIP协议规范总体技术要求 - 34 -

Q/CT XXXX.1-2008

transaction的event链表上。

4.4

Register的处理

同call一样,exosip lib库通用提供了创建一个新的register和发送rejister的接口,管理程序只要调用接口创建一个新的register并且调用发送接口进行发送即可。

Register注册包括初始的注册,改变超时时间和取消该注册。其区别主要为发送给注册服务器的参数expires即超时时间的不同。

如果expires为0,则为终止该注册;如果为expires大于0,则为修改或注册该register。为避免太频繁的重注册行为,规定注册的无效时间最小为100s,而且服务器可以自己配置该最小值比100大。 4.4.1

向一个服务器第一次注册

向一个服务器第一次注册时,调用接口eXosip_register_build_initial_register生成一个新的register message。该函数会进行一些合法性检查,其处理流程如下:

a) 查询所有的register,检查其注册服务器的地址与现在要注册的服务器的地址

是否相同,如果有相同的服务器地址存在,则删除原有的注册的transaction,进行重新注册。

b) 调用eXosip_reg_init生成一个新的register管理结构并且添加到exosip的j_reg

管理链表中。

c) 调整这个新的register的重注册时间,如果输入的超时时间expires <= 0,说明

是一个注销行为,则设置重注册时间为0,即不需要重注册。如果设置的重注册时间小于100,则调整为最小值100。

d) 调用_eXosip_register_build_register创建一个标准的register message,并返回新

生成的register管理结构的id。

4.4.2

调整一个注册的注册超时时间

接口eXosip_register_build_register用于创建一个注册message,用于调整已经注册成功的注册的超时时间。

a) 接口根据传入的register的id查询得到register的管理结构。如果查询失败,

则返回错误。

b) 重置该register的超时时间为传入的参数expires,并且根据协议的规范调整其

范围到[100, 3600]。

c) 检查被调整expires的注册的前一个请求处理是否已经结束,如果没有,则返

回错误提示。

d) 调用_eXosip_register_build_register创建一个新的register message并返回

中国IMS网络SIP协议规范总体技术要求 - 35 -

Q/CT XXXX.1-2008

register的id。

4.4.3

发送一个register注册

前两个的创建新的register message之后,都需要调用接口eXosip_register_send_register发送新创建的message。

Register的发送流程如下:

a) 检查要发送注册message的register的上一个注册请求的状态码是否已经到结

束状态,如果不是,则返回错误。

b) 为要发送的register message创建一个新的transaction,每个新的请求都对应一

个新的transaction。并且将该transaction挂接在register的管理结构 jr->r_last_tr中。每个主次只保存最近的一个transaction,因为注册必须是串行的,在上一个注册还没有处理完毕的情况下,不允许在同一个注册服务器上发送新的注册请求。

c) 根据发送的message生成一个transaction上面的event并挂接在transaction的

event队列中。

d) 唤醒处理线程,处理transaction的event。

Exosip lib包中提供的notify、publish、subscribe、message和options等的功能和call、register的功能是相近的,不做详细解释。

中国IMS网络SIP协议规范总体技术要求 - 36 -

联系客服:779662525#qq.com(#替换为@)