http://blog.csdn.net/perfectpdl/article/details/6442847

1.tinysip 介紹 :


兼容性 : SIP(RFC 3261) 以及 3GPPIMS/LTE (TS 24.229) implementation



依賴 tinySAK,tinyNET, tinySDP, tinyMEDIA, tinyHTTP and tinyIPSec.



2.SIP協議 - tinysip的實現機制



SIP是一個分層結構的協議,這意味着它的行爲是根據一組平等獨立的處理階段來描述,每一階段之間只是松耦合。協議分層描述是爲了表達,從而允許功能的描述可在一個部分跨越幾個元素。它不指定任何方式的實現。當我們說某元素包含某層,我們是指它順從該層定義的規則集。


不是協議規定的每個元素都包含各層。而且,由SIP規定的元素是邏輯元素,不是物理元素。一個物理實現可以選擇作爲不同的邏輯元素,甚至可能在一個個事務的基礎上。


SIP的最底層是語法和編碼。它的編碼使用增強Backus-Nayr形式語法(BNF)來規定。


第二層是傳輸層。它定義了網絡上一個客戶機如何發送請求和接收響應以及一個服務器如何接收請求和發送響應。所有的SIP元素包含傳輸層。


第三層是事務層。事務是SIP的基本元素。一個事務是由客戶機事務發送給服務器事務的請求(使用傳輸層),以及對應該請求的從服務器事務發送回客戶機的所有響應組成。事務層處理應用層重傳,匹配響應到請求,以及應用層超時。任何用戶代理客戶機(UAC)完成的任務使用一組事務產生。用戶代理包含一個事務層,有狀態的代理也有。無狀態的代理不包含事務層。事務層具有客戶機組成部分(稱爲客戶機事務)和服務器組成部分(稱爲服務器事務),每個代表有限的狀態機,它被構造來處理特定的請求。


事務層之上的層稱爲事務用戶(TU)。每個SIP實體,除了無狀態代理,都是事務用戶。當一個TU希望發送請求,它生成一個客戶機事務實例並且向它傳遞請求和IP地址,端口,和用來發送請求的傳輸機制。一個TU生成客戶機事務也能夠刪除它。當客戶機取消一個事務時,它請求服務器停止進一步的處理,將狀態恢復到事務初始化之前,並且生成特定的錯誤響應到該事務。這由CANCEL請求完成,它構成自己的事務,但涉及要取消的事務。


SIP通過EMAIL形式的地址來標明用戶地址。每一用戶通過一等級化的URL來標識,它通過諸如用戶電話號碼或主機名等元素來構造(例如:SIP:vision-com.com)。因爲它與EMAIL地址的相似性,SIPURLs容易與用戶的EMAIL地址關聯。


SIP提供它自己的可靠性機制從而獨立於分組層,並且只需不可靠的數據包服務即可。SIP可典型地用於UDPTCP之上。


Register,Invite, Options …

Nat Tranversal

Dialog Layer

Transaction Layer

Parsing Layer

Transport Layer


sip 協議棧分層結構圖


根據sip消息流向可以分爲incomingmessage outgoing message,incoming 消息從 下到上,即TransportLayer → Register,Invite, Options; outgoing message 消息流向與此相反。



3.根據以上定義,tinysip分如下模塊:



1).api外部接口,對sip協議支持的方法的接口封裝,協議棧提供的發起請求及接受請求對應的接口,包括registar layer, presencelayer等上層應用,當前版本支持如下請求:


REGISTERSUBSCRIBE(訂閱),MESSAGE(即時通信),PUBLISH(狀態展示),OPTIONS(查詢服務器能力),INVITE(發起請求),Cancel(取消一個請求),

BYE(結束通話)。


2).Nat traversal Nat穿越層,tinysip目前支持stunturn穿透。


3).Dialog,會話模塊,一路呼叫的唯一標識,處於sip事務層之上。

4).parserssip消息解析,處於sip 語法層,解析從傳輸層傳遞的數據包爲協議棧理解的結構。


5).transactions, 事務層,事務是一個請求以及與此請求相關的所有響應組成,用transaction id唯一標識,由於sip信令一般由udp承載,所以不能保證信息的可靠到達,所以事務層必須提供一種機制處理udp所不能提供的功能,這裏一般通過定時器及一個有限狀態機來實現。


6).transports ,傳輸層,即 udp, TCP, TLS, SCTP socket 系統調用系列,此層隱藏了所有傳輸層的細節,對於incomingsip message , 此層爲sip消息的入口,對於outgoingsip message, 此層爲sip消息的出口。



