翻譯-pjsip開發者指南(七)發送消息

 Chapter 7:Sending Messages
SIP應用的核心操作就是收發消息。 跟第一章描述的一樣,模塊調用on_rx_request()和on_rx_response()來處理傳入的消息。

本章介紹發送傳出消息的基本方式,即不使用transaction和dialog。
下一個章節介紹 transaction是如何處理有狀態的請求(包括傳入和傳出的請求)。
 7.1 Sending Messages Overview
7.1.1 Creating Messages

PJSIP提供了多種API來創建和響應消息。下面是創建消息的一些方式:
     #對於響應消息,最開始使用 pjsip_endpt_create_response()函數
     #對於請求消息,可以使用函數 pjsip_endpt_create_request(),pjsip_endpt_create_request_from_hdr(),            pjsip_endpt_create_ack(), pjsip_endpt_create_cancel()
     #代理調用pjsip_endpt_create_request_fwd()和pjsip_endpt_create_response_fwd () ,可以根據要轉發的傳入消息,創建請求和響應消息。
     # 或者你可以調用pjsip_endpt_create_tdata()建立傳輸緩衝區來創建請求和響應消息,調用 pjsip_msg_create()創建消息,調用pjsip_msg_add_hdr()或pjsip_msg_insert_first_hdr ()增加消息的頭字段,或者消息體。
     #更高層的模塊(dialog layer)可以提供更多的方式來創建消息。這個描述在各個模塊的文檔中。


所有創建API的消息(除了低級別的 pjsip_endpt_create_tdata())都設置transmit buffer(pjsip_tx_data)的計數器爲1,也就是說有時候應用程序(或堆棧)必須減少計數器來銷燬transmit buffer。
所有發送API的消息必須減少transmit buffer的計數器,也就是說只要應用程序不對transmit buffer的引用計數器做任何事情,buffder就必須在發送後銷燬。

 7.1.2 Sending Messages
發送消息的最基本方式是調用pjsip_endpt_acquire_transport()和pjsip_transport_send ()。然而,要使其工作,必須知道發送消息的目標地址(即sockaddr,而不僅僅是hostname)。有下面幾個步驟來獲取消息和已存在的socket地址(決定使用哪種地址,RFC3263),這個函數級別太低了因此不能直接使用。
發送消息的核心API是pjsip_endpt_send_request_stateless()和pjsip_endpt_send_response ()。從傳輸層的自動處理上來說是很有用的兩個函數,也是上層模塊(transaction)使用的最基本的組成部分。
 pjsip_endpt_send_request_stateless()用來發送請求消息,有以下步驟:
     #根據Request-URI和Route header裏的參數判斷要連接的地址
    #用RFC3263(SIP定位服務器)的步驟來解析目的服務器
    #選擇和建立連接服務器所需的傳輸
    #修改傳入的via header來反映當前所用的傳輸
    #如果服務器不能使用當前的傳輸來連接,轉移錯誤到下一個服務器/傳輸

pjsip_endpt_send_response ()函數用來發送響應消息,有以下步驟:
    #按照RFC3261的18.2.2小節的步驟來選擇使用何種傳輸,向哪個地址發送響應
    #另外rport參數符合RFC3581
    #使用選擇的傳輸發送響應
    #如果使用選擇的傳輸發送響應失敗,轉移失敗到下一個地址,必要時根據RFC3263解析服務器
因爲消息可以異步發出(比如TCP連接後),這兩種函數都提供通知應用程序傳輸狀態的回調。這個回調還通知應用將發生的失敗,應用就有機會去避免這種行爲。


 7.2 Function Reference
7.2.1 Sending Response

 Base Functions
pj_status_t pjsip_endpt_create_response( pjsip_endpoint *endpt,
        pjsip_rx_data *rdata,
        int st_code,
        const char *st_text,
        pjsip_tx_data **tdata);
使用狀態碼st_code和狀態文本st_textrdata中的請求創建標準響應消息。如果st_text是NULL,將使用默認的狀態文本。

