翻譯-pjsip開發者指南(六)傳輸層

 Chapter 6:Transport Layer
傳輸通常是通過網絡來收發消息。PJSIP的傳輸框架是可擴展的,也就是說應用程序可以註冊自己傳輸消息的方法。
 6.1 Transport Layer Design
6.1.1 “Class Diagram”

下圖展示了傳輸層各實例之間的關係。

 6.1.2 Transport Manager
傳輸管理器(pjsip_tpmgr)管理所有傳輸對象和工廠。提供下述功能:
  #通過傳輸引用計數器和空閒計時器來管理傳輸的生命週期
  #管理傳輸工廠
  #從傳輸層收包,解包,分發SIP消息到endpoint
  #基於傳輸類型和遠端地址匹配合適的傳輸方式向目的地傳輸SIP消息。 
  #沒有可用的傳輸時動態創建新的傳輸來向新的目的地傳輸消息。
每個endpoint只有一個傳輸管理器,且一般來說對應用程序不可見;應用程序必須使用endpoint提供的函數。
 6.1.3 Transport Factory

傳輸工廠(pjsip_tpfactory)被用來動態連接遠端endpoint。其中一個例子是TCP傳輸,一個TCP傳輸需要兩端都建立TCP連接。

當傳輸管理器發現需要爲新目的地創建新的傳輸,會根據匹配規則(比如傳輸類型)查找傳輸工廠,然後工廠創建連接。
傳輸工廠的對象聲明如下:
 6.1.4 Transport
傳輸對象使用 pjsip_transport 結構體表示。每個結構體實例通常代表一個socket句柄(如UDP,TCP),儘管傳輸層也支持non_socket的連接方式。
 General Transport Operations
從框架的角度來看,傳輸對象是一個動態的對象。框架沒有輪詢傳輸對象的機制。相反的,傳輸對象必須自己尋找方法來接收網絡的報文,然後分發消息至傳輸管理器來做進一步的處理。
 推薦的方法是註冊傳輸socket句柄到endpoint的I/O隊列(pj_ioqueue),這樣的話當endponit輪詢I/O隊列的時候,網絡的包就可以被傳輸對象收到。
一旦傳輸對象收到了一個包,必須調用 pjsip_tpmgr_receive_packet()向傳輸管理器分發消息,這樣可以解析並分佈到堆棧的空閒部分。傳輸對象必須初始化接收數據緩衝區( pjsip_rx_data)的成員 tp_info 和 pkt_info 。
每個傳輸對象都有一個函數指針來向網絡發送消息(如傳輸對象的send_msg()屬性)。應用程序(或堆棧)調用 pjsip_transport_send()來發送消息到網絡中。發送的包可能會異步完成;如果是這樣,傳輸必須在send_msg()中返回PJ_EPENDING,並且調用消息發送至目的地後的指定參數的回調函數。
 Transport Object Declaration
下面的代碼聲明傳輸對象。

 struct pjsip_transport
 {
     char obj_name[PJ_MAX_OBJ_NAME]; // Name.
     pj_pool_t *pool; // Pool used by transport.
     pj_atomic_t *ref_cnt; // Reference counter.
     pj_lock_t *lock; // Lock object.
     int tracing; // Tracing enabled?
     pjsip_transport_type_e type; // Transport type.
     char type_name[8]; // Type name.
     unsigned flag; // See #pjsip_transport_flags_e
     pj_sockaddr local_addr; // Bound address.
     pjsip_host_port addr_name; // Published name (e.g. STUN address).
     pj_sockaddr rem_addr; // Remote addr (zero for UDP)
     pjsip_endpoint *endpt; // Endpoint instance.
     pjsip_tpmgr *tpmgr; // Transport manager.
     pj_timer_entry idle_timer; // Timer when ref cnt is zero.
     /* Function to be called by transport manager to send SIP messages. */
     pj_status_t (*send_msg)( pjsip_transport *transport,
             pjsip_tx_data *tdata,
             const pj_sockaddr_in *rem_addr,
             void *token,
             void (*callback)( pjsip_transport*,
                 void *token,
                 pj_ssize_t sent));
     /* Called to destroy this transport. */
     pj_status_t (*destroy)( pjsip_transport *transport );
     /* Application may extend this structure. */
 };


 Transport Management
傳輸通過函數 pjsip_transport_register()註冊到傳輸管理器上。在函數調用之前,所有傳輸的結構體成員都必須初始化。
傳輸的生命週期由傳輸管理器自動管理。每次引用計數器爲零,空閒計時器將啓動。當空閒計時器和引用計數器都爲零,傳輸管理器將調用 pjsip_transport_unregister()來銷燬傳輸。這個函數從傳輸管理器的hash表中註銷傳輸並銷燬傳輸。

