翻譯-pjsip開發者指南(十)基本UA層

 Chapter 10:Basic User Agent Layer (UA)
 10.1 Basic Dialog Concept

基本的UA對話( UA dialog)提供了管理SIP對話和對話實例( dialog usage)的基本工具,如基本的對話狀態,會話計數器, Call-ID, From, To和 Contact頭, 事務中CSeq的序列,route-set。
基本的UA對話使用了哪種會話類型是不可知的(如 INVITE session, SUBSCRIBE/NOTIFY sessions, REFER/NOTIFY sessions),在一個單獨的對話中可以建立多種不同的會話類型。
一個PJSIP對話可以被認爲是一個被動的數據結構用來保存通用的對話屬性。不可以將對話和INVITE會話弄混。一個INVITE會話( 通常叫做dialog usage)是對話中的一個會話。在同一個對話中也可以有其他的會話/用法;它們共享通用的對話屬性(儘管每個對話裏只有一個INVITE會話)。
PJSIP對話不知道會話的狀態。它不知道INVITE會話是建立了還是斷開了。事實上,PJSIP對話也不知道對話中是哪種會話。對話以一個會話激活,如果會話計數器爲零,並且最後的事務也終止了,那麼對話就會被銷燬。
每個對話實例需要增加和減少會話計數器。
 10.1.1 Dialog Sessions
PJSIP對話框架中,對話中的會話用引用計數器來表示。這個引用計數器被對話實例模型來增加或減少,當相應對話中創建或銷燬一個會話時。
對話的會話被對話實例創建。一些對話中,一個對話實例可以創建不止一個會話(除了invite,一個對話中僅能創建一個invite會話)。
會話的具體表示在對話實例模型中定義。如上所述,基本的對話僅僅只關心當前活躍的會話數。
 10.1.2 Dialog Usages
對話實例是註冊到對話上用來接收對話事件的PJSIP模型。一個對話上可以註冊多種模型,因此對話可以有多個實例。每個對話實例模型都可以處理一個指定的會話。例如,訂閱的實例模型每次收到一個訂閱的請求後都會創建一個新的訂閱會話(並增加對話的會話計數器),當訂閱終止的時候就會減少會話計數器。
對話對於對話實例的處理和endpoint處理模型類似;對於每個on_rx_request()和on_rx_response()事件,對話從高優先級模型開始傳送至每個實例,直到其中有模型返回true,這時,對話就會停止事件的分發。on_tsx_state()通知將分發給所有對話實例。每個對話實例都需要過濾不屬於它的實例。
在最基本(即底層)的使用中,應用程序直接管理對話,並且它是對話框的唯一“實例”(或用戶)。這種情況下,應用負責管理對話中的會話,也就是處理所有的請求和響應並手動建立和關閉會話。
在後面的章節,我們會學習管理會話的高級API。這些高級API是作爲對話實例註冊到對話的PJSIP模型,他們會處理指定的每種會話的不同類型的SIP消息(一個invite實例模塊將處理invite、PRACK、CANCEL、ACK、BYE、UPDATE和INFO,訂閱實例模塊將處理 REFER, SUBSCRIBE, and NOTIFY)。這個高級API根據指定的會話提供高級的回調。
這一章節我們也只學習基本的低級別的對話實例。
 10.1.3 Dialog Set
每個對話都是一個對話集的成員。對話集有通用的本地tag定義(比如From tag)。通常一個對話集只有一個對話作爲成員。一個對話集有多個對話的唯一一種情況就是INVITE fork了,在這種情況下每個收到不同To tag的響應都會在相同的對話集中創建一個新的對話。
對話集在PJSIP中的定義是不透明的類型(比如void*)。一個對話結構體(pjsip_dialog)有一個成員dlg_set來定義所屬的對話集。應用程序可以使用鏈表API檢索對話的兄弟姐妹(在同一個對話框集中)。
有關對話集的更多信息,請參閱10.1.6Forking。
 10.1.4 Client Authentication
