这章有些单词没翻译,感觉不翻译更好。
Chapter 2:Module
在pjsip的应用中Module framework是软件组件中分发消息的主要方法。所有的软件组件,包括传输层和会话层,都是作为模块实现。没有modules,核心堆栈将不知道如何去处理sip消息。
module基于一个简单但却功能强大的抽象接口。对于收到的消息,endpoint将消息 从最高级别的模块开始发送至所有模块,直到有一个模块能够处理这条消息。对于发出的消息,endpoint允许module按照自己的意愿在发送到网络之前来修改消息。
2.1.1 Module Declaration
module的接口定义在 <pjsip/sip_module.h>
所有函数指针都是可选的,如果没有指定,视为返回成功。
endpoint调用load, start, stop, and unload 这四个指针来控制module的状态。下面的图表展示了module状态的生命周期。
on_rx_request() 和 on_rx_response() 函数指针是module从endpoint或者其他modules收取消息的主要手段。这些回调的返回值都很重要。如果回调返回非零(即true),则表示module已经处理了这个消息。在这种情形下,endpoint会停止分发消息到其他模块。2.1.3小节将会详细描述modules对收到消息的处理。
transport manager 在消息传送后调用on_tx_request() 和 on_tx_response()这两个指针,允许一些类型的modules对消息(eg消息签名)做最后的更改。所有modules都必须返回PJ_SUCCESS(le 0),否则传输将被取消。2.1.4小节将对模块对发送消息处理进行详细描述。
on_tsx_state()用来接收当传输状态改变时的通知,比如收消息,发消息,timer事务,传输错误事务。2.1.5小节详细描述回调函数的信息。
2.1.2 Module Priorities
模块的优先级指明了处理回调优先调用module的顺序。有较高(数字越小)优先级的module最先调用 on_rx_request() 和 on_rx_response(),最后调用 on_tx_request() 和on_tx_response()。
下面是设置模块优先级的标准
note:优先级的数字越小,代表的优先级越高
transport manager 使用PJSIP_MOD_PRIORITY_TRANSPORT_LAYER这个优先级。这个优先级目前仅用来控制消息传输,优先级比这个低(数字比较大)的module在传输层处理消息之前将会调用 on_tx_request()/on_tx_response(),而优先级比这个高的则会在传输层已经处理消息之后调用。2.1.4详细介绍module的消息处理。
事务层使用PJSIP_MOD_PRIORITY_TSX_LAYER优先级。事务层接收所有属于该事务的消息。
UA层(dialog framework)使用PJSIP_MOD_PRIORITY_UA_PROXY_LAYER优先级。UA层接收所有属于该对话集的消息。
dialog usages使用PJSIP_MOD_PRIORITY_DIALOG_USAGE优先级。目前PJSIP实现了两类dialog usages:邀请会话和事务订阅会话(包括REFER订阅)。dialog usages 接收所有属于特定会话中的dialog的消息。
PJSIP_MOD_PRIORITY_APPLICATION是一般应用程序去使用transactions,dialogs和dialog uasegs的一个特定( appropriate value)值。
2.1.3 Incoming Message Processing by Modules
当有消息传入时,放入接收消息缓冲区( receive message buffer)( struct pjsip_rx_data, 5.1小节 “Receive Data Buffer”)。transport manager解析这个消息,然后把解析后的数据放入接收消息缓冲区中发送到endpoint。
endpoint通过调用on_rx_request() 或 on_rx_response()来向每个注册过的module分发收到的消息缓存,按照优先级高到低,直到有module返回非零。当有一个module返回非零,意味着已经有module处理这条消息了,所以endpoint会停止向其他的module分发消息。
返回非零的回调module也可以向其他module分发消息。例如,事务层module,根据收到的消息,处理并发送至同样必须是module的transaction user。 transaction通过调用本模块的 on_rx_request() 和on_rx_response()发送消息至transaction user。通过在收消息的缓冲区设置transaction field,来区分消息是在事务里还是事务外。
下面的图表展示了modules是如何一层层调用其他module的。
2.1.4 Outgoing Message Processing by Modules
发出的请求和响应放到 传输数据缓冲区(transmit data buffer-pjsip_tx_data),其中包含消息结构本身,内存池,连续缓冲区和传输信息。
当调用pjsip_transport_send()发送消息时,transport manager调用on_tx_request()或on_tx_response ()从最低优先级开始发往各个module。这些回调函数被调用的时候,消息可能被传输层处理了,也可能没有。传输层需要把这些信息放入传输缓冲区(transmit buffer):
#传输信息
#在连续的缓冲区内打印消息结构
优先级低于PJSIP_MOD_PRIORITY_TRANSPORT_LAYER的modules将会在获得这些信息之前收消息。也就是说没有计算目标地址,也没有打印连续缓冲区。
如果modules想要在打印进缓冲区前修改消息结构,设置的优先级就必须比传输层的优先级高。如果modules想要在传输到网络时看到真实的数据包字节(logging purpose),就必须设置优先级低于传输层。
note: 设置优先级高于传输层的一个实例就是logging module,它想打印已经放入连续缓冲区并且计算过目的地址的发送消息。
这种情况下,module必须以PJ_SUCCESS作为回调的返回值。如果module返回其他错误码,传输会被取消,错误码会返给 pjsip_transport_send()的调用者。
2.1.5 Transaction User and State Callback
module定义中一个特殊的回调(on_tsx_state)被用于接收事务状态改变时特定事务的通知。这个回调是唯一的因为传输的状态可能由非消息相关的事务改变(比如timer超时,传输错误)。
module在特定的事务中已经注册为transaction user后,才可以调用回调函数。每一个事务中只允许一个transaction user。transaction user可以在每个事务的基础上添加至事务中。
对于dialog中的事务创建,transaction user在特定的dialog中添加到UA层的module。当应用程序手动创建事务时,可以将自己设为transaction user。
收到重传的请求或响应不会调用 on_tsx_state()。注意传输或收到临时响应不被视为是重传,也就是说会调用这个回调函数。
2.1.6 Module Specific Data
一些PJSIP组件可以放置module的数据。方便的来说这种container叫 mod_data,是void类型的指针数组,以module ID作为索引。
例如,一个收到数据包的buffer (pjsip_rx_data) 有如下的module数据定义:
mod_data数组以module ID为索引。module ID在module向endpoint注册时确定。
当把收到的数据buffer(pjsip_rx_data)传递到modules时,module可以把数据(module specific data)以一个合适的索引放到mod_data里,之后这个值可以被module或者应用程序获取。例如,事务层在mod_data里放入匹配的事务实例,UA层放入匹配的会话实例。应用程序可以通过调用简单的数组查找函数pjsip_rdata_get_tsx()或pjsip_rdata_get_dlg ()来检索id。如下:
2.1.7 Callback Summary
下表总结了事务的发生以及特定回调的触发。on_tsx_state()仅有在程序选择处理有状态的请求时调用。
Event | on_rx_request() or | on_tsx_state() |
Receipt of new requests or responses 收到请求或响应 | Called | Called |
Receipt retransmissions of requests or 收到请求或响应的重传 | Called ONLY when 仅当优先级高于事务层时调用 | Not Called |
Transmission of new requests or responses. 传递新的请求或相应 | Not Called | Called |
Retransmissions of requests or responses. 请求或响应的重传 | Not Called | Not Called |
Transaction timeout 事务超时 | Not Called | Called |
Other transaction failure events (e.g. DNS 事务失败(DNS,查询失败,传输失败) | Not Called | Called |
2.1.8 Sample Callback Diagrams
收到的transaction和dialog外的消息
处理过程如下
1)transport manager(pjsip_tpmgr)向endpoint发送所有收到的消息(解析消息后)。
2)endpoint(pjsip_endpt)向已注册的回调分发消息。回调列表的第一个是事务层。事务层把消息放到事务表里,且没有相匹配的事务。
3)endpoint向回调列表的下一个即UA发送消息。
4)UA把消息放到哈希表中,且没有匹配的dialog set。
5)endpoint继续向下一个注册的回调发送消息直到应用程序。应用程序处理消息(有状态的响应,创建UAS事务,代理请求,创建dialog)。
收到事务中的消息
处理如下:
1)transport manager(pjsip_tpmgr)向endpoint发送所有的消息(解析消息后)。
2)endpoint(pjsip_endpt)向已注册的回调分发消息。回调列表的第一个是事务层。事务层把消息放到事务表里,且有匹配的事务。
3)因为事务回调返回pj_true,所以endpoint不会继续分发消息。
4)事务处理响应。如果消息是重发,停止处理。否则把消息发到transaction的transaction user(TU),可以是dialog或应用程序。
5)如果TU是一个dialog,dialog处理响应并发送至dialog user(DU,如应用程序)。
6)如果收到的消息已经改变了事务的状态,事务将会通知TU这个新状态。
7)如果TU是一个dialog,可能还会通知应用程序dialog状态的改变。
收到的消息在dialog里但不在事务里
处理如下:
1)transport manager(pjsip_tpmgr)向endpoint发送所有的消息(解析消息后)。
2)endpoint(pjsip_endpt)向已注册的回调分发消息。回调列表的第一个是事务层。事务层把消息放到事务表里,并且没有相匹配的事务。
3)endpoint向modules列表的下一个发送消息,直到UA module
4)UA把消息放到dialog的哈希表中,且有匹配的dialog。
5)UA把消息发送到相应的dialog。
6)dialog为收到的请求创建事务,然后通过调用dialog usages的on_rx_request()和on_tsx_state ()分发请求。
2.2 Module Management
pjsip的endpoint管理modules。应用程序必须手动向endpoint注册每个module,以便于堆栈的识别。
2.2.1 Module Management API
module管理的API在 <pjsip/sip_endpt.h>
pj_status_t pjsip_endpt_register_module( pjsip_endpoint *endpt,pjsip_module *module );
向endpoint注册module。endpoint会调用load和start函数来初始化module,并分配给module一个module ID。
pj_status_t pjsip_endpt_unregister_module( pjsip_endpoint *endpt,pjsip_module *module );
向endpoint解绑module。endpoint调用stop和unload函数来结束一个module。
2.2.2 Module Capabilities
module可以向endpoint声明新的功能。目前endpoint管理如下的功能:
#允许sip方法(允许的头字段)
#支持的sip扩展(支持的头字段)
#支持的content type(接受的头字段)
这些头字段将根据需要自动添加到发出的请求或响应中。
module通过调用pjsip_endpt_add_capability()来声明新的功能。
pj_status_t pjsip_endpt_add_capability( pjsip_endpoint *endpt,
pjsip_module *mod,
int htype,
const pj_str_t *hname,
unsigned count,
const pj_str_t tags[]);
向endpoint注册新功能。htype参数指明向哪个头添加功能,比如 PJSIP_H_ACCEPT,PJSIP_H_ALLOW, and PJSIP_H_SUPPORTED。hname是可选项,用来指明核心堆栈无法识别的头字段功能。count和tags参数指明了加入头字段的tag字符串数组。
const pjsip_hdr* pjsip_endpt_get_capability( pjsip_endpoint *endpt,
int htype,
const pj_str_t *hname);
获取功能头字段,包含为特定字段注册到endpoint的所有功能。