pj_status_t pjsip_get_response_addr( pj_pool_t *pool,
        pjsip_rx_data *rdata,
        pjsip_response_addr *res_addr);
根據 rdate中收到的請求來判斷髮送響應的地址(和傳輸)。該函數遵循RFC 3261和RFC 3581中18.2.2節中的規範,用於計算目標地址和傳輸。發送響應的地址和傳輸信息將在res_addr中返回。

pj_status_t pjsip_endpt_send_response( pjsip_endpoint *endpt,
        pjsip_response_addr *res_addr,
        pjsip_tx_data *response,
        void *token,
        void (*cb)( pjsip_send_state*,
            pj_ssize_t sent,
            pj_bool_t *cont));
使用res_addr中的目標地址和傳輸來無狀態的發送response的響應。響應地址信息(res_addr)通常調用 pjsip_get_response_addr()來初始化。
調用回調的cb會報告定義的傳輸狀態,還有存在pjsip_send_state中的其他信息(包括原始token)。如果消息成功發送,回調的sent參數爲一個非零正值。如果失敗了,sent爲負值,錯誤碼爲正值(status = -sent)。退出回調的時候,可以通過將該參數置爲零來避免應用程序嘗試其他地址。
如果應用沒有指定cb,當使用選擇的傳輸分發消息失敗了,也不再嘗試下一個地址。消息是有效的,或者非零錯誤碼返回PJ_SUCCESS。但是,即使返回了PJ_SUCCESS,也不能保證響應成功發出去了。
注意回調可能在函數返回之前調用。


 Composite Functions
pj_status_t pjsip_endpt_respond_stateless( pjsip_endpoint *endpt,
        pjsip_rx_data *rdata,
        int st_code,
        const char *st_text,
        const pjsip_hdr *hdr_list,
        const pjsip_msg_body *body);
這個函數爲收到的請求創建和發送響應。除此之外,調用者可以指定消息體和附加的header放到響應消息的hdr_listbody參數裏。如果沒有附加的header和body發送,參數爲NULL。
如果響應成功創建併發送到傳輸層或者返回了非零錯誤碼,則返回PJ_SUCCESS,。但即使返回了PJ_SUCCESS,也不確保響應被成功發送。
                                                                                       
 7.2.2 Sending Request
pj_status_t pjsip_endpt_create_tdata( pjsip_endpoint *endpt,
        pjsip_tx_data **tdata);
創建一個新的空的傳輸數據。

pj_status_t pjsip_endpt_create_request( pjsip_endpoint *endpt,
        const pjsip_method *method
        const pj_str_t *target,
        const pj_str_t *from,
        const pj_str_t *to,
        const pj_str_t *contact,
        const pj_str_t *call_id,
        int cseq,
        const pj_str_t *text,
        pjsip_tx_data **p_tdata);
爲指定的target URI,fromtocontact用指定的method創建新的請求消息。call_idcseq是可選的。如果指定了text,要增加text/plain 消息體。請求消息的引用計數器初始化爲1,在p_tdata中返給發送方。

pj_status_t pjsip_endpt_create_request_from_hdr(pjsip_endpoint *endpt,
        const pjsip_method *method,
        const pjsip_uri *target,
        const pjsip_from_hdr *from,
        const pjsip_to_hdr *to,
        const pjsip_contact_hdr *ch,
        const pjsip_cid_hdr *call_id,
        int cseq,
        const pj_str_t *text,
        pjsip_tx_data **p_tdata);
從指定的參數中淺克隆header然後創建一個新的請求header。

pj_status_t pjsip_endpt_create_ack( pjsip_endpoint *endpt,
        const pjsip_tx_data *tdata,
        const pjsip_rx_data *rdata,
        pjsip_tx_data **ack );
根據收到的響應rdata,從原始請求tdata中創建ACK請求消息。事務未成功收到INVITE的響應使用此函數。成功的INVITE響應的ACK請求由dialog的函數創建(create request)。