一個對話包含一個客戶端認證會話( pjsip_auth_clt_sess),用於下游服務器的對話中的請求認證。基本對話用適當的認證頭來初始化傳出的請求,如果可用的話。但對話實例必須處理認證質詢;比如基本對話不會收到401/407後自動的重發請求。
 10.1.5 Class Diagram
下面的圖表展示了UA層和基本對話框架。

 圖表展示了對話和實例之前的關係。
 在最基本/底層的場景中,應用模塊是對話的唯一實例。在更高級別下,一個高級的模塊(例如pjsip_invite_usage和pjsip_subscribe_usage)可以註冊到對話中作爲對話實例,然後應用從他們的實例中獲取事件而不是直接從對話中。
 這個圖標也展示了PJSIP的UA模型( pjsip_user_agent)。UA模型是所有對話的所有者,UA模型包含了一個當前活動的所有對話的哈希表。
 10.1.6 Forking
Handling Forking Condition
UA模型提供了一個應用可註冊的回調,當檢測到下游代理服務器forked reponse(分叉響應)。一個分叉響應被定義爲對話中的響應(可以是臨時的或2xx的響應),有不同於現有對話的to tag。當收到這樣的響應時,UA將會調用回調 on_dlg_forked(),以參數來傳遞收到的響應和原始對話(對話是由應用最初創建)。
    note:應用程序完全有責任處理分叉情況。
在收到一個分叉的臨時響應後,應用要:
   #忽略臨時響應(可能一直等收到最後的2xx響應)或者
   #創建一個新的對話(通過調用pjsip_dlg_fork ())。在這種情形下,之後從這個特定調用分支(call leg)收到的響應將發送到新的對話。
收到2xx的分叉響應後,應用要:
    #決定終止這個特定的分支。這種情況下,應用將會從響應中構造ACK請求,發送ACK,然後構造一個BYE事務,併發送到調用分支。應用在發送請求到事務或傳輸層之前,必須手動爲ACK和BYE請求構造Route 頭,根據響應中的Record-Route頭。
   #爲這個特定的分支創建對話(通過調用pjsip_dlg_fork ())。應用之後構造和發送ACK請求到調用分支來建立對話。對話建立之後,應用可以終止對話通過發送BYE請求。
應用不能忽略一個分叉的2xx響應。
 Creating Forked Dialog
應用通過調用pjsip_dlg_fork()函數來創建一個分叉對話。這個函數創建對話並有以下功能:
  #複製原始對話(包括認證客戶端會話)的所有屬性到一個新的對話中。
  #根據響應中的TO tag來分派不同的遠端tag值
  #註冊新的對話到UA對話集
  #如果原始對話有應用計時器,將會複製計時器並在新的對話中更新計時器。
注意這個函數不會從原始對話複製對話實例(比如模型)。
 note:函數pjsip_dlg_fork()不從原始對話中複製對話實例的原因是,每次使用通常都有特定於對話的數據,在不瞭解數據語義的情況下無法複製這些數據。
新對話註冊後,應用必須調用 pjsip_dlg_add_usage()來用新的對話來重新初測每一個對話實例。
新對話必須作爲函數的返回值返回。這樣UA會分發新消息到新對話,對話實例代表新對話來接收on_rx_response()通知。
 Using Timer to Handle Failed Forked Dialog
應用可以調用pjsip_dlg_start_app_timer()函數來管理應用指定的計時器。對於與對話關聯的計時器,這個計時器優於一般作用的計時器,因爲對話銷燬的時候計時器會自動刪除。
計時器對處理失敗的分叉對話很重要。一個分叉的early(早期or響鈴?)會話可能不會以最終響應結束,因爲分叉代理服務器收到2xx響應則不會轉發300-699。所以終止這些掛起的會話就要通過在對話中設置timer。
使用對話的應用程序timer來處理失敗的分叉早期對話的最好的方式是在收到對話集中的一個對話的2xx響應時就在另一個分叉對話中開始一個timer。當收到timer週期和2xx響應後,對話就要被終止。
 10.1.7 CSeq Sequencing
