深入淺出 WebRTC 傳輸協議

RTC 是 Real-Time Communication 的簡寫,正如其中文名稱 “即時通訊” 的意思一樣,RTC 協議被廣泛用於各種即時通訊領域,諸如:

WebRTC 則是 Google 基於 RTC 協議實現的一個開源項目,爲 Web 頁面提供了實時音視頻傳輸所需的能力(前端部分);

RTC 有一個非常重要的特性,它是一個支持點對點直接傳輸的 P2P 協議;

P2P

P2P 是 “Peer to Peer” 的簡寫,在金融領域大家應該都聽過這個名詞(P2P 暴雷),金融中 P2P 可以指代 “個人對個人的網絡放貸”;在互聯網中也有着類似的意思,表示數據 “點對點傳輸” ,指數據可以在兩個互聯網用戶之間直接傳輸,無需服務器在中間進行轉發;

舉個例子:IM 聊天軟件中有 A、B 兩個正在聊天的用戶,聊天過程中,用戶 A 的文字信息是沒辦法直接通過網絡發送給用戶 B 的,而是需要一個服務器 S 在中間做轉發,“A -> S -> B”;但採用 RTC 協議的視頻電話就不一樣了,電話的音視頻數據可以通過網絡直接在兩個用戶之間傳輸,無需中間服務器進行轉發:“A -> B”;

採用 RTC 協議能帶來兩個非常大的優勢:

NAT “牆”

從上文知道,RTC 是一個 P2P 協議,數據可以直接在用戶之間傳輸;但現實往往比理論來的複雜,實際用戶的網絡環境並沒有那麼簡單,如果不做一些特殊處理,數據很大概率無法在兩個用戶之間傳輸,之所以無法直接傳輸,爲了大家更好的理解需要從頭說起;

隨着互聯網的用戶逐年增多,接入互聯網的設備也越來越多,公網 IPv4 的地址池慢慢見底,新接入互聯網的設備很難再分配到單獨公網的 IPv4 地址,爲了解決這個問題,引入了一個叫 NAT(Network address translation)的協議;新接入的設備不再直接分配公網的 IPv4 地址,而是躲在 NAT 設備(路由器等)之後,NAT 會給後面的每一個設備都分配一個單獨的內網地址,NAT 內部將維護一個內外網的地址映射表格,舉個例子:

NAT 設備會修改發出去和收到的數據包,比如把上面 “192.168.0.1:8088” 發出去的包改成 “220.181.38.149:1111” ,這樣外部的設備就會以爲自己是在跟 “220.181.38.149:1111” 通信,接收到響應包之後,NAT 會把目標地址 “220.181.38.149:1111” 修改爲 “192.168.0.1:8088”,隨後轉發到內網;這樣就實現了一個公網的 IPv4 地址給多個設備共同使用的效果;

NAT 在實現上述功能之後,爲了內網設備不被攻擊,還使用了兩個安全策略:

根據 NAT 牆策略的不同,最常見的 NAT 可以分爲四種:

  1. Full Cone NAT(完全錐形):表示映射表中所有的地址,外網設備都可以直接訪問到,是最寬鬆的策略了;

  2. Restricted Cone NAT(IP 限制錐形):沒被訪問過的外網地址發的數據包都會被丟棄,用上面的例子來解釋,假如 “192.168.0.1:8088” 訪問過外網 “103.15.99.89:80” 這個地址,那外網 “103.15.99.89” 這個 IP 的所有端口都將可以訪問通內網,但其他外網地址的訪問會被阻止;

  3. Port Restricted Cone NAT(端口限制錐形):類似 2,不過除了限制外網的 IP 地址外還會限制端口;

  4. Symmetric NAT(對稱形):這種 NAT 的丟包策略和 3 一樣,只要外網的 IP 和端口有一個沒被訪問過,數據包就會被丟棄;但是該類型的 NAT 內外網地址映射的策略不一樣,對稱型 NAT 不會直接給一個內網設備分配固定的 IP 和端口,而是根據訪問的外網地址分配不同的 IP 和端口;舉個例子,假設內網設備 A 訪問外網 B 時的映射爲 “192.168.0.1:8088 <--> 220.181.38.149:1111”,那麼內網 A 訪問外網 C 時的映射可能會變成 “192.168.0.1:8088 <--> 220.181.38.149:2222”;

