翻译-pjsip开发者指南(八)事务

 Chapter 8:Transactions
 8.1 Design
8.1.1 Introduction

PJSIP中的事务用头文件< PJSIP /sip_transaction.h>中的pjsip_transaction结构表示。事务的生命周期一般来说有以下的步骤:
# 被pjsip_tsx_endpt_create_uac() / pjsip_tsx_create_uas()创建
#初始化UAS事务之后,应用需要调用 pjsip_tsx_recv_msg()来传递初始请求消息,以便事务的状态可以从NULL变为TRYING。随后的请求重传也可以被事务包含。
#当应用想要通过事务来发送请求或响应消息,将调用 pjsip_tsx_send_msg()
#当消息传递给事务(包括传入消息的endpoint,传出消息的事务用户)或timer失效,事务的状态会自动改变,通过调用 on_tsx_state()来通知事务用户。
#状态变为 PJSIP_TSX_STATE_TERMINATED时,事务要自动销毁。应用也可以通过调用 pjsip_tsx_terminate()强制终止事务。
 8.1.2 Timers and Retransmissions
事务只有两种timer:重传timer和超时timer。这两种timer类型值是事务根据事务类型(UAS,UAC),传输(可靠,非可靠),方法(邀请,非邀请)来自动设置的。
应用程序只能在全局的基础上更改timers的间隔值(甚至可能只在编译期间)。
一个事务能处理传入和传出的重传消息。传入的重传消息,能够被事务包含或忽略,事务不会发(传入)重传消息的通知。必要的时候,事务会自动重传传出消息。同样的事务不会有传出通知。
 8.1.3 INVITE Final Response and ACK Request
Failed INVITE Request
note:对于失败的INVITE请求,事务的行为完全依照RFC 3261
 Client transaction:当一个客户端的INVITE事务收到关于INVITE的300-699的最终响应,会自动发出ACK响应。在终止前,事务会等待一定的timer间隔D,在这期间收到的300-699的响应都会自动回复ACK请求。
 Server transaction:当一个服务器的INVITE事务 被要求发送300-699的最终响应,在ACK请求收到或者timer 间隔H已经到了之前会发送响应并继续转发响应。在此间隔内,如果收到了ACK请求,事务会确认状态并在timer I到期时销毁。当timer H 到期前仍未收到一个有效的ACK请求,事务将被销毁。
 Successfull INVITE Request
Client transaction:当一个客户端的INVITE事务收到一个INVITE的2xx的最终响应,将响应传给TU(dialog or application)后将销毁。之后收到的2xx响应的重传都会直接传给dialog或者application。
在任何情况下,application都必须在收到INVITE的最终响应之后发送ACK的请求。
 Server transaction:当服务器的INVITE事务被要求发送2xx最终响应,他将会发送响应并且保持重传这个响应直到收到ACK或者事务被application调用的
 pjsip_tsx_terminate()中断。
为了实现简单,一个典型的UAS dialog通常会让事务来处理INVITE响应的重传。但是代理应用必须在收发2xx响应后立即销毁UAS事务,以便端到端的UA可以处理2xx的重传。
 note:对于成功的请求,INVITE的服务器事务的这种行为和RFC3261不同,即INVITE服务器事务必须在2xx响应发送后销毁。PJSIP事务行为允许更简单的dialog的处理,同时维护了代理应用兼容FRC3261的灵活性。
    INVITE服务器事务的默认行为可以通过事务创建后设置 transaction->handle_200resp为 0来重写。此情形下,UAS INVITE事务将在2xx响应发送后立马销毁。
 8.1.4 Incoming ACK Request
 当服务器INVITE事务以非成功响应完成时,ACK的响应会被事务接收。TU将不会被通知到来的ACK请求。
 当serverINVITE事务以2xx的最终响应完成时,第一个ACK请求将会通知给TU,之后收到的ACK重传将不会通知TU。
 8.1.5 Server Resolution and Transports
事务使用核心API pjsip_endpt_send_request_无状态()和pjsip_endpt_send_response()发送传出消息。这些函数提供服务器解析,发送消息的传输创建,以及检测到失败后切换传输的故障转换。事务使用这些函数提供的回调来监控传输的进度以及跟踪所使用的传输。
事务对当前使用的传输加入了引用计数器。
 TCP Connection Closure
一个TCP连接的关闭不会自动引起事务的失败。事实上,在它尝试发送消息之前,都不会检测失败。当它这样做的时候,使用切换的传输,将遵循发送消息的常规过程。
 8.1.6 Via Header
Branch Parameter

UAC事务将自动确定via头中唯一的branch参数,当没有该参数的时候。如果branch参数已经存在,事务将使用它作为key,遵循RFC3261和2543的规则。
 Via Sent-By
Via sent-by is always put by pjsip_endpt_send_request_stateless() and
pjsip_endpt_send_response().

 8.2 Reference