對話的本地cseq在請求發送後會更新(與創建請求相反)。當請求中有CSeq,這個值在請求發出去後更新。
對話中遠端的cseq在請求收到後更新。當對話中遠端的cseq爲空,第一個收到的請求將會設置對話的遠端cseq。對於之後的請求,如果對話收到的cseq比之前記錄下來的cseq低,那麼這個請求將以500響應來無狀態自動應答。當比記錄的大時,對話將自動更新遠端cseq。
note:此行爲遵循SIP規範RFC 3261第12.2.2節。
 10.1.8 Transactions
對話通常是有狀態的活動。當有到來的請求時會自動創建UAS事務,當發出請求時創建UAC。
唯一的一種情形,對話會無狀態的活動就是收到請求cseq比當前的小,這種會以500應答。
當對話(通過對話框API,用於UAS和UAC事務)創建事務時,事務的TU設置爲UA引用,對話引用將放到事務的mod_data的適當索引中。所以是UA模型的module ID。當事件或消息到達時,事務向UA模型報告事件,然後模型接收並傳送事件到對話。
 10.2 Basic UA API Reference
10.2.1 User Agent Module API

 typedef pjsip_module pjsip_user_agent;
  UA類型
pj_status_t pjsip_ua_init_module(pjsip_endpoint *endpt,const pjsip_ua_init_param *prm);

pjsip_user_agent* pjsip_ua_instance(void);
  獲取UA實例
pj_status_t pjsip_ua_destroy(void);
  銷燬UA模型
 10.2.2 Dialog Structure
 <pjsip/sip_dialog.h>中聲明瞭對話的結構體和API。下面的代碼描述了 pjsip_dialog。

 // This structure is used to describe dialog's participants, local and remote party.
struct pjsip_dlg_party
{
    pjsip_fromto_hdr *info; // From/To header, inc tag
    pj_uint32_t tag_hval; // Hashed value of the tag
    pjsip_contact_hdr *contact; // Contact header.
    pj_int32_t first_cseq; // First CSeq seen.
    pj_int32_t cseq; // Next sequence number.
};
// This structure describes basic dialog.
struct pjsip_dialog
{
    PJ_DECL_LIST_MEMBER(pjsip_dialog); // List node in dialog set.
// Static properties:
   char obj_name[PJ_MAX_OBJ_NAME]; // Log identification
    pj_pool_t *pool; // Dialog’s memory pool.
    pj_mutex_t *mutex; // Dialog's mutex.
    pjsip_user_agent *ua; // User agent instance.
    void *dlg_set; // The dialog set.
// Dialog session properties.
    pjsip_uri *target; // Current target.
    pjsip_dlg_party local; // Local party info.
    pjsip_dlg_party remote; // Remote party info.
    pjsip_role_e role; // Initial role.
    pj_bool_t secure; // Use secure transport?
    pjsip_cid_hdr *call_id; // Call-ID header.
    pjsip_route_hdr route_set; // Route set list.
    pjsip_auth_clt_sess auth_sess; // Client authentication session.
// Session Management
    int sess_count; // Session counter.
    int tsx_count; // Active transaction counter.
// Dialog usages
    unsigned usage_cnt; // Number of registered usages.
    pjsip_module *usage[PJSIP_MAX_MODULE]; // Usages, priority sorted
// Module specific data.
    void *mod_data[PJSIP_MAX_MODULE];
}


 10.2.3 Dialog Creation API