爲什麼要叫錐形和對稱形?

ICE -- NAT “打洞”

知道 NAT 的存在之後,再舉一個例子🌰:用戶 A 知道用戶 B 的網絡地址,並且 A 和 B 在不同的 NAT 之後;某一時刻 A 想主動聯繫 B,然後 A 經過自己 NAT 發一個請求給 B,請求到達 B 的 NAT 時,因爲 B 沒聯繫過 A,所以 B 的 NAT 便會將 A 的請求丟棄;

上面簡單的例子就可以看出,雖然 RTC 是一個 P2P 的協議,但因爲 NAT 牆的存在,就算通訊的雙方知道對方的網絡地址,也沒辦法直接溝通......

所以,需要引入一個機制對這個溝通過程進行協調,幫助通訊雙方能夠越過 NAT 併成功建立連接,這套機制就是 ICE(Interactive Connectivity Establishment),ICE 是一個框架協議,可以讓互聯網中兩個設備進行點對點的連接,ICE 框架包含的兩個主要工具協議:

STUN

STUN(Session Traversal Utilities for NAT)是一個工具協議,這個協議的主要目的是協調幫助兩個在 NAT 之後的設備建立 UDP (也可以是 TCP)傳輸;既然 STUN 是一個協議,那我們就可以採用任意技術棧來開發實現一個 STUN 服務及 STUN 客戶端,實現的 STUN 主要有兩個作用:

  1. 幫助獲取客戶端的公網地址,並通過複雜的策略,探測出客戶端所處的 NAT 類型;

  2. STUN 還可以幫助兩個客戶端之間進行 NAT “打洞” 或者協調 TURN 在兩個客戶端中間充當中繼服務;

NAT 探測

服務端可以非常輕鬆的在數據包中獲取請求的來源 IP 和端口,但是並沒有辦法知道對應的請求是客戶端直髮還是 NAT 轉發的,更沒辦法知道是哪種類型的 NAT,客戶端也一樣無法知道自己的 NAT 情況;但只要 STUN 客戶端及 STUN 服務齊心協力,就可以一步步探測出 NAT 情況;

STUN 服務和 STUN 客戶端會按照下面的邏輯進行配合,一步步探測客戶端所處的 NAT 情況;

ps:一個 STUN 服務需要擁有兩個 IP ,通過兩個 IP 的服務互相配合來探測 NAT 的情況

  1. 第一步,判斷是否存在 NAT,客戶端主動發一個請求到 STUN 服務的 “IP1 端口 1” 上,STUN 服務把收到的請求的 IP 和端口直接返回給客戶端,客戶端會將 STUN 服務返回的 IP 和端口跟自己的 IP 和端口進行比較,

  2. 如果一致,則表明客戶端處在公網中,或者說客戶端沒有在 NAT 之後;

  3. 如果不一致,則表明客戶端處在 NAT 之後,需要往下走繼續探測 NAT 類型;

  4. 第二步,判斷 NAT 是不是 Full Cone NAT(完全錐形),客戶端發送請求到 STUN 服務的 “IP1 端口 1”,STUN 服務收到請求之後用 “IP2 端口 2” 主動往客戶端發送一個請求,

  5. 如果客戶端能夠收到 STUN 服務 IP2 的請求,則表明 NAT 策略非常寬鬆來者不拒,是完全錐形;

  6. 如果客戶端沒辦法收到 STUN 服務 IP2 的請求,則數據包被 NAT 丟棄了,NAT 不是完全錐形,需要往下走繼續探測 NAT 類型;

  7. 第三步,判斷 NAT 是不是 Symmetric NAT(對稱形),客戶端主動往 STUN 服務的 “IP2 端口 2” 發送請求,STUN 服務收到請求之後把請求的來源 IP 和端口直接返回給客戶端,客戶端用收到的 IP 和端口跟 “第一步” 中的 IP 和端口進行比較,

  8. 如果兩次收到的端口不一致,則表明 NAT 是對稱形的;

  9. 如果一致,則表明 NAT 不是對稱形的,需要進一步探測 NAT 類型;

  10. 第四步,判斷 NAT 對端口的限制,客戶端主動往 STUN 服務的 “IP2 端口 2” 發請求,要求 STUN 服務用 “IP2 端口 3” 往客戶端發請求,

  11. 如果客戶端收到了 “IP2 端口 3” 的請求,則表明 NAT 沒有對端口進行限制,是 Restricted Cone NAT(IP 限制錐形);

  12. 如果沒收到請求,則表明 NAT 限制了端口,是 Port Restricted Cone NAT(端口限制錐形);