8.2.1 Base Functions

 pj_status_t pjsip_tsx_layer_init_module( pjsip_endpoint *endpt );
向指定的endpoint初始化,注册事务层模型。


 pjsip_module *pjsip_tsx_layer_instance(void);
获取事务层实例。


 pj_status_t pjsip_tsx_layer_destroy(void);
关闭事务层模型并从注册的endpoint注销


 pj_status_t pjsip_tsx_create_uac ( pjsip_module *tsx_user,
pjsip_tx_data *tdata,
pjsip_transaction **p_tsx );
为tdata中传输的请求创建一个新的UAC事务通过设置TU为 tsx_user。 事务自动初始化并注册到事务表。注意调用这个函数后,应用通常会调用 pjsip_tsx_send_msg()来实际发送请求。


 pj_status_t pjsip_tsx_create_uas ( pjsip_module *tsx_user,
pjsip_rx_data *rdata,
pjsip_transaction **p_tsx );
为rdata中传输的请求创建一个新的UAS事务通过设置TU为 tsx_user。 事务自动初始化和注册到endpoint的事务表中。


 void pjsip_tsx_recv_msg( pjsip_transaction *tsx,
pjsip_rx_data *rdata );
UAS事务创建后,应用必须调用这个函数来传送初始请求消息,所以事务状态从NULL 变为 TRYING。 调用TU的on_tsx_state()。


 pj_status_t pjsip_tsx_send_msg( pjsip_transaction *tsx,
pjsip_tx_data *tdata );
通过事务来发送消息。如果tdata是NULL,最后的消息或者创建的时候指定的消息将会重发。如果函数返回PJ_SUCCESS,tdata引用计数器递减。


 pj_status_t pjsip_tsx_create_key( pj_pool_t *pool,
pj_str_t *out_key,
pjsip_role_e role,
const pjsip_method *method,
const pjsip_rx_data *rdata);
为收到的请求和响应消息创建一个事务key,考虑消息是否符合RFC3261或RFC2543.这个key可以用在endpoint的事务表中查找事务。
函数返回这个key放到out_key参数中。role参数用来查找UAC或者UAS,method参数包含消息的方法。


 pjsip_transaction* pjsip_tsx_layer_find_tsx( const pj_str_t *key,
pj_bool_t lock );
在事务表通过指定的key查找事务。如果lock参数为非零,函数将在返回这个事务之前锁住事务,所以其他的线程不能删除这个事务。调用者需要在结束使用事务的时候解锁它,通过调用 pj_mutex_unlock()。


 pj_status_t pjsip_tsx_terminate( pjsip_transaction *tsx,
int st_code );
用指定的状态码st_code来强制结束事务tsx。通常应用不需要调用这个函数,因为事务自己会终止和销毁通过它们的状态机。
这个函数用来比如当收发一个200 OK的INVITE响应,UA层想要去手动处理200 OK响应的重传。
事务会发送状态改变的事件(状态变为 PJSIP_TSX_STATE_TERMINATED),然后调用这个函数立即注销和销毁。


 pjsip_transaction* pjsip_rdata_get_tsx ( pjsip_rx_data *rdata );
从收到的消息中获取事务对象。
 8.2.2 Composite Functions
 pj_status_t pjsip_endpt_respond( pjsip_endpoint *endpt,
pjsip_module *tsx_user,
pjsip_rx_data *rdata,
int st_code,
const char *st_text,
const pjsip_hdr *hdr_list,
const pjsip_msg_body *body,
pjsip_transaction **p_tsx)
发送响应,通过为收到的请求创建新的UAS事务。


 pj_status_t pjsip_endpt_send_request(pjsip_endpoint *endpt,
pjsip_tx_data *tdata,
int timeout,
void *token,
void (*cb)(void*, pjsip_event*))
发送请求,通过使用UAC事务,并且当事务结束时调用可选的请求回调。
 8.3 Sending Statefull Responses
8.3.1 Usage Examples

 8.4 Sending Statefull Request
发送有状态请求的两种方式:
  #使用 pjsip_endpt_send_request()
   #手动使用事务
 8.4.1 Usage Examples

 8.5 Statefull Proxy Forwarding
8.5.1 Usage Examples

Statefull Forwarding
下面的代码展示了一个简单有状态的转发代理。代码创建UAS和UAC事务(每边一个),转发请求到UAC侧,转发所有UAC的回应到UAS侧。也处理UAC的传输超时和其他错误,并发送响应到UAS侧。
但是不处理收到UAC侧的CANCEL。
 

