高性能瀏覽器網絡(High Performance Browser Networking) 第三章

第3章UDP篇

1980年8月,用戶數據報協議(UDP)由John Postel添加到到核心網絡協議族中,UDP協議起始於TCP/IP協議以後,但和TCP和IP規範被分裂成爲兩個獨立的RFC的時間差很少。這個時機是很是重要的,由於正如咱們將看到的,UDP重要的特色不是他帶了什麼新特性,而是他忽略了的那些特性。UDP(RFC 768)是通俗稱爲空協議,它描述的操做,基本上能夠容納在一張餐巾紙上。html

數據報 一個自包含的,獨立的數據實體,其承載了足夠的信息,使其能夠從源路由到達目標路由,而不依賴於在網絡節點間和底層傳輸網絡中的前面數據包。算法

數據報文(Datagram)和數據包(Packet)兩個術語往交替使用,但其實兩者有一些細微差異。數據包(packet)通常用來描述任何格式的數據塊,而數據報(Datagram)每每被保留用來描述經過一個不可靠的服務傳輸的數據包(Packet) - 沒有傳輸保障,沒有失敗通知。正由於如此,你常常發現有人用不可靠(Unreliable)來替代UDP官方定義中的User,咱們能夠理解成「不可靠的數據報協議」。這也是爲何UDP包通常或者說更準確的被稱爲數據報(Datagram)。瀏覽器

UDP的一個最著名的也是全部瀏覽器和網絡應用都要依賴的應用就是DNS服務,任何一個Host名稱,咱們須要在數據交換以前獲取它的IP地址。不過,即便是瀏覽器自己依賴於UDP的,但UDP歷來沒有做爲網頁獲取和瀏覽器應用傳輸協議的第一選擇。固然,WebRTC的出現,狀況有所變化了。安全

新的Web實時通訊(WebRTC)標準,由IETF和W3C工做組共同制定,實現實時通訊,如語音和視頻呼叫,以及其餘形式的對等(P2P)通訊,其就是在瀏覽器中採用了UDP。WebRTC中,UDP是首選的傳輸協議。咱們將在第18章中深刻討論WebRTC ,但在此以前,咱們首先探討一下UDP協議的內部運做,瞭解一下WebRTC爲何選擇UDP協議。服務器

 

空協議服務

要了解UDP和爲何它一般被稱爲「空協議」,咱們首先須要瞭解一下互聯網協議(IP),它位於TCP和UDP協議層下面。網絡

IP層主要任務就是基於地址將數據報從源主機發送到目的主機。要作到這一點,消息都封裝在一個IP包( 圖3-1 ),標識源和目的地址,以及一些其餘路由參數。併發

咱們再次強調一下上面提到的數據報這個術語的含義:IP層提供了不可靠的數據傳輸,既沒有消息確認,也沒有丟失通知, IP層直接把這一層的不可靠性暴露給上層。若是一個數據報在傳輸過程當中由於某個路由節點擁塞,高負荷,或因其餘緣由丟失,那麼由IP上層的協議來檢測,恢復,並重傳數據 - 固然這是在上層有這個需求的時候!框架

 

 

 

圖3-1 IPv4報頭(20字節)性能

UDP協議在IP包的基礎上增長了新的報頭( 圖3-2 ),它只增長了四個額外的字段:源端口,目的端口,數據包長度,數據校驗消息。所以,當IP層傳送數據包到目的主機時,主機解開UDP數據包,經過目的端口識別目標應用程序,併發送消息。除此以外,再無其餘。優化

 

 

 

圖3-2 UDP報頭(8字節)

事實上,UDP報頭中的源端口和校驗字段都是可選字段。IP數據包中已經包含它本身的報頭校驗和,應用層徹底能夠選擇忽略UDP的校驗字段,這意味着UDP層全部的錯誤檢測和糾錯,能夠委託給上述應用層校驗。其核心,UDP只是在IP層之上提供了「應用層複用」特性,也就是嵌入了源和目標端口。考慮到這一點,咱們如今能夠總結UDP全部不能提供的服務:

無消息傳輸保證 

沒有確認,重發,或超時 

沒法保證按序傳輸 

沒有數據包的序列號,沒有從新排序,無線頭阻塞 

無鏈接狀態跟蹤 

沒有鏈接創建或關閉的狀態機 

沒有擁塞控制 

無內置的客戶端或網絡反饋機制