通過調用下面的函數可以創建一個對話。


 pj_status_t pjsip_dlg_create_uac( pjsip_user_agent *ua,
const pj_str_t *local_uri,
const pj_str_t *local_contact_uri,
const pj_str_t *remote_uri,
const pj_str_t *target,
pjsip_dialog **p_dlg);
創建一個新的對話,並在p_dlg參數中返回實例。創建對話後,應用程序可以通過調用 pjsip_dlg_add_usage()將模塊作爲對話實例添加。
注意初始化,會話計數器將初始化爲0。


 pj_status_t pjsip_dlg_create_uas( pjsip_user_agent *ua,
pjsip_rx_data *rdata,
const pj_str_t *contact,
pjsip_dialog **p_dlg);
從創建對話的傳入請求中找到的信息來初始化UAS對話(比如INVITE,REFER, or SUBSCRIBE),並設置本地聯繫人爲contact。如果contact沒有指定,本地聯繫人用請求的To頭中的URI來初始化。
如果請求由TO tag參數,對話的本地tag就用這個值來初始化。否則就調用一個全局唯一的id生成器來創建本地tag。
這個函數根據請求Record-Route頭來初始化對話的路由集,如果存在的話。
注意初始化的時候,對話的會話計數器將初始化爲零。


 pj_status_t pjsip_dlg_fork( pjsip_dialog *original_dlg,
pjsip_rx_data *rdata,
pjsip_dialog **new_dlg );
在收到的分叉響應的rdata中創建一個新的(分叉的)對話。這個函數從original_dlg(包括身份驗證會話)克隆一個新的對話,但是新的對話的remote tag是從響應的TO頭中複製的。返回時,new_dlg將已註冊到user_agent。應用只需要增加模型來作爲對話實例。
注意初始化的時候對話的計數器爲零。
 10.2.4 Dialog Termination
一旦會話計數器爲零,並且所有綁定的事務都終止了,對話通常會自動銷燬。可是,也有一些情況下對話實例需要提前銷燬對話,比如初始化失敗的時候。
 pjsip_dlg_terminate()函數用來提前銷燬對話。這個函數通常被對話實例調用。應用也應該使用適當的高級別的會話API,例如 pjsip_inv_terminate(),將會銷燬會話和對話。
 pj_status_t pjsip_dlg_terminate( pjsip_dialog *original_dlg );
銷燬對話並從UA模型的哈希表中註銷。只有會話計數器爲零纔會被調用。
 10.2.5 Dialog Session Management API
下面的函數用來管理對話的會話計數器。
 pj_status_t pjsip_dlg_inc_session( pjsip_dialog *dlg );
增加對話中會話的數量。注意初始的時候(創建後)對話已經設置會話計數器爲1。
 pj_status_t pjsip_dlg_dec_session( pjsip_dialog *dlg );
減少會話數量。一旦會話計數器爲零,且無事務綁定,對話就銷燬。注意這個函數調用的時候如果沒有事務綁定,將會立即銷燬對話。
 10.2.6 Dialog Usages API
下面的函數用來管理對話實例。
 pj_status_t pjsip_dlg_add_usage( pjsip_dialog *dlg,
pjsip_module *module,
void *mod_data );
增加一個模型作爲對話實例,並可選的設置模型特定的數據。


 pj_status_t pjsip_dlg_set_mod_data( pjsip_dialog *dlg,
int module_id,
void *data );
附加模型指定數據到對話。


 void* pjsip_dlg_get_mod_data( pjsip_dialog *dlg,
int module_id);
獲取之前附加到對話中的模型指定數據。應用可以通過 dlg->mod_data[module_id]直接取值。


 10.2.7 Dialog Request and Response API
