在P2P通訊標準協議(二)中,介紹了TURN的基本互動流程,在上篇結束部分也有說到,TURN作為STUN
協議的一個擴充,保持了STUN的工具性質,而不作為完整的NAT傳輸解決方案,只提供穿透NAT的功能,
並且由具體的應用程式來使用.雖然TURN也可以獨立工作,但其本身就是被設計為ICE/RFC5245
的一部分,本章就來介紹一下ICE協議的具體內容.
SDP
ICE資訊的描述格式通常採用標準的SDP,其全稱為Session Description Protocol,即會話描述協議.
SDP只是一種資訊格式的描述標準,不屬於傳輸協議,但是可以被其他傳輸協議用來交換必要的資訊,如SIP和RTSP等.
SDP資訊
一個SDP會話描述包含如下部分:
- 會話名稱和會話目的
- 會話的啟用時間
- 構成會話的媒體(media)
- 為了接收該媒體所需要的資訊(如地址,埠,格式等)
因為在中途參與會話也許會受限制,所以可能會需要一些額外的資訊:
- 會話使用的的頻寬資訊
- 會話擁有者的聯絡資訊
一般來說,SDP必須包含充分的資訊使得應用程式能夠加入會話,並且可以提供任何非參與者使用時需要知道的資源
狀況,後者在當SDP同時用於多個會話宣告協議時尤其有用.
SDP格式
SDP是基於文字的協議,使用ISO 10646字符集和UTF-8編碼.SDP欄位名稱和屬性名稱只使用UTF-8的一個子集US-ASCII,
因此不能存在中文.雖然理論上文字欄位和屬性欄位支援全集,但最好還是不要在其中使用中文.
SDP會話描述包含了多行如下型別的文字:
<type>=<value>
其中type是大小寫敏感的,其中一些行是必須要有的,有些是可選的,所有元素都必須以固定順序給出.固定的順序極大改善了
錯誤檢測,同時使得處理端設計更加簡單.如下所示,其中可選的元素標記為* :
會話描述:
v= (protocol version)
o= (originator and session identifier)
s= (session name)
i=* (session information)
u=* (URI of description)
e=* (email address)
p=* (phone number)
c=* (connection information -- not required if included in
all media)
b=* (zero or more bandwidth information lines)
One or more time descriptions ("t=" and "r=" lines; see below)
z=* (time zone adjustments)
k=* (encryption key)
a=* (zero or more session attribute lines)
Zero or more media descriptions
時間資訊描述:
t= (time the session is active)
r=* (zero or more repeat times)
多媒體資訊描述(如果有的話):
m= (media name and transport address)
i=* (media title)
c=* (connection information -- optional if included at
session level)
b=* (zero or more bandwidth information lines)
k=* (encryption key)
a=* (zero or more media attribute lines)
所有元素的type都為小寫,並且不提供擴充.但是我們可以用a(attribute)欄位來提供額外的資訊.一個SDP描述的例子如下:
v=0
o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5
s=SDP Seminar
i=A Seminar on the session description protocol
u=http://www.example.com/seminars/sdp.pdf
e=j.doe@example.com (Jane Doe)
c=IN IP4 224.2.17.12/127
t=2873397496 2873404696
a=recvonly
m=audio 49170 RTP/AVP 0
m=video 51372 RTP/AVP 99
a=rtpmap:99 h263-1998/90000
具體欄位的type/value描述和格式可以去參考RFC4566.
Offer/Answer模型
上文說到,SDP用來描述多播主幹網路的會話資訊,但是並沒有具體的互動操作細節是如何實現的,因此RFC3264
定義了一種基於SDP的offer/answer模型.在該模型中,會話參與者的其中一方生成一個SDP報文構成offer,
其中包含了一組offerer希望使用的多媒體流和編解碼方法,以及offerer用來接收改資料的IP地址和埠資訊.
offer傳輸到會話的另一端(稱為answerer),由answerer生成一個answer,即用來響應對應offer的SDP報文.
answer中包含不同offer對應的多媒體流,並指明該流是否可以接受.
RFC3264只介紹了交換資料過程,而沒有定義傳遞offer/answer報文的方法,後者在RFC3261/SIP
即會話初始化協議中描述.值得一提的是,offer/answer模型也經常被SIP作為一種基本方法使用.
offer/answer模型在SDP報文的基礎上進行了一些定義,工作過程不在此描述,需要了解細節的朋友可以參考RFC3261.
ICE
ICE的全稱為Interactive Connectivity Establishment,即互動式連線建立.初學者可能會將其與網路程式設計的ICE
弄混,其實那是不一樣的東西,在網路程式設計中,如C++的ICE庫,都是指Internate Communications Engine,
是一種用於分散式程式設計的網路通訊中介軟體.我們這裡說的只是互動式連線建立.
ICE是一個用於在offer/answer模式下的NAT傳輸協議,主要用於UDP下多媒體會話的建立,其使用了STUN協議以及TURN
協議,同時也能被其他實現了offer/answer模型的的其他程式所使用,比如SIP(Session Initiation Protocol).
使用offer/answer模型(RFC3264)的協議通常很難在NAT之間穿透,因為其目的一般是建立多媒體資料流,而且在報文中還
攜帶了資料的源IP和埠資訊,這在通過NAT時是有問題的.RFC3264還嘗試在客戶端之間建立直接的通路,因此中間就缺少
了應用層的封裝.這樣設計是為了減少媒體資料延遲,減少丟包率以及減少程式部署的負擔.然而這一切都很難通過NAT而完成.
有很多解決方案可以使得這些協議執行於NAT環境之中,包括應用層閘道器(ALGs)
,Classic STUN
以及Realm Specific IP
+SDP
協同工作等方法.不幸的是,這些技術都是在某些網路拓撲下工作很好,而在另一些環境下表現又很差,因此我們需要一個單一的,
可自由定製的解決方案,以便能在所有環境中都能較好工作.
ICE工作流程
一個典型的ICE工作環境如下,有兩個端點L和R,都執行在各自的NAT之後(他們自己也許並不知道),NAT的型別和性質也是未知的.
L和R通過交換SDP資訊在彼此之間建立多媒體會話,通常交換通過一個SIP伺服器完成:
+-----------+
| SIP |
+-------+ | Srvr | +-------+
| STUN | | | | STUN |
| Srvr | +-----------+ | Srvr |
| | / \ | |
+-------+ / \ +-------+
/<- Signaling ->\
/ \
+--------+ +--------+
| NAT | | NAT |
+--------+ +--------+
/ \
/ \
/ \
+-------+ +-------+
| Agent | | Agent |
| L | | R |
| | | |
+-------+ +-------+
ICE的基本思路是,每個終端都有一系列傳輸地址
(包括傳輸協議,IP地址和埠)的候選,可以用來和其他端點進行通訊.
其中可能包括:
- 直接和網路介面聯絡的傳輸地址(host address)
- 經過NAT轉換的傳輸地址,即反射地址(server reflective address)
- TURN伺服器分配的中繼地址(relay address)
雖然潛在要求任意一個L的候選地址都能用來和R的候選地址進行通訊.但是實際中發現有許多組合是無法工作的.舉例來說,
如果L和R都在NAT之後而且不處於同一內網,他們的直接地址就無法進行通訊.ICE的目的就是為了發現哪一對候選地址的
組合可以工作,並且通過系統的方法對所有組合進行測試(用一種精心挑選的順序).
為了執行ICE,客戶端必須要識別出其所有的地址候選,ICE中定義了三種候選型別,有些是從實體地址或者邏輯網路介面繼承
而來,其他則是從STUN或者TURN伺服器發現的.很自然,一個可用的地址為和本地網路介面直接聯絡的地址,通常是內網地址,
稱為HOST CANDIDATE
,如果客戶端有多個網路介面,比如既連線了WiFi又插著網線,那麼就可能有多個內網地址候選.
其次,客戶端通過STUN或者TURN來獲得更多的候選傳輸地址,即SERVER REFLEXIVE CANDIDATES
和RELAYED CANDIDATES
,
如果TURN伺服器是標準化的,那麼兩種地址都可以通過TURN伺服器獲得.當L獲得所有的自己的候選地址之後,會將其
按優先順序排序,然後通過signaling通道傳送到R.候選地址被儲存在SDP offer報文的屬性部分.當R接收到offer之後,
就會進行同樣的獲選地址收集過程,並返回給L.
這一步驟之後,兩個對等端都擁有了若干自己和對方的候選地址,並將其配對,組成CANDIDATE PAIRS
.為了檢視哪對組合
可以工作,每個終端都進行一系列的檢查.每個檢查都是一次STUN request/response傳輸,將request從候選地址對的本地
地址傳送到遠端地址. 連線性檢查的基本原則很簡單:
- 以一定的優先順序將候選地址對進行排序.
- 以該優先順序順序傳送checks請求
- 從其他終端接收到checks的確認資訊
兩端連線性測試,結果是一個4次握手過程:
L R
- -
STUN request -> \ L's
<- STUN response / check
<- STUN request \ R's
STUN response -> / check
值的一提的是,STUN request的傳送和接收地址都是接下來進多媒體傳輸(如RTP和RTCP)的地址和埠,所以,
客戶端實際上是將STUN協議與RTP/RTCP協議在資料包中進行復用(而不是在埠上覆用).
由於STUN Binding request用來進行連線性測試,因此STUN Binding response中會包含終端的實際地址,
如果這個地址和之前學習的所有地址都不匹配,傳送方就會生成一個新的candidate,稱為PEER REFLEXIVE CANDIDATE
,
和其他candidate一樣,也要通過ICE的檢查測試.
連線性檢查(Connectivity Checks)
所有的ICE實現都要求與STUN(RFC5389)相容,並且廢棄Classic STUN(RFC3489).ICE的完整實現既生成checks(作為STUN client),
也接收checks(作為STUN server),而lite實現則只負責接收checks.這裡只介紹完整實現情況下的檢查過程.
1. 為中繼候選地址生成許可(Permissions).
2. 從本地候選往遠端候選傳送Binding Request.
在Binding請求中通常需要包含一些特殊的屬性,以在ICE進行連線性檢查的時候提供必要資訊.
- PRIORITY 和 USE-CANDIDATE
- 終端必須在其request中包含PRIORITY屬性,指明其優先順序,優先順序由公式計算而得.
如果有需要也可以給出特別指定的候選(即USE-CANDIDATE屬性).
- 終端必須在其request中包含PRIORITY屬性,指明其優先順序,優先順序由公式計算而得.
- ICE-CONTROLLED和ICE-CONTROLLING
- 在每次會話中,每個終端都有一個身份,有兩種身份,即受控方(controlled role)和主控方(controlling role).
主控方負責選擇最終用來通訊的候選地址對,受控方被告知哪個候選地址對用來進行哪次媒體流傳輸,
並且不生成更新過的offer來提示此次告知.發起ICE處理程式(即生成offer)的一方必須是主控方,而另一方則是受控方.
如果終端是受控方,那麼在request中就必須加上ICE-CONTROLLED屬性,同樣,如果終端是主控方,就需要ICE-CONTROLLING屬性.
- 在每次會話中,每個終端都有一個身份,有兩種身份,即受控方(controlled role)和主控方(controlling role).
- 生成Credential
- 作為連線性檢查的Binding Request必須使用STUN的短期身份驗證.驗證的使用者名稱被格式化為一系列username段
的聯結,包含了傳送請求的所有對等端的使用者名稱,以冒號隔開;密碼就是對等端的密碼.
- 作為連線性檢查的Binding Request必須使用STUN的短期身份驗證.驗證的使用者名稱被格式化為一系列username段
3. 處理Response.
當收到Binding Response時,終端會將其與Binding Request相聯絡,通常通過事務ID.隨後將會將此事務ID與
候選地址對進行繫結.
- 失敗響應
- 如果STUN傳輸返回487(Role Conflict)錯誤響應,終端首先會檢查其是否包含了ICE-CONTROLLED或ICE-CONTROLLING
屬性.如果有ICE-CONTROLLED,終端必須切換為controlling role;如果請求包含ICE-CONTROLLING屬性,
則必須切換為controlled role.切換好之後,終端必須使產生487錯誤的候選地址對進入檢查佇列中,
並將此地址對的狀態設定為Waiting
.
- 如果STUN傳輸返回487(Role Conflict)錯誤響應,終端首先會檢查其是否包含了ICE-CONTROLLED或ICE-CONTROLLING
- 成功響應,一次連線檢查在滿足下列所有情況時候就被認為成功:
- STUN傳輸產生一個Success Response
- response的源IP和埠等於Binding Request的目的IP和埠
- response的目的IP和埠等於Binding Request的源IP和埠
終端收到成功響應之後,先檢查其mapped address是否與本地記錄的地址對有匹配,如果沒有則生成一個新的候選地址.
即對等端的反射地址.如果有匹配,則終端會構造一個可用候選地址對(valid pair).通常很可能地址對不存在於任何
檢查列表中,檢索檢查列表中沒有被伺服器反射的本地地址,這些地址把它們的本地候選轉換成伺服器反射地址的基地址,
並把冗餘的地址去除掉.
後記
本文介紹了一種完整的NAT環境通訊解決方案ICE,並且對其中涉及到的概念SDP和offer/answer模型也作了簡要介紹.
ICE是使用STUN/TURN工具性質的最主要協議之一,其中TURN一開始也被設計為ICE協議的一部分.值的一提的是,
本文只是對這幾種協議作了概述性的說明,而具體工作過程和詳細的屬性描述都未包含,因此如果需要根據協議來
實現具體的應用程式,還需要對RFC的文件進行仔細閱讀.這裡給出一些參考:
而具體的程式碼以及實現可以參考: