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 -