默認
打賞 發表評論 7
想開發IM:買成品怕坑?租第3方怕貴?找開源自已擼?盡量別走彎路了... 找站長給點建議
不為人知的網絡編程(六):深入地理解UDP協議并用好它
閱讀(25161) | 評論(7 收藏5 淘帖1

原作者:黃日成,手Q游戲中心后臺開發,騰訊高級工程師。從事C++服務后臺開發4年多,主要負責手Q游戲中心后臺基礎系統、復雜業務系統開發,主導過手Q游戲公會、企鵝電競App-對戰系統等項目的后臺系統設計,有豐富的后臺架構經驗。


1、前言


很早就計劃寫篇關于UDP的文章,盡管UDP協議遠沒TCP協議那么龐大、復雜,但是要想將UDP描述清楚,用好UDP卻要比TCP難不少,于是文章從下筆寫到最終寫成,斷斷續續拖了好幾個月。

本文接系列文章的上篇《不為人知的網絡編程(五):UDP的連接性和負載均衡》,將從實踐出發,討論如何深入地理解UDP協議并在實踐中用好它。

注意:本文中涉及到的具體代碼、函數都是以Linux C++為例來講解,如果您對Linux C++不太了解的話也沒有關系,把它們當偽碼看即可,必竟具體的語言實現并不妨礙問題解決思路的表達。

2、系列文章


本文是系列文章中的第6篇,本系列文章的大綱如下:


如果您覺得本系列文章過于專業,您可先閱讀《網絡編程懶人入門》系列文章,該系列目錄如下:


本站的《腦殘式網絡編程入門》也適合入門學習,本系列大綱如下:


關于移動端網絡特性及優化手段的總結性文章請見:


3、參考資料


TCP/IP詳解 - 第11章·UDP:用戶數據報協議
為什么QQ用的是UDP協議而不是TCP協議?
移動端IM/推送系統的協議選型:UDP還是TCP?
簡述傳輸層協議TCP和UDP的區別
UDP中一個包的大小最大能多大
為什么說基于TCP的移動端IM仍然需要心跳保活?

4、UDP的疑難雜癥


4.1UDP的傳輸方式:面向報文


面向報文的傳輸方式決定了 UDP 的數據發送方式是一份一份的,也就是應用層交給 UDP 多長的報文,UDP 就照樣發送,即一次發送一個報文。那么UDP的報文大小由哪些影響因素呢? UDP 數據包的理論長度是多少,合適的 UDP 數據包應該是多少呢?

(1) UDP 報文大小的影響因素,主要有以下3個:

  • [1] UDP協議本身,UDP協議中有16位的UDP報文長度,那么UDP報文長度不能超過2^16=65536;
  • [2] 以太網(Ethernet)數據幀的長度,數據鏈路層的MTU(最大傳輸單元);
  • [3] socket的UDP發送緩存區大小。

(2) UDP數據包最大長度:

根據 UDP 協議,從 UDP 數據包的包頭可以看出,UDP 的最大包長度是2^16-1的個字節。由于UDP包頭占8個字節,而在IP層進行封裝后的IP包頭占去20字節,所以這個是UDP數據包的最大理論長度是2^16 - 1 - 8 - 20 = 65507字節。如果發送的數據包超過65507字節,send或sendto函數會錯誤碼1(Operation not permitted, Message too long),當然啦,一個數據包能否發送65507字節,還和UDP發送緩沖區大小(linux下UDP發送緩沖區大小為:cat /proc/sys/net/core/wmem_default)相關,如果發送緩沖區小于65507字節,在發送一個數據包為65507字節的時候,send或sendto函數會錯誤碼1(Operation not permitted, No buffer space available)。

(3) UDP數據包理想長度:

理論上 UDP 報文最大長度是65507字節,實際上發送這么大的數據包效果最好嗎?我們知道UDP是不可靠的傳輸協議,為了減少 UDP 包丟失的風險,我們最好能控制 UDP 包在下層協議的傳輸過程中不要被切割。相信大家都知道MTU這個概念。 MTU 最大傳輸單元,這個最大傳輸單元實際上和鏈路層協議有著密切的關系,EthernetII 幀的結構 DMAC + SMAC + Type + Data + CRC 由于以太網傳輸電氣方面的限制,每個以太網幀都有最小的大小64字節,最大不能超過1518字節,對于小于或者大于這個限制的以太網幀我們都可以視之為錯誤的數據幀,一般的以太網轉發設備會丟棄這些數據幀。由于以太網 EthernetII 最大的數據幀是1518字節,除去以太網幀的幀頭(DMAC目的 MAC 地址48bit=6Bytes+SMAC源 MAC 地址48bit=6Bytes+Type域2bytes)14Bytes和幀尾CRC校驗部分4Bytes那么剩下承載上層協議的地方也就是Data域最大就只能有1500字節這個值我們就把它稱之為MTU。

在下層數據鏈路層最大傳輸單元是1500字節的情況下,要想IP層不分包,那么UDP數據包的最大大小應該是1500字節 – IP頭(20字節) – UDP頭(8字節) = 1472字節。不過鑒于Internet上的標準MTU值為576字節,所以建議在進行Internet的UDP編程時,最好將UDP的數據長度控制在 (576-8-20)548字節以內論壇里的另一篇文章也深入地討論了這個問題,詳情請見《UDP中一個包的大小最大能多大)。

