本文目錄一覽:
webrtc P2P之turn協議介紹
TURN的全稱為Traversal Using Relays around NAT,是STUN/RFC5389的一個拓展,主要添加了Relay功能。如果終端在NAT之後,那麼在特定的情景下,有可能使得終端無法和其對等端(peer)進行直接的通信,這時就需要公網的服務器作為一個中繼, 對來往的數據進行轉發。這個轉發的協議就被定義為TURN。TURN和其他中繼協議的不同之處在於,它允許客戶端使用同一個中繼地址(relay address)與多個不同的peer進行通信。
使用TURN協議的客戶端必須能夠通過中繼地址和對等端進行通訊,並且能夠得知每個peer的的IP地址和端口(確切地說,應該是peer的服務器反射地址)。而這些行為如何完成,是不在TURN協議範圍之內的。其中一個可用的方式是客戶端通過email來告知對等端信息,另一種方式是客戶端使用一些指定的協議,如“introduction” 或 “rendezvous”,詳見RFC5128
如果TURN使用於ICE協議中,relay地址會作為一個候選,由ICE在多個候選中進行評估,選取最合適的通訊地址。一般來說中繼的優先級都是最低的。TURN協議被設計為ICE協議(Interactive Connectivity Establishment)的一部分,而且也強烈建議用戶在他們的程序里使用ICE,但是也可以獨立於ICE的運行。值得一提的是,TURN協議本身是STUN的一個拓展,因此絕大部分TURN報文都是STUN類型的,作為STUN的一個拓展,TURN增加了新的方法(method)和屬性(attribute)。
在典型的情況下,TURN客戶端連接到內網中,並且通過一個或者多個NAT到達公網,TURN服務器架設在公網中,不同的客戶端以TURN服務器為中繼和其他peer進行通信,如下圖所示:
在上圖中,左邊的TURN Client是位於NAT後面的一個客戶端(內網地址是10.1.1.2:49721),連接公網的TURN服務器(默認端口3478)後,服務器會得到一個Client的反射地址(Reflexive Transport Address, 即NAT分配的公網IP和端口)192.0.2.1:7000,此時Client會通過TURN命令創建或管理ALLOCATION,allocation是服務器上的一個數據結構,包含了中繼地址的信息。服務器隨後會給Client分配一個中繼地址,即圖中的192.0.2.15:50000,另外兩個對等端若要通過TURN協議和Client進行通信,可以直接往中繼地址收發數據即可,TURN服務器會把發往指定中繼地址的數據轉發到對應的Client,這裡是其反射地址。
Server上的每一個allocation都唯一對應一個client,並且只有一個中繼地址,因此當數據包到達某個中繼地址時,服務器總是知道應該將其轉發到什麼地方。但值得一提的是,一個Client可能在同一時間在一個Server上會有多個allocation,這和上述規則是並不矛盾的。
在協議中,TURN服務器與peer之間的連接都是基於UDP的,但是服務器和客戶端之間可以通過其他各種連接來傳輸STUN報文,比如TCP/UDP/TLS-over-TCP. 客戶端之間通過中繼傳輸數據時候,如果用了TCP,也會在服務端轉換為UDP,因此建議客戶端使用
UDP來進行傳輸. 至於為什麼要支持TCP,那是因為一部分防火牆會完全阻擋UDP數據,而對於三次握手的TCP數據則不做隔離.
要在服務器端獲得一個中繼分配,客戶端須使用分配事務. 客戶端發送分配請求(Allocate request)到服務器,然後服務器返回分配成功響應,並包含了分配的地址.客戶端可以在屬性字段描述其想要的分配類型(比如生命周期).由於中繼數據實現了安全傳輸,服務器會要求對客戶端進行驗證,主要使用STUN的long-term credential mechanism.
一旦中繼傳輸地址分配好,客戶端必須要將其保活.通常的方法是發送刷新請求(Refresh request)到服務端.這在TURN中是一個標準的方法.刷新頻率取決於分配的生命期,默認為10分鐘.客戶端也可以在刷新請求里指定一個更長的生命期,而服務器會返回一個實際上分配的時間. 當客戶端想中指通信時,可以發送一個生命期為0的刷新請求.
服務器和客戶端都保存有一個成為五元組(5-TUPLE)的信息,比如對於客戶端來說,五元組包括客戶端本地地址/端口,服務器地址/端口,和傳輸協議;服務器也是類似,只不過將客戶端的地址變為其反射地址,因為那才是服務器所見到的. 服務器和客戶端在分配
請求中都帶有5-TUPLE信息,並且也在接下來的信息傳輸中使用,因此彼此都知道哪一次分配對應哪一次傳輸.
如上圖所示,客戶端首先發送Allocate請求,但是沒帶驗證信息,因此STUN服務器會返回error response,客戶端收到錯誤後加上所需的驗證信息再次請求,才能進行成功的分配.
client和peer之間有兩種方法通過TURN server交換應用信息,第一種是使用Send和Data方法(method),第二種是使用通道(channels),兩種方法都通過某種方式告知服務器哪個peer應該接收數據,以及服務器告知client數據來自哪個peer.
Send Mechanism使用了Send和Data指令(Indication).其中Send指令用來把數據從client發送到server,而Data指令用來把數據從server發送到client.當使用Send指令時,客戶端發送一個Send Indication到服務端,其中包含:
當服務器收到Send Indication之後,會將DATA部分的數據解析出來,並將其以UDP的格式轉發到對應的端點去,並且在封裝數據包的時候把client的中繼地址作為源地址.從而從對等端發送到中繼地址的數據也會被服務器轉發到client上.值得一提的是,Send/Data Indication是不支持驗證的,因為長效驗證機制不支持對indication的驗證,因此為了防止攻擊,TURN要求client在給對等端發送indication之前先安裝一個到對等端的許可(permission),如下圖所示,client到Peer B沒有安裝許可,導致其indication數據包將被服務器丟棄,對於peer B也是同樣:
TURN支持兩種方式來創建許可,一種是發送CreatePermission request
對於一些應用程序,比如VOIP(Voice over IP),在Send/Data Indication中多加的36字節格式信息會加重客戶端和服務端之間的帶寬壓力.為改善這種情況,TURN提供了第二種方法來讓client和peer交互數據.該方法使用另一種數據包格式,即ChannelData message,信道數據報文. ChannelData message不使用STUN頭部,而使用一個4字節的頭部,包含了一個稱之為信道號的值(channel number).每一個使用中的信道號都與一個特定的peer綁定,即作為對等端地址的一個記號.
要將一個信道與對等端綁定,客戶端首先發送一個信道綁定請求(ChannelBind Request)到服務器,並且指定一個未綁定的信道號以及對等端的地址信息.綁定後client和server都能通過ChannelData message來發送和轉發數據.信道綁定默認持續10分鐘,並且可以通過重新發送ChannelBind Request來刷新持續時間.和Allocation不同的是,並沒有直接刪除綁定的方法,只能等待其超時自動失效.
上圖中0x4001為信道號,即ChannelData message的頭部中頭2字節,值得一提的是信道號的選取有如下要求:
在上一章也提到過,因為RFC是標準協議,因此實現上往往有良好的兼容性和拓展性.現存的開源P2P應用程序,如果按照標準來設計,可以很容易與之對接.其中比較著名的就是PJSIP,PJSIP是一個開源的多媒體通信庫,實現了許多標準協議,如SIP, SDP, RTP, STUN, TURN 和 ICE. 當然我們也能自己實現.比如GitHub上的TurnServer就是其中一個對TURN服務端的實現.下面在局域網環境下對TURN數據包進行簡要分析.首先有如下機器情況:
這裡使用wireshark來抓包分析,首先TurnClient發送Allocation請求:
可以看到第一次requst被服務器拒絕,因為後者要求nonce驗證信息,服務器的返回中包含了nonce信息,除此之外還包含了ERROR-CODE,SOFTWARE,FINGERPRINT屬性.
在下一次request請求中,客戶端加上了收到的nonce,以及USERNAME和REALM等屬性,再次發送到TurnServer:
服務器如果通過驗證,就會返回success response,隨後Client可以通過上文說到的兩種方法與Peer進行通訊,比如下面的Send indication方法:
如何用c++調用pjsip庫實現一個簡單的sip服務器
1、開源的sip服務器端,比較好用的是Asterisk,標準C程序實現,代碼清晰。
2、sip的client相對比較多,主要有exosip,pjsip和opal。exosip簡單易用,在PC上用比較方便。但是涉及的相關資源太多,用了osip,srtp,ms2等眾多的開源庫,ms2下面還用到了ffmpeg,別的不說,光編譯就是噩夢。opal功能最強,雖然也用到了ffmpeg ,但是自己封裝的非常好,採用插件方式,調用靈活。opal採用class方式提供封裝,接口非常友好。感覺唯一不爽的地方,就是低層使用了ptlib,雖然多平台下都很好用,但放在嵌入式下感覺稍龐大了一些。pjsip精巧,方便移植,嵌入式下應該是首選。不過視頻頻支持方面擴展起來比opal麻煩。個人感覺,對於windows開發者來說,pjsip最大的好處就是代碼調試方便。整個工程一次編譯通過,另外兩個庫還要找很多相關的資源
。
3、其他的一些協議棧也調試過,比如reSipphone,好象是這個名字,還有Yate,不過從快速開發角度看,都不太合適。現在搞sip開發的,一開始就是先找好協議棧。linphone,ekiga什麼的,但龐大。對於剛開始做的,最好是一個精簡的demo。後來找到pjsip下面的幾個例子,慢慢地了解了sip的工作流程,當然少不了抓包工具和tcpdump。
不過,其實,sip沒有想象中的那麼麻煩。現在回頭看,剛開始做項目,使用協議棧絕對不是好想法。如果換個方向,先熟悉SIP基本協議,然後自己改造一個,或完全寫一個,可能效果更好。
pjsip開發——sip日誌分析
這一節將通過一個簡單的例子來介紹一些基本的 SIP 操作。先讓我們來診視下圖展示的兩個用戶代理之間的消息順序。關於SIP協議詳細流程,可以參考 RFC3261-SIP協議 ,當然如果不習慣的話可以參考中文翻譯版 RFC3261-SIP協議中文版
收到180響應時建立dialog叫做早期對話(early dialog),收到2XX的應答開始才是真正的dialog建立。
當然,這個代理可以有多個代理,其實也就就是多了代理服務器之間的轉發過程,如下圖:
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/258052.html