NAT “打洞”

經過上面四個步驟之後,便知道了客戶端的公網地址以及所在的 NAT 情況,光知道 NAT 情況還沒用,NAT 依舊會對請求進行攔截,STUN 還需要協調兩個客戶端對各自的 NAT 進行打洞,客戶端才能穿越 NAT 完成連接建立,下面從簡單到複雜舉幾個例子來說明 NAT 的打洞流程;

只有一方在 NAT 後

假設:客戶端 A 和客戶端 B 需要建立 P2P 連接,客戶端 A 直接擁有一個公網 IP,而客戶端 B 在 NAT 之後;

這種情況下如果客戶端 A 直接與客戶端 B 通信,通信將會失敗,客戶端 A 發送的數據包會被客戶端 B 的 NAT 丟棄;此時,STUN 服務端便會進行協調,讓客戶端 B 主動先往客戶端 A 發送數據包,客戶端 B 的 NAT 便記錄了客戶端 A 的通信記錄,客戶端 A 後續便可以與客戶端 B 進行通信了;

“客戶端 B 主動連接客戶端 A” 這個過程就被形象的稱爲 “給客戶端 B 的 NAT 打洞”;

雙方都在非對稱形 NAT 後

假設:客戶端 A 和客戶端 B 需要建立 P2P 連接,客戶端 A 和客戶端 B 在不同的 NAT 之後;

這種情況下客戶端 A 和客戶端 B 往對方發送的數據都會被 NAT 丟棄,STUN 服務便會協調兩個客戶端,讓它們先主動都往對方發送數據,在自己的 NAT 上留下對方的 “洞”,後續兩個客戶端便可以完成連接的建立了;

雙方在對稱形 NAT 後

假設:客戶端 A 和 B 的 NAT 均爲 Symmetric NAT(對稱形);

這種情況下,先說結論,STUN 服務將無法協調客戶端 B 的 NAT 打洞;

由於對稱形 NAT 的特性,STUN 服務端看到的客戶端 A “ip 、 端口”,將會和客戶端 B 看到的客戶端 A 的 “ip、 端口” 不一樣,此時如果 STUN 服務強行協調客戶端 B 給 NAT 進行打洞,打的洞客戶端 A 並沒辦法使用;

所以這種情況下是沒有辦法建立 P2P 連接的,也因爲這種情況的存在,才引入了 TURN 中繼協議;

TURN

TURN 全稱 “Traversal Using Relays around NAT(TURN):Relay Extensions to Session Traversal Utilities for NAT(STUN)” ,從全稱就可以看出,TURN 是 STUN 的一個補充協議,當 STUN 無法完成兩個客戶端的 P2P 直連時,TURN 便會充當一個 “中繼服務器” 的角色,對兩個客戶端之間的信息進行轉發;

TURN 服務功能示意圖

如何快速判斷是否能打洞?

