Chapter 4:Parser
4.1 Features
PJSIP解析器的特性
#自顶向下手写解析器。使用PJLIB的扫描器,快速并且能够减少复杂的解析,使解析器可读。
#如上所述,解析很快。在一台P4/2.6HZ的机器上,它能够在一秒钟内解析超过68K的典型800字节SIP消息或860K的80字节url。注意速度可能会有不同,不同的PJSIP版本有不同的表现。
#可重入,在多处理器的机器上可以扩展( It’s reentrant, which will make it scalable on machine with multiprocessors)
#可扩展,modules可以在解析器里面添加新的header或者URI
解析器有有许多可实现的方式来实现更高的性能,比如:
#解析器采用所有消息元素的零拷贝方式,比如解析消息元素(pvalue,),解析器不会把pvalue的值拷到消息的相应字段里,而是把指针和长度放到相应字段。这个是有可能的,因为PJSIP在库中全程使用pj_str_t,且不要求字符串以NULL结尾。
#解析器在消息结构体中使用PJLIB的内存池作为内存分配,速度比传统的malloc()函数快好几倍。
#解析器采用零同步,解析器可重入所以不需要同步函数。
#使用PJLIB的try/catch异常框架,不仅简化了解析器使其可读,还能否减少解析中冗余的错误检查。对于异常框架,只需要在解析器的顶层函数添加一个异常的处理程序(handler)。
还有一个特性是PJSIP的解析器不使用lazy parsing,虽然可能一些人会夸赞它的用处。在设计之初,我们就决定不用lazy pasring,原因如下:
#使事情复杂化,尤其是错误处理。如果使用lazy parsing,基本上程序的所有部分都要为之后应用程序访问特殊消息元素解析时的出错处理做准备。
#后面我们也相信PJSIP解析器会很快所以不需要lazy parsing。尽管这么说,PJSIP解析器里也会有一些开关,来忽略部分类型的应用程序的一些header(比如,proxies,只需要检查很少的几个header type)
4.2 Functions
PJSIP的解析器声明主要在<pjsip/sip_parser.h>,定义在 <pjsip/sip_parser.c>。其他的库可可能提供其他的解析函数和扩展解析器(比如 <pjsip/sip_tel_uri.c>解析TEL URI以及向主解析器注册这个函数)。
pj_status_t pjsip_find_msg( const char *buf,
pj_size_t size,
pj_bool_t is_datagram,
pj_size_t *msg_size);
检查buf中收到的包是否包含有效的sip消息,如果发现有,就把消息的大小放到msg_size,如果指定了is_datagram,函数将总是返回PJ_SUCCESS。
注意函数期望的缓冲区以NULL结尾。
pjsip_msg* pjsip_parse_msg( pj_pool_t *pool,
char *buf, pj_size_t size,
pjsip_parser_err_report *err_list);
将buf中的缓冲区buffer解析为sip消息。 如果最后消息的request/status 行被成功解析则返回消息。如果不为空,所有解析的报错都会放到err_list。
注意函数期望的缓冲区以NULL结尾。
pjsip_msg* pjsip_parse_rdata( char *buf, pj_size_t size,
pjsip_rx_data *rdata );
解析buf中的缓冲区消息。如果最后消息的request/status 行被成功解析则返回消息。此外,函数将更新rdata中msg_info部分的指向header的指针。
注意函数期望的缓冲区以NULL结尾。
4.2.2 URI Parsing
pjsip_uri* pjsip_parse_uri( pj_pool_t *pool,
char *buf, pj_size_t size,
unsigned option);
解析buf中的数据解析为SIP URI。如果option参数为 PJSIP_PARSE_URI_AS_NAMEADDR,函数将会一直把URI作为地址。如果是 PJSIP_PARSE_URI_IN_FROM_TO_HDR,函数将不会解析URI后面的参数,如果URI没有以括号结束(视为header的参数而不是URI参数)。
这个函数可以解析库识别的各种URI,正确返回URI实例。
注意函数期望的缓冲区以NULL结尾。
4.2.3 Header Parsing
void* pjsip_parse_hdr( pj_pool_t *pool, const pj_str_t *hname,
char *line, pj_size_t size,
int *parsed_len);
根据hname解析line中的header内容(冒号后的头的一部分)。返回相应header实例。
注意函数期望的缓冲区以NULL结尾。
pj_status_t pjsip_parse_headers( pj_pool_t *pool,
char *input, pj_size_t size,
pj_list *hdr_list );
在input里找到多个header,将结果放到hdr_list。函数建议用换行符(例如在sip消息里)或&来分割header。最后一个header的分隔符可加可不加。
注意函数期望的缓冲区以NULL结尾。
4.3 Extending Parser
注册函数指针可以解析新类型的header或URI来扩展解析器。
typedef pjsip_hdr* (pjsip_parse_hdr_func)(pjsip_parse_ctx *context);
pj_status_t pjsip_register_hdr_parser( const char *hname,
const char *hshortname,
pjsip_parse_hdr_func *fptr);
注册新函数来解析新的sip消息头。
typedef void* (pjsip_parse_uri_func)(pj_scanner *scanner, pj_pool_t *pool,
pj_bool_t parse_params);
pj_status_t pjsip_register_uri_parser( char *scheme,
pjsip_parse_uri_func *func);
注册新函数来解析新的URI格式。