這章有些單詞沒翻譯,感覺不翻譯更好。
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的所有功能。