pj_status_t pjsip_endpt_create_cancel( pjsip_endpoint *endpt,
        const pjsip_tx_data *tdata,
        pjsip_tx_data **p_tdata);
根據之前發送的tdata中的請求創建CANCEL請求,在p_tdata中創建一個新的傳輸數據。

pj_status_t pjsip_endpt_send_request_stateless(pjsip_endpoint *endpt,
        pjsip_tx_data *tdata,
        void *token,
        void (*cb)(pjsip_send_state*,
            pj_ssize_t sent,
            pj_bool_t *cont));
發送tdata無狀態請求。這個函數關注的是根據消息中的信息判斷目的地和所使用的傳輸以及request line中的URI和Route header。這個函數有以下幾個步驟:
    #根據 Request-URI和 Route headers  (pjsip_get_request_addr())確定連接的host
    #解析目的host (pjsip_endpt_resolve())
    #獲取所用的傳輸 (pjsip_endpt_acquire_transport())
    #發送消息 (pjsip_transport_send())
    #必要時將失敗轉入下一個地址或傳輸
調用回調cb將報告定義的傳輸狀態,以及其他放到 pjsip_send_state中的信息(包括原始的token)。如果消息成功發送,回調的sent參數爲一個非零正值。如果失敗,sent是負值,錯誤碼是這個值的正數(status= -sent)。如果cont參數的值是非零,就認爲這函數可以嘗試其他的地址發送消息(fail-over)。通過在退出回調的時候設置這個參數爲零使應用不再嘗試其他的地址。
如果應用沒有指定回調的cb,那麼所選傳輸分發消息失敗也不會再嘗試下一個的地址。
消息是有效的或返回了非零錯誤碼則返回PJ_SUCCESS,不過即使是返回PJ_SUCCESS也不確保請求成功發出去了。

注意回調可能在函數返回之前被調用。

 7.2.3 Stateless Proxy Forwarding(無狀態代理轉發)
代理可以選擇轉發一個無狀態的請求。如果要這麼做必須嚴格按照在RFC 3261的第16.11節Stateless Proxy中的規則。
pj_status_t pjsip_endpt_create_request_fwd( pjsip_endpoint *endpt,
        pjsip_rx_data *rdata,
        const pjsip_uri *uri,
        const pj_str_t *branch,
        unsigned options,
        pjsip_tx_data **tdata);
創建新請求消息向上轉發給uri。新請求深/全克隆收到的rdata中的請求,除非options中有其他的拷貝機制。branch參數如果不爲NULL,用作Via header中的 branch-param。如果是NULL,將使用唯一的branch參數。

pj_status_t pjsip_endpt_create_response_fwd( pjsip_endpoint *endpt,
        pjsip_rx_data *rdata,
        unsigned options,
        pjsip_tx_data **tdata);
根據rdata的響應消息,代理創建並轉發一個下行的新響應消息來轉發。注意函數按照響應原樣克隆,也就是說,不檢查響應的有效性,也不會移除Via header的最頂部。這個函數會深/全克隆除了非options指定了的其他拷貝機制之外的響應。

 pj_str_t pjsip_calculate_branch_id( pjsip_rx_data *rdata );
根據收到的請求消息,創建全局唯一的branch參數。這個函數確保了之後如果重發消息依然具有同樣的branch id。
這個函數還可以用在循環檢查程序中。如果代理的相同請求返回相同URL,就給它們賦予相同的branch id。
注意返回的字符串從rdate的池中分配。


 7.3 Examples
7.3.1 Sending Responses

發送未找到賬戶的無狀態響應

 static pj_bool_t on_rx_request(pjsip_rx_data *rdata )
{
    pjsip_account *acc;
    pj_status_t status;
// Find account referred to in the request.
    acc = ...
// Respond statelessly if account can not be found.
    if (!acc) {
        status = pjsip_endpt_respond_stateless( endpt, rdata, 404, NULL /*Not Found*/,
        NULL, NULL, NULL);
    return PJ_TRUE;
}
// Process the account
...
    return PJ_TRUE;
}


