翻译-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.
(我下的这个指南还没有描述,等待新版补充)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值