// This is our proxy module.
extern pjsip_module proxy_module;
static pj_bool_t on_rx_request( pjsip_rx_data *rdata )
{
    pjsip_account *acc;
    pjsip_uri *dest;
    pjsip_transaction *uas_tsx, *uac_tsx;
    pjsip_tx_data *tdata;
    pj_status_t status;
// Find the account specified in the request.
acc = ...
// Respond statelessly with 404/Not Found if account can not be found.
    if (!acc) {
    ...
        return PJ_TRUE;
    }
Page 61
PJSIP Developer’s Guide
// Set destination URI from account’s contact list that has highest priority.
dest = ...
// Create UAS transaction
    status = pjsip_endpt_create_uas_tsx( endpt, &proxy_module, rdata, &uas_tsx);
// Copy request to new tdata with new target URI.
    status = pjsip_endpt_create_request_fwd( endpt, rdata, dest, NULL, 0, &tdata);
// Create new UAC transaction.
    status = pjsip_endpt_create_uac_tsx( endpt, &proxy_module, tdata, &uac_tsx );
// “Associate” UAS and UAC transaction
    uac_tsx->mod_data[proxy_module.id] = (void*)uas_tsx;
    uas_tsx->mod_data[proxy_module.id] = (void*)uac_tsx;
// Forward message to UAC side
    status = pjsip_tsx_send_msg( uac_tsx, tdata );
    return PJ_TRUE;
}
static pj_bool_t on_rx_response( pjsip_rx_data *rdata )
{
pjsip_transaction *tsx;
pjsip_tx_data *tdata;
pj_status_t status;
// Get transaction object in rdata.
tsx = pjsip_rdata_get_tsx( rdata );
// Check that this transaction was created by the proxy
if (tsx->tsx_user == &proxy_module) {
// Get the peer UAC transaction.
pjsip_transaction *uas_tsx;
uas_tsx = (pjsip_transaction*) tsx->mod_data[proxy_module.id];
// Check top-most Via is ours
...
// Strip top-most Via
// Note that after this code, rdata->msg_info.via is invalid.
    pj_list_erase(rdata->msg_info.via);
// Code above is equal to:
// pjsip_hdr *via = pjsip_msg_find_hdr(rdata->msg, PJSIP_H_VIA);
// pj_list_erase(via);
// Copy the response msg.
    status = pjsip_endpt_create_response_fwd( endpt, rdata, 0, &tdata);
// Forward the response upstream.
    pjsip_tsx_send_msg( uas_tsx, tdata );
    return PJ_TRUE;
}
...
}// This is our proxy module.
    extern pjsip_module proxy_module;
static pj_bool_t on_rx_request( pjsip_rx_data *rdata )
{
    pjsip_account *acc;
    pjsip_uri *dest;
    pjsip_transaction *uas_tsx, *uac_tsx;
    pjsip_tx_data *tdata;
    pj_status_t status;
// Find the account specified in the request.
acc = ...
// Respond statelessly with 404/Not Found if account can not be found.
    if (!acc) {
    ...
        return PJ_TRUE;
    }
Page 61
PJSIP Developer’s Guide
// Set destination URI from account’s contact list that has highest priority.
dest = ...
// Create UAS transaction
    status = pjsip_endpt_create_uas_tsx( endpt, &proxy_module, rdata, &uas_tsx);
// Copy request to new tdata with new target URI.
    status = pjsip_endpt_create_request_fwd( endpt, rdata, dest, NULL, 0, &tdata);
// Create new UAC transaction.
    status = pjsip_endpt_create_uac_tsx( endpt, &proxy_module, tdata, &uac_tsx );
// “Associate” UAS and UAC transaction
    uac_tsx->mod_data[proxy_module.id] = (void*)uas_tsx;
    uas_tsx->mod_data[proxy_module.id] = (void*)uac_tsx;
// Forward message to UAC side
status = pjsip_tsx_send_msg( uac_tsx, tdata );
    return PJ_TRUE;
    }
static pj_bool_t on_rx_response( pjsip_rx_data *rdata )
{
    pjsip_transaction *tsx;
    pjsip_tx_data *tdata;
    pj_status_t status;
// Get transaction object in rdata.
    tsx = pjsip_rdata_get_tsx( rdata );
// Check that this transaction was created by the proxy
    if (tsx->tsx_user == &proxy_module) {
// Get the peer UAC transaction.
    pjsip_transaction *uas_tsx;
    uas_tsx = (pjsip_transaction*) tsx->mod_data[proxy_module.id];
// Check top-most Via is ours
...
// Strip top-most Via
// Note that after this code, rdata->msg_info.via is invalid.
    pj_list_erase(rdata->msg_info.via);
// Code above is equal to:
// pjsip_hdr *via = pjsip_msg_find_hdr(rdata->msg, PJSIP_H_VIA);
// pj_list_erase(via);
// Copy the response msg.
    status = pjsip_endpt_create_response_fwd( endpt, rdata, 0, &tdata);
// Forward the response upstream.
    pjsip_tsx_send_msg( uas_tsx, tdata );
    return PJ_TRUE;
    }
    ...
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值