4.2UDP數據包的發送和接收問題


(1) UDP的通信有界性:

在阻塞模式下,UDP的通信是以數據包作為界限的,即使server端的緩沖區再大也要按照client發包的次數來多次接收數據包,server只能一次一次的接收,client發送多少次,server就需接收多少次,即客戶端分幾次發送過來,服務端就必須按幾次接收。

(2) UDP數據包的無序性和非可靠性:

client依次發送1、2、3三個UDP數據包,server端先后調用3次接收函數,可能會依次收到3、2、1次序的數據包,收包可能是1、2、3的任意排列組合,也可能丟失一個或多個數據包。

(3) UDP數據包的接收:

client發送兩次UDP數據,第一次 500字節,第二次300字節,server端阻塞模式下接包,第一次recvfrom( 1000 ),收到是 1000,還是500,還是300,還是其他?

由于UDP通信的有界性,接收到只能是500或300,又由于UDP的無序性和非可靠性,接收到可能是300,也可能是500,也可能一直阻塞在recvfrom調用上,直到超時返回(也就是什么也收不到)。

在假定數據包是不丟失并且是按照發送順序按序到達的情況下,server端阻塞模式下接包,先后三次調用:recvfrom( 200),recvfrom( 1000),recvfrom( 1000),接收情況如何呢?

由于UDP通信的有界性,第一次recvfrom( 200)將接收第一個500字節的數據包,但是因為用戶空間buf只有200字節,于是只會返回前面200字節,剩下300字節將丟棄。第二次recvfrom( 1000)將返回300字節,第三次recvfrom( 1000)將會阻塞。

(4) UDP包分片問題:

如果MTU是1500,Client發送一個8000字節大小的UDP包,那么Server端阻塞模式下接包,在不丟包的情況下,recvfrom(9000)是收到1500,還是8000。如果某個IP分片丟失了,recvfrom(9000),又返回什么呢?

根據UDP通信的有界性,在buf足夠大的情況下,接收到的一定是一個完整的數據包,UDP數據在下層的分片和組片問題由IP層來處理,提交到UDP傳輸層一定是一個完整的UDP包,那么recvfrom(9000)將返回8000。如果某個IP分片丟失,udp里有個CRC檢驗,如果包不完整就會丟棄,也不會通知是否接收成功,所以UDP是不可靠的傳輸協議,那么recvfrom(9000)將阻塞。

4.3UDP丟包問題


在不考慮UDP下層IP層的分片丟失,CRC檢驗包不完整的情況下,造成UDP丟包的因素有哪些呢?

[1] UDP socket緩沖區滿造成的UDP丟包:

通過 cat /proc/sys/net/core/rmem_default 和cat /proc/sys/net/core/rmem_max可以查看socket緩沖區的缺省值和最大值。如果socket緩沖區滿了,應用程序沒來得及處理在緩沖區中的UDP包,那么后續來的UDP包會被內核丟棄,造成丟包。在socket緩沖區滿造成丟包的情況下,可以通過增大緩沖區的方法來緩解UDP丟包問題。但是,如果服務已經過載了,簡單的增大緩沖區并不能解決問題,反而會造成滾雪球效應,造成請求全部超時,服務不可用。