pj_status_t pjsip_dlg_create_request( pjsip_dialog *dlg,
const pjsip_method *method,
int cseq,
pjsip_tx_data **tdata)
創建一個基本/通用的請求通過指定的method和可選的指定cseq。cseq使用01來自動的爲請求放入下一個cseq。否則對於其他請求,比如CANCEL或者ACK,應用必須將CSeq作爲參數放到原始的INVITE請求中。這個函數還可以在適當的地方放置Contact。


 pj_status_t pjsip_dlg_send_request ( pjsip_dialog *dlg,
pjsip_tx_data *tdata,
pjsip_transaction **p_tsx );
向遠端點發送請求消息。如果不是ACK的請求,對話有狀態的發送請求消息,通過創建UAC事務以及用這個事務來發送請求。當請求不是ACK和CANCEL時,對話將增加本地的cseq並根據對話的cseq來更新請求中的cseq。
注意 對話實例的on_tsx_state回調調用可能在函數返回之前。
如果p_tsx不是NULL,這個參數將會和用於發送請求的事務實例一起設置。
無論操作的狀態如何,此函數都會減少傳輸數據的引用計數器。


 pj_status_t pjsip_dlg_create_response( pjsip_dialog *dlg,
pjsip_rx_data *rdata,
int st_code,
const pj_str_t *st_text,
pjsip_tx_data **tdata);
爲rdata中狀態爲st_code且可選狀態描述爲st_text的請求創建響應消息。這個函數和endpoint的API pjsip_endpt_create_response() 不同的是對話的函數在響應中適當的增加Contact頭和Record-Route頭。


 pj_status_t pjsip_dlg_modify_response( pjsip_dialog *dlg,
pjsip_tx_data *tdata,
int st_code,
const pj_str_t *st_text);
用其他的狀態碼來修改之前發送的響應。適當時增加Contact頭。


 pj_status_t pjsip_dlg_send_response( pjsip_dialog *dlg,
pjsip_transaction *tsx,
pjsip_tx_data *tdata);
有狀態的發送響應消息。這個事務實例必須是在 on_rx_request()回調中報告過得。
無論操作的狀態如何,此函數都會減少傳輸數據的引用計數器。


 10.2.8 Dialog Auxiliary API
pj_status_t pjsip_dlg_set_route_set( pjsip_dialog *dlg,
const pjsip_route_hdr *route_set );
設置對話的初始路由集爲route_set列表。在任何請求發出去前,這個函數只能被UAC對話調用。對話建立之後,路由集就不能改變了。
對於UAS對話,路由集在 pjsip_dlg_create_uas()中初始化,這個值來自收到請求中的Record-Route中。
route_set參數是Route頭的標準列表(帶sentinel)。


 pj_status_t pjsip_dlg_start_app_timer( pjsip_dialog *dlg,
int app_id,
const pj_time_val *interval,
void (*cb)(pjsip_dialog*,int));
用對話來開啓應用計時器,並指定應用的id app_id ,回調cb。應用只能爲每個對話設置一個應用計時器。這個計時器比對話指定的計時器更加有效,因爲它可以在對話銷燬時自動銷燬。注意這個計時器還可以在分叉對話中複製。


 pj_status_t pjsip_dlg_stop_app_timer( pjsip_dialog *dlg );
如果開啓,則停止應用指定的timer。


 pjsip_dialog* pjsip_rdata_get_dlg( pjsip_rx_data *rdata );
在收到的rdata中獲取對話實例。如果收到的消息和已存在的對話匹配,UA必須將匹配的對話實例放到rdata中,否則這個函數將消息在未匹配到任何存在的對話後返回NULL。


 pjsip_dialog* pjsip_tsx_get_dlg( pjsip_transaction *tsx );
在指定的事務中獲取對話實例。


 10.3 Examples
10.3.1 Invite UAS Dialog

下面的例子使用了基本/低級別的對話API來處理傳入的對話。這些例子展示了:
   #建立和初始化傳入對話
   #創建UAS事務來處理傳入的INVITE請求併發送1xx響應
   #發送2xx的可靠響應
   #處理收到的ACK
通常來說,許多的錯誤處理都被簡單的省略了。真正的應用應該隨時準備處理各種情形下的錯誤。
 Creating Initial Invite Dialog