TCP是一個面向字節流的協議,可以經過多個數據包發送應用程序的消息數據,包內自己沒有任何明確的消息邊界。爲了實現這一目標,鏈接兩端都分配了鏈接狀態,而且數據包被排序,重發丟包,按順序發送。相反UDP數據報有明確的界限:每個數據報都被打包到一個IP包中,應用層讀到的每個UDP包都是完整的信息 - 數據包不能被分割。

UDP是一個簡單的,無狀態的協議,適合於引導上層的其餘應用層協議 - 幾乎全部的協議決策都留給它上面的應用層。然而,在你想實現本身的協議來取代TCP,你應該仔細考慮有關的複雜性,如UDP與其它層的交互(好比NAT穿越),以及網絡協議一些最佳實踐。沒有仔細的規劃和設計,設計一個新的協議不是一個好主意,最終也許實現成一個的簡陋的TCP版本。各類算法和TCP狀態機已通過幾十年的錘鍊和提高,並已採起幾十種機制來保證他的性能。

 

UDP和網絡地址轉換

很是不幸,IPv4地址只有32位長,它提供了最多42.9億的IP地址。1994年中旬(RFC 1631),IP網絡地址轉換(NAT)規範,做爲一個臨時的解決方案,被提出來解決IPv4地址枯竭的問題 - 在上世紀90年代初期,互聯網上的主機的數量開始成倍增加,咱們根本沒法爲每臺主機分配一個惟一的IP。

建議的IP重用的解決方案是在邊緣網絡中引入NAT設備,NAT將負責爲維護本地IP和端口的元組到一個或多個全球惟一的(公共)IP地址和端口的元組的映射關係(圖3 -3 )。NAT內部的本地IP地址空間能夠被許多不一樣的子網絡重用,從而解決地址耗盡的問題。

 

 

 

圖3-3 IP網絡地址轉換

不幸的也是常常發生的是,臨時方案最後老是變成最終方案。NAT設備不只僅用來解決IP地址枯竭的問題,他們也很快成爲一個無處不在的網絡部件,包括許多企業和家庭代理和路由器,安全設備,防火牆,和幾十個其餘的硬件和軟件設備都包含了NAT功能。NAT再也不是臨時方案了,它已經成爲互聯網基礎設施的一個組成部分。

 

 

保留的私人網絡地址範圍

互聯網編號分配機構(IANA),這是一個負責全球IP地址分配的機構,預留了三個著名的私人網絡段,常常用在NAT設備內部網絡:

 

表3-1. 保留的IP地址段

 

IP地址段

地址數量

10.0.0.0 - 10.255.255.255

16,777,216

172.16.0.0 - 172.31.255.255

1,048,576

192.168.0.0 - 192.168.255.255

65,536

你們應該熟悉上面全部或者部分地址段。基本狀況是,本地路由器給您的計算機分配上面某個IP地址段中的一個地址 - 那是你在內部網絡的私有IP地址,當與外部網絡通訊時,NAT將會作網絡地址轉換。

爲了不路由錯誤和混亂,公網主機不容許從上面任何這些保留的私有網絡範圍分配IP地址。

 

鏈接狀態超時

NAT轉換的關鍵問題,至少對UDP而言,是它必須保存數據傳輸的路由表。NAT依賴網絡鏈接狀態,而UDP剛好沒有 - 這是一個嚴重的不匹配,這也爲UDP傳輸問題的根源。此外,如今很普通的一個狀況就是NAT內網的設備有不少層,這隻會使問題進一步複雜化。

每一個TCP鏈接有一個明確的協議狀態機,開始三次握手,跟着開始數據傳輸,最後關閉鏈接,有一個完整的流程。基於這種流程,NAT能夠觀察到每一個鏈接狀態,並能夠根據須要建立和刪除的路由條目。而UDP,既沒有握手,也沒有鏈接終止,同時沒有任何狀態機來監控鏈接狀態。

經過UDP往外發送數據並不須要任何額外的工做,但請求的答覆卻須要NAT維護路由表,用來識別本地目標主機的IP和端口。所以,NAT必須保持每一個UDP流的路由表信息,由於UDP是無狀態的。

更糟的是,NAT須要知道何時清除路由記錄,但UDP沒有鏈接終止序列,任什麼時候候,兩端均可以中止發送數據包,不帶任何通知。爲了解決這個問題,UDP路由記錄有一個老化定時器。這個定時器到底多長?」基本上沒有明確的答案,而是取決於設備提供商,版本,配置等。所以,事實上長時間運行的UDP會話的最佳實踐之一就是引入雙向 keepalive報文,按期的重置路由上全部的NAT設備的老化計時器。

 

 

