sip信令超時時間調整

Pjsip在使用過程中,如果網路環境不好,sip信令在互動過程中,會出現超時的情況,此時,sip信令會重複傳送信令,直到收到信令的反饋或者sip會話超時退出通話。

針對上訴的情況,需要修改sip信令的超時時間,以便適應複雜的網路情況。

sip信令的傳輸程式碼主要在:pjsip/src/pjsip/sip_transaction.c中。

sip_transaction.c實現了一個狀態機,根據sip信令互動的不同的信令,切換狀態機。

UAC (呼叫方)狀態機轉換如下:

剛開始呼叫時,sip_transaction的狀態機處於tsx_on_state_null狀態,撥打電話發出INVITE信令後,狀態機轉為Calling狀態,處理函式:tsx_on_state_calling,應用可以收到Calling的狀態通知。

之後會收到被叫方發來的100 try信令,狀態機轉為Processing狀態,處理函式:tsx_on_state_proceeding_uac

在收到180 ringing的信令後,還是在tsx_on_state_proceeding_uac中處理,應用會收到Early的狀態通知。

如果被叫方接聽了該呼叫,會收到被叫方的200 OK信令,狀態機會轉為Terminated狀態,處理函式:tsx_on_state_terminated,應用會收到Connecting的狀態通知,之後會通過timeout方式,把狀態轉為Destroyed。Call的狀態也轉為Confirmed。

Bye信令狀態轉換:

發出Bye信令時,狀態機轉為Calling狀態(tsx_on_state_calling),在收到200OK信令時,轉為Completed狀態(tsx_on_state_completed_uac),應用會收到Disconnected的狀態通知

,之後通過timeout的方式,狀態轉為Terminated和Destroyed

UAS(被叫方)狀態機轉換如下:

Bye狀態轉換:

被叫方的轉換和呼叫方類似。

sip的超時時間修改:

INVITE傳送超時時間修改,在函式:tsx_on_state_null中

sip信令一般使用UDP進行傳輸,屬於不可靠的傳輸,所以在傳送完INVITE信令後,會加入超時機制。超時時間為t1_timer_val時間間隔。

        /* Start Timer A (or timer E) for retransmission only if unreliable 
* transport is being used.
*/
if (!tsx->is_reliable)  {
tsx->retransmit_count = 0;
if (tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) {
tsx->transport_flag |= TSX_HAS_PENDING_RESCHED;
} else {
tsx_schedule_timer(tsx, &tsx->retransmit_timer,
&t1_timer_val, RETRANSMIT_TIMER);
}
}
/* Move state. */
tsx_set_state( tsx, PJSIP_TSX_STATE_CALLING, 
PJSIP_EVENT_TX_MSG, tdata, 0);

t1_timer_val的定義在,檔案的開頭:

/* Timer timeout value constants */
static pj_time_val t1_timer_val = { PJSIP_T1_TIMEOUT/1000, 
PJSIP_T1_TIMEOUT%1000 };
/** Transaction T1 timeout value. */
#if !defined(PJSIP_T1_TIMEOUT)
#  define PJSIP_T1_TIMEOUT      500
#endif

所以INVITE傳送後,首次的超時時間為500ms,第二次超時在tsx_on_state_calling函式中

    if (event->type == PJSIP_EVENT_TIMER && 
event->body.timer.entry == &tsx->retransmit_timer) 
{
pj_status_t status;
/* Retransmit the request. */
status = tsx_retransmit( tsx, 1 );
if (status != PJ_SUCCESS) {
return status;
}
}
 /*
* Retransmit last message sent.
*/
static pj_status_t tsx_retransmit( pjsip_transaction *tsx, int resched)
{
pj_status_t status;
if (resched && pj_timer_entry_running(&tsx->retransmit_timer)) {
/* We've been asked to reschedule but the timer is already rerunning.
* This can only happen in a race condition where, between removing
* this retransmit timer from the heap and actually scheduling it,
* another thread has got in and rescheduled the timer itself.  In
* this scenario, the transmission has already happened and so we
* should just quit out immediately, without either resending the
* message or restarting the timer.
*/
return PJ_SUCCESS;
}
PJ_ASSERT_RETURN(tsx->last_tx!=NULL, PJ_EBUG);
PJ_LOG(5,(tsx->obj_name, "Retransmiting %s, count=%d, restart?=%d", 
pjsip_tx_data_get_info(tsx->last_tx), 
tsx->retransmit_count, resched));
tsx->retransmit_count;
/* Restart timer T1 first before sending the message to ensure that
* retransmission timer is not engaged when loop transport is used.
*/
if (resched) {
pj_assert(tsx->state != PJSIP_TSX_STATE_CONFIRMED);
if (tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) {
tsx->transport_flag |= TSX_HAS_PENDING_RESCHED;
} else {
tsx_resched_retransmission(tsx);
}
}
status = tsx_send_msg( tsx, tsx->last_tx);
if (status != PJ_SUCCESS) {
return status;
}
return PJ_SUCCESS;
}
/*
* Retransmit last message sent.
*/
static void tsx_resched_retransmission( pjsip_transaction *tsx )
{
pj_uint32_t msec_time;
pj_assert((tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) == 0);
if (tsx->role==PJSIP_ROLE_UAC && tsx->status_code >= 100)
msec_time = pjsip_cfg()->tsx.t2;
else
msec_time = (1 << (tsx->retransmit_count)) * pjsip_cfg()->tsx.t1;
......
}

第二次的超時:msec_time = (1 << (tsx->retransmit_count)) * pjsip_cfg()->tsx.t1 ;就是第一次的超時*2,在1秒鐘,第三次,在*2,知道timeout觸發。

INVITE週期的timeout時間修改,在tsx_on_state_null函式中,只要修改timeout_timer_val的值即可:

            lock_timer(tsx);
tsx_cancel_timer( tsx, &tsx->timeout_timer );
tsx_schedule_timer( tsx, &tsx->timeout_timer, &timeout_timer_val,
TIMEOUT_TIMER);
unlock_timer(tsx);