[2] UDP socket緩沖區過小造成的UDP丟包:

如果Client發送的UDP報文很大,而socket緩沖區過小無法容下該UDP報文,那么該報文就會丟失。

[3] ARP緩存過期導致UDP丟包:

ARP 的緩存時間約10分鐘,APR 緩存列表沒有對方的 MAC 地址或緩存過期的時候,會發送 ARP 請求獲取 MAC 地址,在沒有獲取到 MAC 地址之前,用戶發送出去的 UDP 數據包會被內核緩存到 arp_queue 這個隊列中,默認最多緩存3個包,多余的 UDP 包會被丟棄。被丟棄的 UDP 包可以從 /proc/net/stat/arp_cache 的最后一列的 unresolved_discards 看到。當然我們可以通過 echo 30 > /proc/sys/net/ipv4/neigh/eth1/unres_qlen 來增大可以緩存的 UDP 包。

UDP 的丟包信息可以從 cat /proc/net/udp 的最后一列drops中得到,而倒數第四列 inode 是丟失 UDP 數據包的 socket 的全局唯一的虛擬i節點號,可以通過這個 inode 號結合 lsof ( lsof -P -n | grep 25445445)來查到具體的進程。

4.4UDP的冗余傳輸方案


在外網通信鏈路不穩定的情況下,有什么辦法可以降低UDP的丟包率呢?一個簡單的辦法來采用冗余傳輸的方式。如下圖,一般采用較多的是延時雙發,雙發指的是將原本單發的前后連續的兩個包合并成一個大包發送,這樣發送的數據量是原來的兩倍。這種方式提高丟包率的原理比較簡單,例如本例的冗余發包方式,在偶數包全丟的情況下,依然能夠還原出完整的數據,也就是在這種情況下,50%的丟包率,依然能夠達到100%的數據接收。

不為人知的網絡編程(六):深入地理解UDP協議并用好它_11.jpg

5、UDP真的比TCP要高效嗎


相信很多同學都認為UDP無連接,無需重傳和處理確認,UDP比較高效。然而UDP在大多情況下并不一定比TCP高效,TCP發展至今天,為了適應各種復雜的網絡環境,其算法已經非常豐富,協議本身經過了很多優化,如果能夠合理配置TCP的各種參數選項,那么在多數的網絡環境下TCP是要比UDP更高效的。

影響UDP高效因素有以下3點。

(1) 無法智能利用空閑帶寬導致資源利用率低:
一個簡單的事實是UDP并不會受到MTU的影響,MTU只會影響下層的IP分片,對此UDP一無所知。在極端情況下,UDP每次都是發小包,包是MTU的幾百分之一,這樣就造成UDP包的有效數據占比較小(UDP頭的封裝成本);或者,UDP每次都是發巨大的UDP包,包大小MTU的幾百倍,這樣會造成下層IP層的大量分片,大量分片的情況下,其中某個分片丟失了,就會導致整個UDP包的無效。由于網絡情況是動態變化的,UDP無法根據變化進行調整,發包過大或過小,從而導致帶寬利用率低下,有效吞吐量較低。而TCP有一套智能算法,當發現數據必須積攢的時候,就說明此時不積攢也不行,TCP的復雜算法會在延遲和吞吐量之間達到一個很好的平衡。

(2) 無法動態調整發包:
由于UDP沒有確認機制,沒有流量控制和擁塞控制,這樣在網絡出現擁塞或通信兩端處理能力不匹配的時候,UDP并不會進行調整發送速率,從而導致大量丟包。在丟包的時候,不合理的簡單重傳策略會導致重傳風暴,進一步加劇網絡的擁塞,從而導致丟包率雪上加霜。更加嚴重的是,UDP的無秩序性和自私性,一個瘋狂的UDP程序可能會導致這個網絡的擁塞,擠壓其他程序的流量帶寬,導致所有業務質量都下降。

(3) 改進UDP的成本較高:
可能有同學想到針對UDP的一些缺點,在用戶態做些調整改進,添加上簡單的重傳和動態發包大小優化。然而,這樣的改進并比簡單的,UDP編程可是比TCP要難不少的,考慮到改造成本,為什么不直接用TCP呢?當然可以拿開源的一些實現來抄一下(例如:libjingle),或者擁抱一下Google的QUIC協議,然而,這些都需要不少成本的。