4.doubango sip 協議棧使用流程:



1.初始化


doubango sip協議棧依賴於tinyNET模塊,所以必須先調用tnet_startup函數初始化,退出時調用tnet_cleanup清除資源,初始化sip


協議棧之前必須設置用戶的域(realm參見(1)及用戶的私有(IMPI2))及共有標別(IMPU3)),這些爲ims引入的概念。


(1)realm 解釋:reaml爲域名,用來作客戶端認證用(authenticate.必須是一個有效的 sipuri 如:sip:vision-com.com.cnrealm sip協議棧啓動之前必須設置的選項,一旦協議棧啓動realm就不可以更改,如果不填寫sip代理服務器地址,則系統會用realm通過 dns NAPTR + SRV 或者DHCP(還沒實現)動態查找機制確定sip 服務器地址。


(2)用戶私有標識,爲用戶所屬網絡賦予的唯一值,用來做驗證,爲IMS中的概念,如果用doubangosip協議棧作爲普通sip功能,即非IMS網絡中的sip功能,此處的impi意義與sip協議棧中的驗證域名相同,私有ID用在身份認證,授權等,主要是安全方面的作用。


3)用戶公有標識,ims網絡中一個impi可以對應多個impu.,公有ID用在業務配置、計費等,主要是業務方面的作用。



更進一步解釋:

IMPU,它更靠近業務層,用於標識業務簽約關係,計費等,還表示用戶身份以及用於路由,但它不能表示用戶實際的位置信息,當然這個是對於非傳統固話來說,傳統固定方式下用戶號碼與位置存在綁定關係,因爲用戶線接入是固定的。
然後是IMPI,它是網絡層的東東,用於表示用戶和網絡的簽約關係,一般也可以唯一的表示一個終端。使用IMPI,網絡可以通過鑑權來識別用戶是否可以使用網絡。
最後是用戶的聯繫地址,這個是註冊的時候要帶的,它是用戶真實的尋址地址,IMPU和聯繫地址分離才能支持移動性,對於傳統GSM網絡,漫遊號起到了類似聯繫地址的作用,你也可以認爲GSM中的位置更新也是一種註冊流程。
註冊後S-CSCF知道IMPU以及聯繫地址,當有人呼叫此IMPU,根據聯繫地址即可找到實際的用戶,因此註冊維護了一個用戶的尋址通路。


2.創建以及啓動


通過調用 tsip_stack_create創建協議棧, 調用tsip_stack_start啓動,


完整例子:


tsip_stack_handle_t*stack = tsk_null;


intret;


constchar* realm_uri = "sip:vision-com.com.cn";


constchar* impi_uri = "[email protected]";


constchar* impu_uri = "sip:[email protected]";



//...必須先初始化tnet工具


tnet_startup();


// ...


//創建協議棧,指定回調函數,參數爲域名,公有及私有標識。


stack= tsip_stack_create(sip_callback, realm_uri, impi_uri, impu_uri,


TSIP_STACK_SET_PASSWORD("yourpassword"),


//...other macros...


//此處初始化其他信息,比如代理服務器地址,編碼信息等。


tsip_stack_start(stack) //啓動協議棧


TSIP_STACK_SET_NULL());//用來終止傳給app_callback的參數。


TSK_OBJECT_SAFE_FREE(stack);



tnet_cleanup();


//釋放資源


//事件回調


intsip_callback(const tsip_event_t *sipevent)