TCP超時和NAT

從技術原理上來講,在NAT設備沒有必要爲TCP鏈接提供超時老化機制。TCP協議有一個良好握手機制和終止序列包,NAT能夠清晰的根據這些信息來添加或者刪除路由記錄。

不幸的是,在實際應用中,許多NAT設備爲TCP提供了相似UDP的老化計時器。其結果是,在某些狀況下,TCP鏈接也須要雙向Keepalive報文。若是你的TCP鏈接忽然降低,也許就是NAT設備的老化機制惹的禍。

 

NAT穿越

不可預知的鏈接狀態管理是NAT的一個嚴重問題,但對於許多應用程序的一個更大的問題是根本沒法創建UDP鏈接。這對不少應用譬如P2P,如VoIP,遊戲,文件共享等來講更是如此。這些應用每每通訊雙方須要同時充當客戶端和服務端角色,使其能雙向通訊。

第一個問題是,在有NAT的場景下,內部客戶端不知道它的公網IP​​:它只知道它的內部IP地址,NAT設備對每個UDP數據包進行重寫,修改UDP包的源端口和地址,以及IP層的源IP地址。可是,若是客戶端將私有IP地址做爲應用層數據的一部分與外部網絡地址進行通訊,那麼鏈接將不可避免地失敗。所以,NAT這種「透明」的轉換就有問題了,應用程序必須先發現它的公網IP​​地址,若是它須要與外部網絡中的一個地址進行通訊。

然而,僅僅知道的本身的公網IP是沒法保證UDP傳輸成功的。任何數據包到達擁有公網IP的NAT設備後​​,也​​有一個目的端口,NAT路由表中必須有一個外網IP端口與內網地址和端口的映射記錄,數據才能真正達到目的地址。若是這個記錄不存在,那麼數據包被簡單地丟棄(圖3-4 )。NAT做爲一個簡單的包過濾器,它沒有辦法自動肯定內部路由映射關係,除非用戶經過端口轉發或者相似機制顯式在NAT上進行了登記。

 

 

 

圖3-4 因爲缺乏映射記錄,收到的包被丟棄

須要注意的是,上面描述的問題對於客戶端應用程序來講不是一個問題,客戶端從內部網絡中先發起鏈接,NAT天然會添加相應的路由。可是,對於那種須要主動接收鏈接(內網主機做爲服務器)的應用如P2P應用(如VoIP),遊戲終端,文件共享等等,就會碰到這個問題。

爲了解決這種UDP的穿越問題,各類穿越技術(STUN,TURN,ICE)被提出了,用於創建在 兩個內網主機之間創建端至端的鏈接。

 

STUN,TURN,ICE

STUN(RFC 5389)協議是一種容許主機應用程序發現網絡中的NAT設備,並藉助其來爲當前鏈接分配公網IP​​和端口元組(圖3-5 )的方案。要作到這一點,該協議須要藉助一個第三方部署在公網上的STUN服務器。

 

 

 

圖3-5 STUN查詢公網IP和端口

假設STUN服務器的IP地址是可知的(經過DNS發現,或經過手動指定的地址),應用程序首先發送綁定請求到STUN服務器。相應的,STUN服務器回覆一個響應,其中包含爲其分配的客戶端對外暴露的公網IP​​地址和端口。這個簡單的流程解決咱們了咱們前面討論中遇到的幾個問題:

 

  • 該程序經過該方式獲取了其公網IP和端口的元組,並使用這個信息,做爲其應用數據的一部分,就可以與對端進行通訊。
  • 向STUN服務器發送的請求,也同時在NAT上創建了路由映射記錄,這確保了對端的請求能夠準備達到內部網絡中的應用。
  • STUN協議定義了一個簡單的機制來保持NAT上的路由老化。

有了這個機制,兩端須要經過UDP進行通訊時,他們會先發送綁定請求到各自的STUN服務器,收到各自STUN服務器的響應,而後他們可使用各自分配的公共IP地址和端口進行數據交換了。

然而,在實際應用中,STUN是不足以處理全部的NAT的拓撲結構和網絡配置。此外,不幸的是,在某些狀況下,UDP可能會被防火牆或其餘一些網絡設備徹底阻止 - 這種許多企業網絡中不是一種罕見的情景。爲了解決這個問題,只要STUN失敗,咱們還可使用TURN協議(RFC 5766)做爲備用方案,它能夠運行在UDP上,還能夠將UDP轉換成TCP。