在這個例子中我們將學習怎樣爲傳入的INVITE請求創建一個對話並且使用180/Ringing來進行臨時響應。

 pj_bool_t on_rx_request(pjsip_rx_data *rdata)
{
    if (rdata->msg->line.request.method.id == PJSIP_INVITE_METHOD &&
                                pjsip_rdata_get_dlg(rdata) == NULL)
{
// Process incoming INVITE!
    pjsip_dialog *dlg;
    pjsip_transaction *tsx;
    pjsip_tx_data *tdata;
    struct app_dialog *app_dlg;
// Create, initialize, and register new dialog for incoming INVITE.
// This also implicitly create UAS transaction for rdata.
    status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata, NULL, &dlg);
// Register application as the only dialog usage
    status = pjsip_dlg_add_usage( dlg, &app_module, NULL );
// Increment session.
    pjsip_dlg_inc_session(dlg);
// Create 180/Ringing response
    status = pjsip_dlg_create_response( dlg, rdata, 180, NULL /*Ringing*/, &tdata);
// Send 180 response statefully. A transaction will be created in &tsx.
    status = pjsip_dlg_send_response( dlg, pjsip_rdata_get_tsx(rdata), tdata);
// As in real application, normally we will send 200/OK later,
// when the user press the “Answer” button. In this example, we’ll send
// 200/OK in answer_dlg() function which will be explained later. In order
// to do so, we must “save” the INVITE transaction. We do this by putting
// the transaction instance in dialog’s module data at index application
// module’s ID.
//
    dlg->mod_data[app_module.id] = pjsip_rdata_get_tsx(rdata);
// Done processing INVITE request
    return PJ_TRUE;
}
// Process other requests
...
}


 Answering Dialog
這個例子我們學習怎麼發送200/OK來創建對話。

 static void answer_dlg(pjsip_dlg *dlg)
{
    pjsip_transaction *invite_tsx;
    pjsip_tx_data *tdata;
    invite_tsx = dlg->mod_data[app_module.id];
// Modify previously sent (provisional) response to 200/OK response.
// The previously sent message is found in tsx->last_tx.
    tdata = invite_tsx->last_tx;
    status = pjsip_dlg_modify_response( dlg, tdata, 200, NULL /*OK*/ );
// You may modify the response before it’s sent
// (e.g. add msg body etc).
...
// Send the 200 response using previous transaction.
// Transaction will take care of the retransmission.
    status = pjsip_dlg_send_response( dlg, invite_tsx, tdata);
// We don’t need to keep pending invite tsx anymore.
    dlg->mod_data[app_module.id] = NULL;
}
 Processing CANCEL Request
這個例子學習處理收到的CALCEL請求。
 pj_bool_t on_rx_request(pjsip_rx_data *rdata)
{
...
    if (rdata->msg->line.request.method.id == PJSIP_CANCEL_METHOD)
    {
// See if we have pending INVITE transaction.
        pjsip_dialog *dlg;
        pjsip_transaction *invite_tsx;
// All requests within a dialog will have the dialog instance
// recorded in rdata.
        dlg = pjsip_rdata_get_dlg(rdata);
    if (!dlg) {
// Not associated with any dialog. Respond statelessly with 481.
        status = pjsip_endpt_respond_stateless( endpt, rdata, 481, NULL, NULL,
        NULL, NULL);
        return PJ_TRUE;
    }
    invite_tsx = dlg->mod_data[app_module.id];
    if (invite_tsx) {
        pjsip_tx_data *tdata;
// Transaction found. Respond CANCEL (statefully!) with 200 regardless
// whether the INVITE transaction has completed or not.
        status = pjsip_dlg_respond( dlg, rdata, 200, NULL /*OK*/);
// Respond the INVITE transaction with 487/Request Terminated
// only when INVITE transaction has not send final response.
    if (invite_tsx->status_code < 200) {
        tdata = invite_tsx->last_tx;
         status = pjsip_dlg_modify_response( dlg, tdata, 487, NULL );
// Send the 487 response.
        status = pjsip_dlg_send_response( dlg, invite_tsx, tdata);
        dlg->mod_data[app_module.id] = NULL;
// Decrement session!
        pjsip_dlg_dec_session(dlg);
        }
    } else {
// Transaction not found, respond CANCEL with 481 (statefully!)
        status = pjsip_dlg_respond ( dlg, rdata, 481, NULL );
        }
// Done processing CANCEL request
        return PJ_TRUE;
    }
// Process other requests
...
}


 Processing ACK Request