上面說了這么多,難道真的不該用UDP了嗎?其實也不是的,在某些場景下,我們還是必須UDP才行的。那么UDP的較為合適的使用場景是哪些呢?

6、UDP協議的正確使用場合


6.1高通信實時性要求和低持續性要求的場景下


在分組交換通信當中,協議棧的成本主要表現在以下兩方面:

  • [1] 封裝帶來的空間復雜度;
  • [2] 緩存帶來的時間復雜度。

以上兩者是對立影響的,如果想減少封裝消耗,那么就必須緩存用戶數據到一定量在一次性封裝發送出去,這樣每個協議包的有效載荷將達到最大化,這無疑是節省了帶寬空間,帶寬利用率較高,但是延時增大了。如果想降低延時,那么就需要將用戶數據立馬封裝發出去,這樣顯然會造成消耗更多的協議頭等消耗,浪費帶寬空間。

因此,我們進行協議選擇的時候,需要重點考慮一下空間復雜度和時間復雜度間的平衡。

通信的持續性對兩者的影響比較大,根據通信的持續性有兩種通信類型:

  • [1] 短連接通信;
  • [2] 長連接通信。

對于短連接通信,一方面如果業務只需要發一兩個包并且對丟包有一定的容忍度,同時業務自己有簡單的輪詢或重復機制,那么采用UDP會較為好些。在這樣的場景下,如果用TCP,僅僅握手就需要3個包,這樣顯然有點不劃算,一個典型的例子是DNS查詢。另一方面,如果業務實時性要求非常高,并且不能忍受重傳,那么首先就是UDP了或者只能用UDP了,例如NTP 協議,重傳NTP消息純屬添亂(為什么呢?重傳一個過期的時間包過來,還不如發一個新的UDP包同步新的時間過來)。如果NTP協議采用TCP,撇開握手消耗較多數據包交互的問題,由于TCP受Nagel算法等影響,用戶數據會在一定情況下會被內核緩存延后發送出去,這樣時間同步就會出現比較大的偏差,協議將不可用。

6.2多點通信的場景下