{


//事件類型


switch(sipevent->type){


casetsip_event_register:


{ /*REGISTER */


break;


}


casetsip_event_invite:


{ /*INVITE */


break


}


casetsip_event_message:


{ /*MESSAGE */


break


}


casetsip_event_publish:


{/* PUBLISH */


break


}


casetsip_event_subscribe:


{ /*SUBSCRIBE */


break


}


casetsip_event_options:


{ /*OPTIONS */


break


}


casetsip_event_dialog:


{ /*Common to all dialogs */


break


}


/*case …*/


}


會話事件,協議棧事件放入事件隊列,協議棧啓動時創建一個線程不斷從隊列裏取事件,然後通過調用事件回調通知上層用戶。



5.代碼分析


首先調用tsip_stack_create創建協議棧,tsip_stack_create內部首先檢查參數是否合法,然後創建協議棧結構tsip_stack_t,設置realm,IMPI and IMPU,初始化一些協議棧默認值。創建SigComp 信令壓縮模塊(可選)創建dns處理模寬,DHCPcontext ,接下來創建上面提到的sip協議棧各層,分別調用tsip_dialog_layer_createtsip_transac_layer_create,tsip_transport_layer_create創建會話層,事務層及傳輸層。至此,創建協議棧畢。


在真正啓動協議棧之前,即tsip_stack_createtsip_stack_start之間可調用協議棧提供的api初始化其他參數,然後調用tsip_stack_start啓動協議棧,首先啓動定時器線程,這裏定時器主要在事務層提供狀態機功調度功能。然後設置傳輸層類型,設置是否用ipsecsip信令加密,然後如果協議棧是處於客戶端模式並且代理服務器地址沒有設置則用認證的域名查找代理服務器的地址(用SNAPTR+SRV),然後設置Runnable回調,啓動run 線程,run內部不斷從消息隊列裏取消息,這裏的消息是從傳輸層從下到上傳送過來,最終串聯到消息隊列,然後調用協議棧創建時指定的回調sip_callback,所有incomingsip消息以及媒體信息的改變最終都會走到此回調函數,此函數內部根據消息類型的不同調用相應的handler,接下來啓動nat穿越模塊,設置stun地址。然後調用tsip_transport_layer_start啓動傳輸層線程,在sip端口5060接收數據,最後,設置stack->started= tsk_true; 至此協議棧啓動完畢,各層在相應端口或狀態機上監聽,不斷輪詢到來的事件並處理。


驅動過程:

協議棧啓動完畢後,對於每一個incomingoutgoing 消息的入口不一樣,下面分別分析對於呼入請求(incoming)及外乎請求(outgoing)的代碼流程。


a.呼入請求


1)客戶端傳輸層在5060端口上接收到udp包,語法層解析成識別的sip消息後傳給事務層。

2)事務層鎖住本地事務鏈表,根據sip消息的事務id在事務鏈查找是否存在匹配的事務,沒有則創建。

3)一旦找到事務或創建新事務完畢,釋放鎖並把消息傳遞到會話層。

4)會話層收到sip消息後查找會話鏈,找不到則創建會話,同時根據消息類型(invite,ack 等)設置此消息的狀態機,狀態機內指定具體事件的回調。


b.呼出請求

1)構造外乎請求,包括消息頭和消息體.

2)創建會話層,事務層,事務層調用傳輸層接口發出請求。




6. 外部編程接口


爲了在android上層通過java訪問doubango核心,imsdroiddoubango voip框架做了面向對象封裝,根據具體模塊功能抽象成具體Java類供應用層使用,應用層通過jni訪問doubango核心,同時,在imsdroid2.0版本中,根據android上應用層的架構抽象出一個類庫,doubango-ngn-stack,利用此類庫我們可以在android上自己開發一些客戶端應用程序,包括語音,視頻,即時通信,多媒體共享,會議等應用。Imsdroid2.0即是構建在 doubango-ngn-stack上的一個具體應用。



Doubango-ngn-stack原理

doubango-ngn-stack是對doubangovoip框架的一個java層封裝,內部通過java本地調用技術實現(jni),這與android上的框架設計是相符的(java類庫提供的攝像頭功能即依賴於底層驅動,上層通過jni訪問底層驅動)


doubango/bindings/java




SWIG工具把c/C++函數封裝成JAVA中的類,目前SWIG已經可以支持Python,Java, C#,RubyPHP,等語言。



7. 代碼實例分析

      註冊過程

1). 註冊流程(java-->C++-->C)


register(NgnSipService.java)

|

register(NgnRegistrationSession.java)

|

register_(sipsession.cxx)

|

tsip_action_REGISTER(stip_api_register.c)


tsip_action_REGISTER

分三步:

_tsip_action_create創建註冊請求, tsip_dialog_layer_new 創建註冊session,爲後續進入狀態機作準備, tsip_dialog_fsm_act 進入狀態機模式。


1._tsip_action_create


創建註冊請求,這裏只是創建一個抽象的請求,請求對應sip的方法,sip協議定義了

register,invite,publish,subscribe, bye,message等方法。

然後調用_tsip_action_set初始化上層(java)傳過來的參數。



  1. 請求創建成功後創建會話層tsip_dialog_layer_new

sip協議中每個請求方法對應一個會話,session,此函數根據會話類型創建相應會話的session.對於註冊會話,會調用tsip_dialog_register_create創建會話層,然後把創建後的會話保存到協議棧的會話層鏈表。

1tsip_dialog_register_createtsip_dialog_register.c)創建註冊會話

內部new一個註冊對象tsip_dialog_register_def_t,構造函數中執行如下動作。

tsip_dialog_register_ctor內部分三步:

a.tsip_dialog_init 初始化基本的會話信息,這裏分客戶端及服務器端。同時會創建此會話的狀態機,並初始化狀態機的狀態。

b.tsk_fsm_set_callback_terminated 設置註冊會話結束狀態機回調。

c.tsip_dialog_register_init 初始化具體註冊會話信息


tsip_dialog_register_client_init初始化註冊請求的客戶端狀態機回調。


這裏實際上是當一個請求過程中,當請求從一個狀態到另一個狀態時應該調用的回調函數。

tsip_dialog_register_server_init功能相同。


tsip_dialog_register_event_callback設置從傳輸層過來的註冊事件的回調,通知上層。


3. tsip_dialog_fsm_act,是所有sip會話開始進入狀態機的入口。

參數爲 會話,會話類型,sip消息,請求。

此函數內部會調用狀態機通用函數tsk_fsm_act。執行一個具體的請求,同時可能改變相應會話的狀態機的狀態。

inttsk_fsm_act(tsk_fsm_t* self, tsk_fsm_action_id action, const void*cond_data1, const void* cond_data2, …)

內部是一個循環,不斷檢測狀態,根據狀態調用相應的回調。


每個狀態機都有一個初始狀態作爲運轉入口,對於註冊請求,入口爲,


TSK_FSM_ADD_ALWAYS(_fsm_state_Started,_fsm_action_oREGISTER, _fsm_state_InProgress,tsip_dialog_register_Started_2_InProgress_X_oRegister,"tsip_dialog_register_Started_2_InProgress_X_oRegister"),


tsk_fsm_act一開始會執行tsip_dialog_register_Started_2_InProgress_X_oRegister回調,

此回調是由狀態_fsm_state_Started_fsm_state_InProgress時執行的操作,

內部先更改自己的當前狀態爲_fsm_state_InProgress,這樣給tsk_fsm_act 提供了調用下一個狀態的入口。

函數內部調用tsip_dialog_register_send_REGISTER 創建事務層,初始化事務層狀態機,最後調用傳輸層socket接口把請求發送出去。

由於sip根據請求的類型把事務層分爲幾種類型,包括客戶端請求(invite)事務,客戶端非請求事務(bye,register),服務器端請求事務,服務器端非請求事務。


所以對於註冊請求,會創建非請求客戶端事務層。

tsip_dialog_register_send_REGISTER---> tsip_dialog_request_new

--->tsip_dialog_request_send---->tsip_transac_layer_new--->tsip_transac_start

--->tsip_transac_nict_start,進入事務層狀態機模式。

這裏,在創建事務層時會設置事務層事件回調,tsip_transac_nict_event_callback

比如發出register請求,服務器端給200ok響應。此時傳輸層會把此響應作爲事件給事務層,事務層收到此事件,解析後進入事務層狀態機。


所以對於 一次 sip請求,有兩個狀態機在運轉,一個爲會話層狀態機,一個爲事務層狀態機。


至此,一個register請求已經發送出去,會話層,事務層狀態機都在運轉,

此時,如果服務器返回註冊成功,則會給客戶端傳輸層發送200OK響應。

tsip_transport_layer_dgram_cbtsip_transport_layer_handle_incoming_msg

tsip_transac_layer_handle_incoming_msgtsip_transac_layer_find_client


客戶端傳輸層收到響應後會根據響應的事務id查找是否爲已經創建的事務。

,找到後會根據此事務創建時指定的回調,調用相應事務層的回調函數,這裏爲tsip_transac_nict_event_callback,此函數內部根據消息類型調用tsip_transac_fsm_act執行事務層狀態機,比如 200ok ,會調用事務層創建時指定的回調。這裏爲tsip_transac_nict_Trying_2_Completed_X_200_to_699

內部調用會話層回調通知上層註冊成功。tsip_dialog_register_event_callback

此函數根據狀態調用相應會話層狀態機,tsip_dialog_register_InProgress_2_Connected_X_2xx,修改狀態機當前狀態,爲下一個狀態作準備,最後調用TSIP_DIALOG_REGISTER_SIGNAL發射 註冊成功事件給上層用戶。

這裏事件機制:TSIP_DIALOG_REGISTER_SIGNAL 通過tsip_register_event_signal創建一個註冊事件,然後把此事件放入協議棧啓動時的事件隊列中,


tsip_stack_start內部啓動run線程處理協議棧事件。

/* ===Runnable === */

TSK_RUNNABLE(stack)->run= run;

