Doubango
voip
框架分析之tinysip
協(xié)議棧
1.
tinysip
介紹 :
兼容性 : SIP
(RFC 3261)
以及 3GPP
IMS/LTE (TS 24.229) implementation
依賴 tinySAK,
tinyNET, tinySDP, tinyMEDIA, tinyHTTP and tinyIPSec.
2.
SIP
協(xié)議 - tinysip
的實(shí)現(xiàn)機(jī)制
SIP
是一個(gè)分層結(jié)構(gòu)的協(xié)議,這意味著它的行為是根據(jù)一組平等獨(dú)立的處理階段來描述,每一階段之間只是松耦合。協(xié)議分層描述是為了表達(dá),從而允許功能的描述可在一個(gè)部分跨越幾個(gè)元素。它不指定任何方式的實(shí)現(xiàn)。當(dāng)我們說某元素包含某層,我們是指它順從該層定義的規(guī)則集。
不是協(xié)議規(guī)定的每個(gè)元素都包含各層。而且,由SIP
規(guī)定的元素是邏輯元素,不是物理元素。一個(gè)物理實(shí)現(xiàn)可以選擇作為不同的邏輯元素,甚至可能在一個(gè)個(gè)事務(wù)的基礎(chǔ)上。
SIP
的最底層是語法和編碼。它的編碼使用增強(qiáng)Backus-Nayr
形式語法(BNF
)來規(guī)定。
第二層是傳輸層。它定義了網(wǎng)絡(luò)上一個(gè)客戶機(jī)如何發(fā)送請(qǐng)求和接收響應(yīng)以及一個(gè)服務(wù)器如何接收請(qǐng)求和發(fā)送響應(yīng)。所有的SIP
元素包含傳輸層。
第三層是事務(wù)層。事務(wù)是SIP
的基本元素。一個(gè)事務(wù)是由客戶機(jī)事務(wù)發(fā)送給服務(wù)器事務(wù)的請(qǐng)求(使用傳輸層),以及對(duì)應(yīng)該請(qǐng)求的從服務(wù)器事務(wù)發(fā)送回客戶機(jī)的所有響應(yīng)組成。事務(wù)層處理應(yīng)用層重傳,匹配響應(yīng)到請(qǐng)求,以及應(yīng)用層超時(shí)。任何用戶代理客戶機(jī)(UAC
)完成的任務(wù)使用一組事務(wù)產(chǎn)生。用戶代理包含一個(gè)事務(wù)層,有狀態(tài)的代理也有。無狀態(tài)的代理不包含事務(wù)層。事務(wù)層具有客戶機(jī)組成部分(稱為客戶機(jī)事務(wù))和服務(wù)器組成部分(稱為服務(wù)器事務(wù)),每個(gè)代表有限的狀態(tài)機(jī),它被構(gòu)造來處理特定的請(qǐng)求。
事務(wù)層之上的層稱為事務(wù)用戶(TU
)。每個(gè)SIP
實(shí)體,除了無狀態(tài)代理,都是事務(wù)用戶。當(dāng)一個(gè)TU
希望發(fā)送請(qǐng)求,它生成一個(gè)客戶機(jī)事務(wù)實(shí)例并且向它傳遞請(qǐng)求和IP
地址,端口,和用來發(fā)送請(qǐng)求的傳輸機(jī)制。一個(gè)TU
生成客戶機(jī)事務(wù)也能夠刪除它。當(dāng)客戶機(jī)取消一個(gè)事務(wù)時(shí),它請(qǐng)求服務(wù)器停止進(jìn)一步的處理,將狀態(tài)恢復(fù)到事務(wù)初始化之前,并且生成特定的錯(cuò)誤響應(yīng)到該事務(wù)。這由CANCEL
請(qǐng)求完成,它構(gòu)成自己的事務(wù),但涉及要取消的事務(wù)。
SIP
通過EMAIL
形式的地址來標(biāo)明用戶地址。每一用戶通過一等級(jí)化的URL
來標(biāo)識(shí),它通過諸如用戶電話號(hào)碼或主機(jī)名等元素來構(gòu)造(例如:SIP:vision-com.com
)。因?yàn)樗cEMAIL
地址的相似性,SIP
URLs
容易與用戶的EMAIL
地址關(guān)聯(lián)。
SIP
提供它自己的可靠性機(jī)制從而獨(dú)立于分組層,并且只需不可靠的數(shù)據(jù)包服務(wù)即可。SIP
可典型地用于UDP
或TCP
之上。
Register,Invite, Options …
Nat Tranversal
Dialog Layer
Transaction Layer
Parsing Layer
Transport Layer
sip
協(xié)議棧分層結(jié)構(gòu)圖
根據(jù)sip
消息流向可以分為incoming
message
和outgoing message,
incoming
消息從 下到上,即Transport
Layer → Register,Invite, Options; outgoing message
消息流向與此相反。
3
.
根據(jù)以上定義,tinysip
分如下模塊:
1).api
外部接口,對(duì)sip
協(xié)議支持的方法的接口封裝,協(xié)議棧提供的發(fā)起請(qǐng)求及接受請(qǐng)求對(duì)應(yīng)的接口,
包括
registar layer, presence
layer
等上層應(yīng)用,當(dāng)前版本支持如下請(qǐng)求:
REGISTER
,SUBSCRIBE(
訂閱),MESSAGE(
即時(shí)通信),PUBLISH
(狀態(tài)展示),OPTIONS
(查詢服務(wù)器能力),INVITE
(發(fā)起請(qǐng)求),Cancel
(取消一個(gè)請(qǐng)求),
BYE
(結(jié)束通話)。
2).
Nat traversal
:Nat
穿越層,tinysip
目前支持stun
,turn
穿透。
3).Dialog
,
會(huì)話模塊,一路呼叫的唯一標(biāo)識(shí),
處于sip
事務(wù)層之上。
4).parsers
,sip
消息解析,處于
sip
語法層,解析從傳輸層傳遞的數(shù)據(jù)包為協(xié)議棧理解的結(jié)構(gòu)。
5).
transactions
, 事務(wù)層,事務(wù)是一個(gè)請(qǐng)求以及與此請(qǐng)求相關(guān)的所有響應(yīng)組成,用
transaction id
唯一標(biāo)識(shí),由于sip
信令一般由udp
承載,所以不能保證信息的可靠到達(dá),所以事務(wù)層必須提供一種機(jī)制處理udp
所不能提供的功能,這里一般通過定時(shí)器及一個(gè)有限狀態(tài)機(jī)來實(shí)現(xiàn)。
6).
transports ,
傳輸層,即 udp
, TCP, TLS, SCTP socket
系統(tǒng)調(diào)用系列,此層隱藏了所有傳輸層的細(xì)節(jié),對(duì)于incoming
sip message
, 此層為sip
消息的入口,
對(duì)于outgoing
sip message,
此層為sip
消息的出口。
4.
doubango sip
協(xié)議棧使用流程:
1
).
初始化
doubango
sip
協(xié)議棧依賴于tinyNET
模塊,所以必須先調(diào)用tnet_startup
函數(shù)初始化,退出時(shí)調(diào)用tnet_cleanup
清除資源,初始化sip
協(xié)議棧之前必須設(shè)置用戶的域(realm
參見(1
))
及用戶的私有(IMPI
(2
))及共有標(biāo)別(IMPU
(3
)),
這些為ims
引入的概念。
(1)
realm
解釋:reaml
為域名,用來作客戶端認(rèn)證用(authenticate
).
必須是一個(gè)有效的 sip
uri
如:sip:vision-com.com.cn
,realm
為sip
協(xié)議棧啟動(dòng)之前必須設(shè)置的選項(xiàng),一旦協(xié)議棧啟動(dòng)realm
就不可以更改,如果不填寫sip
代理服務(wù)器地址,則系統(tǒng)會(huì)用realm
通過 dns NAPTR + SRV
或者DHCP
(
還沒實(shí)現(xiàn))
動(dòng)態(tài)
查找機(jī)制確定sip
服務(wù)器地址。
(2)
用戶私有標(biāo)識(shí),為用戶所屬網(wǎng)絡(luò)賦予的唯一值,用來做驗(yàn)證,為IMS
中的概念,如果用doubango
sip
協(xié)議棧作為普通sip
功能,即非IMS
網(wǎng)絡(luò)中的sip
功能,此處的impi
意義與sip
協(xié)議棧中的驗(yàn)證域名相同。
(3
)用戶公有標(biāo)識(shí),ims
網(wǎng)絡(luò)中
一個(gè)impi
可以對(duì)應(yīng)多個(gè)impu.
對(duì)比理解,比如我們的手機(jī)只能屬于一個(gè)地方,而當(dāng)我們到不同外地后會(huì)有漫游,此處的impi
即是我們手機(jī)本地給的唯一標(biāo)識(shí),而impu
是手機(jī)到外地后,當(dāng)?shù)鼐W(wǎng)絡(luò)給的一個(gè)標(biāo)識(shí)。
2
).
創(chuàng)建以及啟動(dòng)
通過調(diào)用 tsip_stack_create
創(chuàng)建協(xié)議棧, 調(diào)用tsip_stack_start
啟動(dòng),
完整例子:
tsip_stack_handle_t
*stack = tsk_null;
int
ret;
const
char* realm_uri = "sip:vision-com.com.cn";
const
char* impi_uri = "lideping@vision-com.com.cn";
const
char* impu_uri = "sip:bob@vision-com.com.cn";
//
...
必須先初始化tnet
工具
tnet_startup();
// ...
//
創(chuàng)建協(xié)議棧,指定回調(diào)函數(shù),參數(shù)為域名,公有及私有標(biāo)識(shí)。
stack
= tsip_stack_create(sip_callback, realm_uri, impi_uri, impu_uri,
TSIP_STACK_SET_PASSWORD("yourpassword"),
//
...other macros...
//
此處初始化
其他信息,比如代理服務(wù)器地址,編碼信息等。
tsip_stack_start(stack)
//
啟動(dòng)協(xié)議棧
TSIP_STACK_SET_NULL());
//
用來終止傳給app_callback
的參數(shù)。
TSK_OBJECT_SAFE_FREE(stack);
tnet_cleanup();
//
釋放資源
//
事件回調(diào)
int
sip_callback(const tsip_event_t *sipevent)
{
//
事件類型
switch(sipevent->type){
case
tsip_event_register:
{ /*
REGISTER */
break;
}
case
tsip_event_invite:
{ /*
INVITE */
break
}
case
tsip_event_message:
{ /*
MESSAGE */
break
}
case
tsip_event_publish:
{
/* PUBLISH */
break
}
case
tsip_event_subscribe:
{ /*
SUBSCRIBE */
break
}
case
tsip_event_options:
{ /*
OPTIONS */
break
}
case
tsip_event_dialog:
{ /*
Common to all dialogs */
break
}
/*
case …*/
}
3
).
代碼分析
首先調(diào)用tsip_stack_create
創(chuàng)建協(xié)議棧
,tsip_stack_create
內(nèi)部首先檢查參數(shù)是否合法,然后創(chuàng)建協(xié)議棧結(jié)構(gòu)tsip_stack_t
,設(shè)置realm,
IMPI and IMPU
,初始化一些協(xié)議棧默認(rèn)值。創(chuàng)建
SigComp
信令壓縮模塊(可選)
創(chuàng)建dns
處理模寬,DHCP
context
,接下來創(chuàng)建上面提到的sip
協(xié)議棧各層,分別調(diào)用tsip_dialog_layer_create
,tsip_transac_layer_create,tsip_transport_layer_create
創(chuàng)建會(huì)話層,事務(wù)層及傳輸層。至此,創(chuàng)建協(xié)議棧畢。
在真正啟動(dòng)協(xié)議棧之前,即
tsip_stack_create
與tsip_stack_start
之間
可調(diào)用協(xié)議棧提供的api
初始化其他參數(shù),
然后調(diào)用tsip_stack_start
啟動(dòng)協(xié)議棧,首先啟動(dòng)定時(shí)器線程,這里定時(shí)器主要在事務(wù)層提供狀態(tài)機(jī)功調(diào)度功能。然后設(shè)置傳輸層類型,設(shè)置是否用ipsec
把sip
信令加密,
然后如果協(xié)議棧是處于客戶端模式并且代理服務(wù)器地址沒有設(shè)置則用認(rèn)證的域名查找代理服務(wù)器的地址(用S
NAPTR+SRV),
然后設(shè)置Runnable
回調(diào),啟動(dòng)run
線程,run
內(nèi)部不斷從消息隊(duì)列里取消息,這里的消息是從傳輸層從下到上傳送過來,最終串聯(lián)到消息隊(duì)列,然后調(diào)用協(xié)議棧創(chuàng)建時(shí)指定的回調(diào)sip_callback
,所有incoming
sip
消息以及媒體信息的改變最終都會(huì)走到此回調(diào)函數(shù),此函數(shù)內(nèi)部根據(jù)消息類型的不同調(diào)用相應(yīng)的handler
,
接下來啟動(dòng)nat
穿越模塊,設(shè)置stun
地址。然后調(diào)用tsip_transport_layer_start
啟動(dòng)傳輸層線程,在sip
端口5060
接收數(shù)據(jù),最后,設(shè)置stack->started
= tsk_true;
至此協(xié)議棧啟動(dòng)完畢,各層在相應(yīng)端口或狀態(tài)機(jī)上監(jiān)聽,不斷輪詢到來的事件并處理。
驅(qū)動(dòng)過程:
協(xié)議棧啟動(dòng)完畢后,對(duì)于每一個(gè)incoming
及outgoing
消息的
入口是不一樣的,下面分別分析對(duì)于呼入請(qǐng)求(incoming)
及外乎請(qǐng)求(outgoing)
的代碼流程。
a.
呼入請(qǐng)求
1
)客戶端傳輸層在5060
端口上接收到udp
包,語法層解析成識(shí)別的sip
消息后傳給事務(wù)層。
2
)事務(wù)層鎖住本地事務(wù)鏈表,根據(jù)sip
消息的
事務(wù)id
在事務(wù)鏈查找是否存在匹配的事務(wù),沒有則創(chuàng)建。
3
)一旦找到事務(wù)或創(chuàng)建新事務(wù)完畢,釋放鎖并把消息傳遞到會(huì)話層。
4
)會(huì)話層收到sip
消息后查找會(huì)話鏈,找不到則創(chuàng)建會(huì)話,同時(shí)根據(jù)消息類型(invite,
ack
等)設(shè)置此消息的狀態(tài)機(jī),狀態(tài)機(jī)內(nèi)指定具體事件的回調(diào)。
b.
呼出請(qǐng)求
........
外部編程接口
為了在android
上層通過java
訪問doubango
核心,imsdroid
對(duì)doubango voip
框架做了面向?qū)ο蠓庋b,根據(jù)具體模塊功能抽象成具體Java
類供應(yīng)用層使用,應(yīng)用層通過jni
訪問doubango
核心,同時(shí),在imsdroid
2.0
版本中,根據(jù)android
上應(yīng)用層的架構(gòu)抽象出一個(gè)類庫,doubango-ngn-stack,
利用此類庫我們可以在android
上自己開發(fā)一些客戶端應(yīng)用程序,包括語音,視頻,即時(shí)通信,多媒體共享,會(huì)議等應(yīng)用。Imsdroid
2.0
即是構(gòu)建在 doubango-ngn-stack
上的一個(gè)具體應(yīng)用。
Doubango-ngn-stack
原理
doubango-ngn-stack
是對(duì)doubango
voip
框架的一個(gè)java
層封裝,內(nèi)部通過java
本地調(diào)用技術(shù)實(shí)現(xiàn)(jni),
這與android
上的框架設(shè)計(jì)是相符的(
如
java
類庫提供的攝像頭功能即依賴于底層驅(qū)動(dòng),上層通過jni
訪問底層驅(qū)動(dòng))
,
doubango/bindings/java
|