處理認證失敗的無狀態響應

     另一種發送無狀態響應的方式
   

static pj_bool_t on_rx_request( pjsip_rx_data *rdata )
{
    pjsip_account *acc;
// Lookup acc.
    acc = ...;
// Check authorization and handle failure statelessly
    if (!pjsip_auth_authorize( acc, rdata->msg )) {
        pjsip_proxy_authenticate_hdr *auth_hdr;
    status = pjsip_endpt_create_response( endpt, rdata,
            407, NULL /* Proxy Auth Required */,
            &tdata);
// Add Proxy-Authenticate header.
    status = pjsip_auth_create_challenge( tdata->pool, ..., &auth_hdr);
    pjsip_msg_add_hdr( &tdata->msg, auth_hdr );
// Send response statelessly
    status = pjsip_endpt_send_response( endpt, tdata, NULL);
    return PJ_TRUE;
}
// Authorization success. Proceed to next stage..
    ...
    return PJ_TRUE;
}                                                       

                                                  

無狀態重定向
 

static pj_bool_t on_rx_request( pjsip_rx_data *rdata )
{
    pjsip_account *acc;
    pj_status_t status;
// Find the account referred to in the request.
    acc = ...
if (!acc) {
    status = pjsip_endpt_respond_stateless( endpt, rdata, 404, NULL /*Not Found*/,
    NULL, NULL, NULL );
    return PJ_TRUE;
}
//
// Send 301/Redirect message, specifying the Contact details in the response
//
    status = pjsip_endpt_respond_stateless( endpt, rdata,
            301, NULL /*Moved Temporarily*/,
             &acc->contact_list, NULL, NULL);
    return PJ_TRUE;
}

 7.3.2 Sending Requests
發送無狀態請求
 

void my_send_request()
{
    pj_status_t status;
    pjsip_tx_data *tdata;
// Create the request.
// Actually the function takes pj_str_t* argument instead of char*.
    status = pjsip_endpt_create_request( endpt, // endpoint
        method, // method
    “sip:[email protected]”, // target URI
    “sip:[email protected]”, // From:
    “sip:[email protected]”, // To:
    “sip:[email protected]”, // Contact:
    NULL, // Call-Id
    0, // CSeq#
    NULL, // body
    &tdata ); // output
// You may modify the message before sending it.
...
// Send the request statelessly (for whatever reason...)
    status = pjsip_endpt_send_request_stateless( endpt, tdata, NULL);
}

 7.3.3 Stateless Forwarding
無狀態的轉發
 

static pj_bool_t on_rx_request( pjsip_rx_data *rdata )
{
    pjsip_account *acc;
    pjsip_tx_data *tdata;
    pj_str_t branch_id;
    pj_status_t status;
// Find the account specified in the request.
    acc = ...
// Generate unique branch ID for the request.
    branch_id = pjsip_calculate_branch_id( rdata );
// Create new request to be forwarded to new destination.
    status = pjsip_endpt_create_request_fwd( endpt, rdata, dest, &branch_id, 0,
                &tdata );
// The new request is good to send, but you may modify it if necessary
// (e.g. adding/replacing/removing headers, etc.)
...
// Send the request downstream
    status = pjsip_endpt_send_request_stateless( endpt, tdata, NULL );
    return PJ_TRUE;
}
//
// Forward response upstream
//
 static pj_bool_t on_rx_response(pjsip_rx_data *rdata)
{
    pjsip_tx_data *tdata;
    pj_status_t status;
// Check that topmost Via is ours, strip top-most Via, etc.
...
// Create new tdata for the response.
    status = pjsip_endpt_create_response_fwd( endpt, rdata, 0, &tdata );
// Send the response upstream
    status = pjsip_endpt_send_response( endpt, tdata, NULL);
    return PJ_TRUE;
}

 

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