if((ret= tsk_runnable_start(TSK_RUNNABLE(stack), tsip_event_def_t))){

stack_error_desc= "Failed to start timer manager";

TSK_DEBUG_ERROR("%s",stack_error_desc);

gotobail;

}

run線程中不斷掃描事件隊列,pop出一個事件,然後送給sip協議棧創建時指定的事件回調,從而把協議棧事件(事務層事件,會話層事件,協議棧事件)返回給上層用戶。


至此,一次正常(沒考慮異常,401認證過程等)的註冊流程分析完畢。




2.)外乎流程:


screenAV.java


publicstatic boolean makeCall(String remoteUri, NgnMediaType mediaType){


makeCall(StringremoteUri, NgnMediaType mediaType)

|

createOutgoingSession

|

makeCall(remoteUri);NgnAVSession.java

|


callAudioVideo

|

callAudioVideo(sipsession.cxx)

|

__droid_call(sipsession.cxx)

|

__droid_call_thread(sipsession.cxx)

|

tsip_action_INVITE(tsip_api_invite)


至此,由上層調用到底層協議棧。


同樣分三步:

_tsip_action_create

tsip_dialog_layer_new

tsip_dialog_fsm_act


從創建會話層開始分析:


tsip_dialog_layer_new

tsip_dialog_invite_create

tsip_dialog_invite_ctortsip_dialog_invite.c

構造函數內過程:

1tsip_dialog_init創建相關頭域,創建invitesession狀態機並初始化狀態。


2tsk_fsm_set_callback_terminated設置 invite會話狀態機結束的回調函數tsip_dialog_invite_OnTerminated

3tsip_dialog_invite_init初始化invite請求本身,具體如下:

a.tsip_dialog_invite_client_init初始化服務器端invite會話信息,實際上爲設置客戶端invite會話的狀態機。

Started-> (send INVITE) -> Outgoing-> Connected ......

狀態轉換過程中調用相應回調。

b.tsip_dialog_invite_server_init 設置服務器端invite會話狀態機。


//Started -> (Bad Extendion) -> Terminated

//Started -> (Bad content) -> Terminated

//Started -> (Session Interval Too Small) -> Started

..........

  1. tsip_dialog_invite_hold_init設置 hold 狀態機,3GPPTS 24.610:

CommunicationHold


d.tsip_dialog_invite_stimers_init ,設置sessiontimer 狀態機,rfc RFC


4028:SessionTimers

e.tsip_dialog_invite_qos_init,設置 qos狀態機,RFC 3312

f.初始化其他狀態機。


TSIP_DIALOG(self)->callback= TSIP_DIALOG_EVENT_CALLBACK_F(tsip_dialog_invite_event_callback);

設置invite會話層事件回調,比如服務器響應,則會調用此回調,內部根據響應類型調用狀態機,根據狀態執行相應回調。


tsip_dialog_fsm_act,所有準備階段完成後,第三步進入狀態機模型,輪轉吧。


第一個調用的狀態機回調爲c0000_Started_2_Outgoing_X_oINVITE


Started-> (oINVITE) -> Outgoing

此函數內部:

首先調用tmedia_session_mgr_create初始化自己的媒體信息,後面放到invite請求的

sdp裏面。


接下來更新此次請求的狀態機階段,這樣下一個狀態機回調以此爲起點。

然後設置invite請求的一些特殊頭域。tmedia_session_mgr_set_qos/*100rel */

self->supported._100rel等。


最後調用send_INVITETSIP_DIALOG_SIGNAL產生invite事件通知上層用戶。


send_INVITE又調用send_INVITEorUPDATE


內部又分如下過程。


a.tsip_dialog_request_new,創建一個通用的sip 請求。


b.tmedia_session_mgr_get_lo創建invite消息的消息體,即sdp信息。

  1. 初始化其他頭域

tsip_dialog_request_send發送請求。 創建事務層。

tsip_transac_layer_newtsip_transac_ict_create


tsip_transac_ict_ctor初始化 客戶端invite事務層:



tsip_transac_init

tsk_fsm_set_callback_terminated

tsip_transac_ict_init


最終開啓客戶端請求事務狀態機。


tsip_transac_start


當服務器端返回 200ok後,會調用事務層,事務層又轉給會話層,最終轉到


c0000_Outgoing_2_Connected_X_i2xxINVITE回調函數,

內部對200ok響應做出裏:


tsip_dialog_update更新會話狀態,


tsip_dialog_invite_process_ro處理對端 sdp,建立rtp流。



send_ACK 給 ACK響應。


TSIP_DIALOG_INVITE_SIGNAL最後給上層發射 事件,通知用戶接通。


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