TRUN方案的關鍵就是中繼(relay)。該協議依賴於公網上的中繼來保證私網主機的可見性和可用性( 圖3-6 )。

 

 

 

圖3-6 TURN中繼服務器

 

  • 兩端都向相同的TURN服務器發送地址分配請求,其次是權限協商。
  • 一旦協商完成後,兩端經過將數據發送到TURN服務器,並由TURN進行轉發到對端的方式進行互相通訊。

固然,這種通訊方式的最明顯的缺點就是他再也不是P2P的通訊。他須要依賴於TURN服務器來保證可靠的傳輸,TURN服務器成爲一個瓶頸,維護TURN的成本將很高,至少TURN服務器須要足夠的帶寬來保證全部的數據流。所以,TURN方案最好做爲最後的備用方案,只有在其餘方案都失效的狀況下才能使用。

 

 

STUN和TURN實踐

google提供的Libjingle,是一個開放源碼的C + +庫,能夠用它來建立P2P的應用,它在底層實現了STUN,TURN,ICE等協商。這個庫用在Google Talk中,庫文檔爲STUN與 TURN在現實世界中的性能提供了有價值的參考點:

 

  • 92%的時間能夠直接鏈接方案(STUN)
  • 8%的時間鏈接須要一箇中轉器(TURN)

不幸的是,即便採用STUN方案,有部分用戶仍是沒法創建直接的P2P隧道。爲了提供可靠的服務,咱們還須要TURN中繼,它能夠做爲STUN方案不可用狀況下的一個備選方案。

創建一個有效的NAT穿越解決方案,不是一件簡單容易的事情。值得慶幸的是,咱們能夠藉助ICE協議(RFC 5245)來幫助咱們完成這一任務。ICE是一個協議,和一組方法,用來尋求最有效的端與端之間(圖3-7 )隧道創建方法:若是可能則直接鏈接,若是不行則經過STUN進行協商,若是都失敗了則採起TURN。

 

 

 

圖3-7 ICE試圖經過直接鏈接,STUN和TURN創建鏈接

在實踐中,若是你正在建設一個基於UDP的P2P應用程序,那麼你最但願利用現有的平臺API或第三方的庫,爲您實現ICE,STUN和TURN。如今你應該瞭解了這些協議,如今你能夠跳轉到相應的安裝和配置去實現你的方案了!

 

UDP優化

UDP是一種簡單而經常使用的協議。事實上,UDP的主要特徵是它忽略了的功能:無鏈接狀態,握手,重發,重組,從新排序,擁塞控制,擁塞避免,流量控制,甚至可選的錯誤檢查。然而,這個面向消息的傳輸層能提供的靈活性,也是實現者的責任。您的應用程序可能從頭開始從新實現部分或者許多缺失的特性,每個特性都應該需對端或者應用協議匹配。

與TCP不一樣,內置了流量和擁塞控制、擁塞避免機制,UDP應用程序必須本身實現這些機制。擁塞不敏感的UDP應用程序能夠很容易的擁塞網絡,可能會致使網絡性能下降,在嚴重的狀況下,會致使網絡擁塞崩潰。

若是你想在本身的應用程序中使用UDP,確保研究和閱讀當前的最佳實踐和建議。在RFC 5405中,特別強調了應用程序經過單播UDP傳送數據設計指南。下面是一個簡短的例子:

 

  • 應用必須忍受變化的互聯網路徑
  • 應用應控制傳輸速率
  • 應用應當實現全部流量擁塞控制
  • 應用應該使用和TCP同等的帶寬
  • 應用當丟包時應該回退重傳計數器
  • 應用不該該發送超過MTU的數據報
  • 應用應該處理數據報的丟失,重複,從新排序
  • 應用應該是確保能夠支持兩分鐘的延遲
  • 應用應該啓用IPv4 UDP校驗,必須啓用IPv6校驗
  • 應用可能在須要的時候使用保活(最小間隔15秒)

設計一個新的傳輸協議,須要不少的認真思考,規劃和研究 - 作你的盡職調查。在可能的狀況下,充分利用現有的庫或已經採用了一個現有框架來實現NAT穿越,使其可以與其餘來源的網絡創建某種程度的公平通訊。

關於這一點,好消息, WebRTC就是這樣一個框架!

相關文章
相關標籤/搜索