給 NAT 類型進行一個排序,從寬鬆到嚴格的順序如下:

  1. Full Cone NAT(完全錐形)

  2. Restricted Cone NAT(IP 限制錐形)

  3. Port Restricted Cone NAT(端口限制錐形)

  4. Symmetric NAT(對稱形)

如果兩個客戶端的 NAT 類型的序號相加大於等於 7 ,則無法打洞成功,需要引入 TURN 服務;舉個例子,如果兩個客戶端分別是 “4. Symmetric NAT(對稱形)” 和 “2. Restricted Cone NAT(IP 限制錐形)”,則這兩個客戶端能打洞成功,因爲他們的序號相加爲 6 ,小於 7;

SDP

SDP 全稱 “Session Description Protocol” 會話描述協議,雖然名字上看起來是一個協議,但本質上只是一種數據格式,這種數據格式可以用來讓 RTC 通信雙方互相交換信息, SDP 數據格式如下圖所示:

Session description(會話級別描述) 
    v= (protocol version) 
    o= (originator and session identifier)
    s= (session name) 
    c=(connection information -- not required if included in all media) One or more Time descriptions ("t=" and "r=" lines; see below) 
    a=(zero or more session attribute lines) Zero or more Media descriptions 
    
Time description 
    t= (time the session is active) 

Media description(媒體級別描述), if present 
    m= (media name and transport address) 
    c=(connection information -- optional if included at session level) 
    a=(zero or more media attribute lines)

教室中一個實際的 SDP

v=0
o=- 6307461294211741498 2 IN IP4 127.0.0.1
s=-
t=0
a=group:BUNDLE 0 1
a=msid-semantic: WMS
m=audio 9 UDP/TLS/RTP/SAVPF 111 63 103 104 9 0 8 106 105 13 110 112 113 126
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:USPp
a=ice-pwd:qDZxlC3pdC2JJLUhEGDTh71+
a=ice-options:trickle
m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 122 102 121 127 120 125 107 108 109 124 119 123 117 35 36 114 115 116 62 118 37
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:USPp
a=ice-pwd:qDZxlC3pdC2JJLUhEGDTh71+
a=ice-options:trickle
a=rtpmap:102 H264/90000
a=fingerprint:sha-256 AE:C2:C3:BC:8E:87:E1:32:3F:D4:C1:C4:B4:78:0A:10:03:E1:02:9D:2D:8C:8A:E5:DC:D0:60:A4:0A:3B:C6:C3
a=setup:actpass
a=mid:1
a=extmap:3 urn:ietf:params:rtp-hdrext:toffset
a=extmap:4 urn:3gpp:video-orientation

鏈接建立完整流程

RTP

經過面的流程,我們已經把 “傳輸層” 打通,最常見的傳輸層協議有 TCP 和 UDP ,應該用 TCP 還是 UDP 來傳輸音視頻呢?

1vh9Yx

總的來說,UDP 效率更高,TCP 更穩定,對於即時通訊來說,音視頻丟失一兩幀的影響是可以接受的,但是高延遲會帶來非常差的用戶體驗,所以大部分實時音視頻的傳輸都會選擇 UDP 作爲傳輸層協議;

視頻一幀的數據需要拆成多個 UDP 數據包進行傳遞,UDP 並不具備視頻幀拆分及組裝的能力,所以還需要在其之上構建一個應用層協議來完成音視頻傳輸;

RTP (Real-time Transport Protocol)是一個基於 UDP 的應用層協議,包含兩個子協議:

總結

RTC 雖然是一個 P2P 的協議,但是由於 NAT 的存在,需要引入 ICE 協議進行網絡穿透,才能在兩個設備之間建立 UDP 連接,連接建立完成之後,會採用 RTP 作爲應用層協議保證音視頻的正常傳輸。

以上便是本次分享的全部內容,希望對你有所幫助 ^_^

歡迎關注公衆號 ELab 團隊 收貨大廠一手好文章~

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/XMhSDABc74dpALIHrPPt7w