有些傳輸即使沒有在傳輸依然要一直存在(比如UDP傳輸,是一個單例實例)。爲了讓這種傳輸不被銷燬,初始化引用計數器爲1,這樣計數器永遠也不會達到0。
 Transport Error Handling
傳輸的錯誤(比如發包失敗,重置連接失敗)都通過傳輸用戶(transport user)來處理。傳輸對象除了在函數返回值報告這個錯誤,並不需要處理這些錯誤。尤其不需要嘗試連接失敗或關閉的連接。

 6.2 Using Transports
6.2.1 Function Reference

pj_status_t
pjsip_endpt_acquire_transport( pjsip_endpoint *endpt,
        pjsip_transport_type_e t_type,
        const pj_sockaddr_t *remote_addr,
        int addrlen,
        pjsip_transport **p_transport);

獲取使用t_type類型的傳輸,並將消息發送到目的地remote_addr。注意,如果成功獲取傳輸,傳輸的引用計數器將增加。

 

 pj_status_t pjsip_transport_add_ref( pjsip_transport *transport );

增加傳輸引用計數器。這個函數用來避免銷燬傳輸,如果idle timer存活將取消。

 

 pj_status_t pjsip_transport_dec_ref( pjsip_transport *transport );

減少傳輸引用計時器。當計時器爲零,idle timer將啓動,當timer過期,並且計數器仍爲零,傳輸管理器將銷燬傳輸。

 

 pj_status_t pjsip_transport_send( pjsip_transport *transport,
         pjsip_tx_data *tdata,
         const pj_sockaddr_t *remote_addr,
         int addrlen,
         void *token,
         void (*cb)(void *token,
             pjsip_tx_data *tdata,
             pj_ssize_t bytes_sent));

使用傳輸transport發送tdata的數據到remote_addr。如果函數立即完成且數據已發送,函數返回PJ_SUCCESS。如果函數以錯誤結束,返回非零。這兩種情況不會調用回調函數。
如果函數不能立即返回(當底層socket buf 滿了),函數返回PJ_EPENDING,調用者將以回調cb來被告知完成。如果掛起的發送操作以錯誤結束,在回調的bytes_sent參數中,錯誤碼爲該錯誤碼的負值(獲取錯誤碼使用 pj_status_t status = -bytes_sent)。
這個函數原樣發送消息,不會校驗消息,也不會修改via header。

 6.3 Extending Transports
PJSIP的傳輸可以使用自定義傳輸來擴展。理論上來說,不限於TCP/IP任何類型的傳輸都可以插入到傳輸管理器的框架中。詳細信息在 <pjsip/sip_transport.h>, sip_transport_udp.[hc]。

 6.4 Initializing Transports
PJSIP默認不啓動任何傳輸(也不啓動內置built-in的傳輸);而是由應用程序來初始化和啓動它需要使用的傳輸。
下面是內置UDP和TCP傳輸的初始化函數。

 6.4.1 UDP Transport Initialization
PJSIP提供兩種方式來初始化和啓動UDP傳輸。這些函數在頭文件 <pjsip/sip_transport_udp.h>中聲明。

pj_status_t pjsip_udp_transport_start( pjsip_endpoint *endpt,
        const pj_sockaddr_in *local_addr,
        const pj_sockaddr_in *pub_addr,
        unsigned async_cnt,
        pjsip_transport **p_transport );

創建、初始化,註冊和啓動一個新的UDP傳輸。UDP的socket綁定到local_addr上。如果endpoint位於firewall、NAT或者其他端口轉發設備之後,pub_addr將作爲傳輸的地址;否則pub_addr就要和local_addr一致。參數async_cnt 指定允許多少個同步操作用於該傳輸,爲了更好的性能,數值必須和節點中處理器的數目相等。
如果傳輸成功啓動,函數返回PJ_SUCCESS,傳輸將在p_transport參數中返回,這樣應用程序就可以立即使用傳輸。應用程序不需要向管理器註冊傳輸;函數返回成功的時候這個函數已經完成了這些。
對於錯誤,函數返回非零錯誤碼。

 

 pj_status_t pjsip_udp_transport_attach( pjsip_endpoint *endpt,
         pj_sock_t sock,
         const pj_sockaddr_in *pub_addr,
         unsigned async_cnt,
         pjsip_transport **p_transport);

在 UDP socket已經可用的情況下,使用這個函數來創建、初始化、註冊和啓動一個新的UDP傳輸。這個函數的用處是,比如應用程序剛用STUN解析一個socket的公共地址,沒有關閉和重建socket,那應用程序可重用這個socket到SIP傳輸上。


 6.4.2 TCP Transport Initialization
TODO.
6.4.3 TLS Transport Initialization
TODO.
6.4.4 SCTP Transport Initialization
TODO.
(我下的這個指南還沒有描述,等待新版補充)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章