對于一些多點通信的場景,如果采用有連接的TCP,那么就需要和多個通信節點建立其雙向連接,然后有時在NAT環境下,兩個通信節點建立其直接的TCP連接不是一個容易的事情,在涉及NAT穿越的時候,UDP協議的無連接性使得穿透成功率更高(原因詳見:由于UDP的無連接性,那么其完全可以向一個組播地址發送數據或者輪轉地向多個目的地持續發送相同的數據,從而更為容易實現多點通信。

一個典型的場景是多人實時音視頻通信,這種場景下實時性要求比較高,可以容忍一定的丟包率。比如:對于音頻,對端連續發送p1、p2、p3三個包,另一端收到了p1和p3,在沒收到p2的保持p1的最后一個音(也是為什么有時候網絡丟包就會聽到嗞嗞嗞嗞嗞嗞…或者卟卟卟卟卟卟卟卟…重音的原因),等到到p3就接著播p3了,不需要也不能補幀,一補就越來越大的延時。對于這樣的場景就比較合適用UDP了,如果采用TCP,那么在出現丟包的時候,就可能會出現比較大的延時。

6.3UDP的使用原則小結


通常情況下,UDP的使用范圍是較小的,在以下的場景下,使用UDP才是明智的。

  • [1] 實時性要求很高,并且幾乎不能容忍重傳:
    例子:NTP協議,實時音視頻通信,多人動作類游戲中人物動作、位置。
  • [2] TCP實在不方便實現多點傳輸的情況;
  • [3] 需要進行NAT穿越;
  • [4] 對網絡狀態很熟悉,確保udp網絡中沒有氓流行為,瘋狂搶帶寬;
  • [5] 熟悉UDP編程。

(原文鏈接:點此進入

7、更多資料


TCP/IP詳解 - 第11章·UDP:用戶數據報協議
TCP/IP詳解 - 第17章·TCP:傳輸控制協議
TCP/IP詳解 - 第18章·TCP連接的建立與終止
TCP/IP詳解 - 第21章·TCP的超時與重傳
技術往事:改變世界的TCP/IP協議(珍貴多圖、手機慎點)
通俗易懂-深入理解TCP協議(上):理論基礎
通俗易懂-深入理解TCP協議(下):RTT、滑動窗口、擁塞處理
理論經典:TCP協議的3次握手與4次揮手過程詳解
理論聯系實際:Wireshark抓包分析TCP 3次握手、4次揮手過程
計算機網絡通訊協議關系圖(中文珍藏版)
UDP中一個包的大小最大能多大?
Java新一代網絡編程模型AIO原理及Linux系統AIO介紹
NIO框架入門(一):服務端基于Netty4的UDP雙向通信Demo演示
NIO框架入門(二):服務端基于MINA2的UDP雙向通信Demo演示
NIO框架入門(三):iOS與MINA2、Netty4的跨平臺UDP雙向通信實戰
NIO框架入門(四):Android與MINA2、Netty4的跨平臺UDP雙向通信實戰
P2P技術詳解(一):NAT詳解——詳細原理、P2P簡介
P2P技術詳解(二):P2P中的NAT穿越(打洞)方案詳解
P2P技術詳解(三):P2P技術之STUN、TURN、ICE詳解
高性能網絡編程(一):單臺服務器并發TCP連接數到底可以有多少
高性能網絡編程(二):上一個10年,著名的C10K并發連接問題
高性能網絡編程(三):下一個10年,是時候考慮C10M并發問題了
高性能網絡編程(四):從C10K到C10M高性能網絡應用的理論探索
不為人知的網絡編程(一):淺析TCP協議中的疑難雜癥(上篇)
不為人知的網絡編程(二):淺析TCP協議中的疑難雜癥(下篇)
不為人知的網絡編程(三):關閉TCP連接時為什么會TIME_WAIT、CLOSE_WAIT
不為人知的網絡編程(四):深入研究分析TCP的異常關閉
不為人知的網絡編程(五):UDP的連接性和負載均衡
>> 更多同類文章 ……

即時通訊網 - 即時通訊開發者社區! 來源: - 即時通訊開發者社區!

上一篇:不為人知的網絡編程(五):UDP的連接性和負載均衡下一篇:iOS 的socket 接收到消息 能放在子線程里面嗎?

本帖已收錄至以下技術專輯

推薦方案
評論 7
UDP有iOS端的Demo嗎?跪求!
簽名: 抓狂擴擴擴擴擴擴擴擴擴擴擴擴擴擴
引用:o0oi1i 發表于 2017-09-29 14:40
UDP有iOS端的Demo嗎?跪求!

ios的完整UDP Demo很難找到,不過本站有,你看看是不是你要的:
NIO框架入門(三):iOS與MINA2、Netty4的跨平臺UDP雙向通信實戰
簽名: 《史上最通俗,徹底搞懂字符亂碼問題的本質》http://www.emxvra.tw/thread-2868-1-1.html
引用:JackJiang 發表于 2017-09-29 15:05
ios的完整UDP Demo很難找到,不過本站有,你看看是不是你要的:
《NIO框架入門(三):iOS與MINA2、Netty4 ...

感謝大神
簽名: 抓狂擴擴擴擴擴擴擴擴擴擴擴擴擴擴
物聯網、智能家居適合使用UDP還是TCP呢?
簽名: 要注冊賬號?
引用:wx_cXE2u2Zx 發表于 2018-12-16 19:42
物聯網、智能家居適合使用UDP還是TCP呢?

你了解一下 MQTT協議
簽名: 《史上最通俗,徹底搞懂字符亂碼問題的本質》http://www.emxvra.tw/thread-2868-1-1.html
引用:JackJiang 發表于 2018-12-16 19:48
你了解一下 MQTT協議

謝謝,我們現在使用的是UDP,不知道這個和我們現在產品遇到的問題有沒有關系:在網絡比較復雜的環境下,硬件設備沒有響應服務器消息,先了解MQTT,考慮換到MQTT上來
簽名: 要注冊賬號?
感謝分享,寫的非常好
打賞樓主 ×
使用微信打賞! 使用支付寶打賞!

返回頂部
777彩票走势图表