學習處理收到的ACK請求

 pj_bool_t on_rx_request(pjsip_rx_data *rdata)
{
...
    if (rdata->msg->line.request.method.id == PJSIP_ACK_METHOD &&
        pjsip_rdata_get_dlg(rdata) != NULL)
    {
// Process the ACK request
    pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
...
    return PJ_TRUE;
    }
...
}


 10.3.2 Outgoing Invite Dialog
下面的例子展示了怎麼使用傳出INVITE對話。
 Creating Initial Dialog

 static pj_status_t make_call( const pj_str_t *local_info, const pj_str_t *remote_info)
{
    pjsip_dialog *dlg;
    pjsip_tx_data *tdata;
// Create and initialize dialog.
    status = pjsip_dlg_create_uac( user_agent, local_info, local_info,
    remote_info, remote_info, &dlg );
// Register application as the only dialog usage.
    status = pjsip_dlg_add_usage( dlg, &app_module, NULL);
 // Add session.
    pjsip_dlg_inc_session(dlg);
// Send initial INVITE.
    status = pjsip_dlg_create_request( dlg, &pjsip_invite_method, -1, &tdata);
// Modify the INVITE (e.g. add message body etc.. )
...
// Send the INVITE request.
    status = pjsip_dlg_send_request( dlg, tdata, NULL);
// Done.
// Further responses will be received in on_rx_response.
    return status;
}
 Receiving Response
 static pj_bool_t on_rx_response( pjsip_rx_data *rdata )
{
    pjsip_dialog *dlg;
    dlg = pjsip_rdata_get_dlg( rdata );
    if (dlg != NULL ) {
        pjsip_transaction *tsx = pjsip_rdata_get_tsx( rdata );
        if ( tsx != NULL && tsx->method.id == PJSIP_INVITE_METHOD) {
        if (tsx->status_code < 200) {
        PJ_LOG(3,(“app”, “Received provisional response %d”, tsx->status_code));
    } else if (tsx->status_code >= 300) {
    PJ_LOG(3,(“app”, “Dialog failed with status %d”, tsx->status_code));
    pjsip_dlg_dec_session(dlg);
// ACK for non-2xx final response is sent by transaction.
    } else {
        PJ_LOG(3,(“app”, “Received OK response %d!”, tsx->status_code));
        send_ack( dlg, rdata );
            }
    }
    else if (tsx == NULL && rdata->msg_info.cseq->method.id == PJSIP_INVITE_METHOD
            && rdata->msg_info.msg->line.status.code/100 == 2)
        {
// Process 200/OK response retransmission.
        send_ack( dlg, rdata );
        }
        return PJ_TRUE;
    }
    else
// Process other responses not belonging to any dialog
...
}
 Sending ACK
 static void send_ack( pjsip_dialog *dlg, pjsip_rx_data *rdata )
{
    pjsip_tx_data *tdata;
// Create ACK request
    status = pjsip_dlg_create_request( dlg, &pjsip_ack_method,
    rdata->msg_info.cseq->cseq, &tdata );
// Add message body
 ...
// Send the request.
    status = pjsip_dlg_send_request ( dlg, tdata, NULL );
}


 10.3.3 Terminating Dialog
下面的例子展示了終止INVITE對話的一種方式,比如發送BYE

 static void send_bye( pjsip_dialog *dlg )
{
    pjsip_tx_data *tdata;
// Create BYE request
    status = pjsip_dlg_create_request( dlg, &pjsip_bye_method, -1, &tdata );
// Send the request.
    status = pjsip_dlg_send_request ( dlg, tdata, NULL );
// Decrement session.
// Dialog will be destroyed once the BYE transaction terminates.
    pjsip_dlg_dec_session(dlg);
}

 

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