PJSIP開發手冊之傳輸層(六)
第六章 傳輸層
Transport是用來通過網路傳送/接收訊息的。PJSIP的transport框架是可擴充套件的,這意味著程式可以自己的transport來傳輸訊息。
傳輸層設計
類圖
下圖展示了transport層的各個例項之間的關係。
Transport Manager
Transport Manager(pjsip_tpmgr)管理所有的transport物件和工廠。它提供以下功能:
- 通過使用引用計數器和空閒的定時器管理transport的生命週期
- 管理transport工廠
- 從transport接收包,解析包並把SIP訊息傳給Endpoint
- 基於transport的型別和遠端的地址,找到匹配的transport
- 當不存在可用的transport來發送SIP訊息時,動態建立transport
每個Endpoint只有一個transport manager。Transport manager通常是應用不可見的,應用需要使用Endpoint提供的函式。
Transport工廠
Transport工廠(pjsip_tpfactory)是用來建立與遠端Endpoint的連線。這種型別的連線的一個例子就是TCP transport。每個目的地址需要建立一個TCP transport。
當transport manager檢測到它需要為新的地址建立新的transport,它將會找到符合的transport
Transport
Transport物件用pjsip_transport結構表示。每個例項代表一個socket處理(如UDP,TCP),但是transport層也支援non-socket的傳輸。
通用的transport操作
從框架角度來看,transport物件是一個活動的物件。框架中並不存在輪詢transport物件的機制。因此傳輸物件必須找到自己的方式來從網路上接收資料包,以及將資料包提交到transport manager為之後的處理。
推薦的實現方法是將transport的socket處理註冊到Endpoint的I/O佇列(pj_ioqueue_t)。因此,當Endpoint
一旦transport物件接收了一個包,它必須呼叫pjsip_tpmgr_receiver_packet()函式將這個包傳給transport manager,這樣這個包將被解析並分發到棧剩下的部分。Transport物件必須初始化接收資料快取tp_info和pkt_info。
每個transport物件有一個指向傳送訊息到網路上的函式指標(如send_msg())。應用(或棧)向網路傳送訊息通過呼叫pjsip_transport_send()函式,這個函式最終也會呼叫各個transport物件的send_msg()函式。傳送包可以非同步完成,transport物件的send_msg()必須返回PJ_EPENDING,並且當訊息到達目的地時,呼叫引數內指定的回撥函式。
Transport物件宣告
下面程式碼展示了transport物件的宣告。
Transport管理
通過呼叫pisip_transport_register()函式來註冊transport到transport manager。在呼叫此函式前transport結構中的所有成員必須被初始化。
Transport的生命週期由transport manager自動管理。每當transport的引用計數器為0時,一個空閒定時器將啟動。當空閒的計數器超時時並且引用計數器仍為0,transport manager將呼叫pjsip_transport_unregister()來銷燬transport。這個函式將transport從transport manager的雜湊表上登出,並最終銷燬它。
一些transport需要一直存在,即使沒有被使用(如UDP transport,它是一個單例例項)。為了阻止那個transport被刪除,它需要把引用計數器初始設定為1,這樣,引用計數器將永不為0。
Transport錯誤處理
Transport使用者負責處理transport錯誤(如傳送包失敗或連線重置)。Transport物件自己不需要處理這種錯誤,除了在函式的返回值報出錯誤。特別地,它一定不要嘗試去重連線一個失敗/關閉的連線。
使用transport
函式指南
獲取t_type型別的transport來發送訊息到目的地remote_addr。注意,當transport被成功獲取,這個transport的引用指標將增加。
增加transport的引用計數器。這個函式阻止transport被銷燬,並且它可以取消活動的空閒計數器。
減少transport的引用計數器。每當transport的引用計數器為0時,一個空閒定時器將啟動。當空閒的計數器超時時並且引用計數器仍為0,transport manager將呼叫pjsip_transport_unregister()來銷燬transport。
傳送tdata到remote_addr使用transport。如果函式立刻完成且資料已經發送,則返回PJ_SUCCESS。如果函式立刻完成但有錯誤,則返回一個非0的錯誤碼。這兩種情況下,回撥函式都不會被呼叫。
如果函式沒有立刻完成(如當下層socket快取滿),函式將返回PJ_EPENDING,並且呼叫者將通過cb回撥函式被通知。如果待發送的操作完成時有錯誤,則bytes_sent是負值的錯誤碼(使用”pj_status_t status = -bytes_sent”來獲取錯誤碼)。
該函式只會照原樣傳送訊息,不會對訊息做任何驗證。Via頭部域也不會被修改。
擴充套件transports
PJSIP可以擴充套件自定義的transports。理論上,所有型別的transport,不侷限於TCP/IP,都可以加入transport manager的框架中。
初始化transports
PJSIP是預設不會開始任何的transport(甚至內建的transport);應用在需要時會被初始化並開始transports。
下面是內建的UDP和TCP transport的初始化。
UDP transport初始化
PJSIP提供兩種選擇去建立、初始化和開始UDP transports。下面函式宣告在<pjsip/sip_transport_udp.h>中:
建立,初始化,註冊和開始一個新的UDP transport。UDP socket將被繫結到local_addr。如果Endpoint位於防火牆/NAT等埠轉發裝置後面,那麼pub_addr可以用作此transport物件的公共地址;否則pub_addr應該與local_addr相同。引數async_cnt指定這個transport物件可以允許有多少個併發操作,為了效能最大化,這個引數應該等於節點的處理器數。
如果transport物件啟動成功,函式返回PJ_SUCCESS,且傳輸物件返回到p_transport引數中。應用可以立刻使用這個transport物件,而不需要向transport manager註冊,因為這個函式返回成功時,就已註冊過了。
關於錯誤,函式將返回一個非0的錯誤碼。
在UDP socket已經可用的情況下建立、初始化、註冊和啟動一個新的UDP transport物件。這個函式在如下的情況下非常有用,當應用已使用了STUN解析了socket的公共地址,這時可以不用關閉它後再重新建立一個新的socket,而是可以直接重用此socket。
TCP transpor初始化
待做
TLS transport初始化
待做
SCTP transport初始化
待做