動圖圖解 UDP 就一定比 TCP 快嗎?

話說,UDP 比 TCP 快嗎?

相信就算不是八股文老手,也會下意識的脫口而出:" "。

這要追問爲什麼,估計大家也能說出個大概。

但這也讓人好奇,用 UDP 就一定比用 TCP 快嗎?什麼情況下用 UDP 會比用 TCP 慢?

我們今天就來聊下這個話題。

使用 socket 進行數據傳輸

作爲一個程序員,假設我們需要在 A 電腦的進程發一段數據到 B 電腦的進程,我們一般會在代碼裏使用 socket 進行編程。

socket 就像是一個電話或者郵箱(郵政的信箱)。當你想要發送消息的時候,撥通電話或者將信息塞到郵箱裏,socket 內核會自動完成將數據傳給對方的這個過程。

基於 socket 我們可以選擇使用 TCP 或 UDP 協議進行通信。

對於 TCP 這樣的可靠性協議,每次消息發出後都能明確知道對方收沒收到,就像打電話一樣,只要 "喂喂" 兩下就能知道對方有沒有在聽。

而 UDP 就像是給郵政的信箱寄信一樣,你寄出去的信,根本就不知道對方有沒有正常收到,丟了也是有可能的。

這讓我想起了大概 17 年前,當時還沒有現在這麼發達的網購,想買一本《掌機迷》雜誌,還得往信封裏塞錢,然後一等就是一個月,好幾次都懷疑信是不是丟了。我至今印象深刻,因爲那是我和我哥攢了好久的錢。。。

回到 socket 編程的話題上。

創建 socket 的方式就像下面這樣。

fd = socket(AF_INET, 具體協議,0);

注意上面的 " 具體協議 ",如果傳入的是SOCK_STREAM,是指使用字節流傳輸數據,說白了就是 TCP 協議

TCP 是什麼

如果傳入的是SOCK_DGRAM,是指使用數據報傳輸數據,也就是 UDP 協議

UDP 是什麼

返回的fd是指 socket 句柄,可以理解爲 socket 的身份證號。通過這個fd你可以在內核中找到唯一的 socket 結構。

如果想要通過這個 socket 發消息,只需要操作這個 fd 就行了,比如執行 send(fd, msg, ...),內核就會通過這個 fd 句柄找到 socket 然後進行發數據的操作。

如果一切順利,此時對方執行接收消息的操作,也就是 recv(fd, msg, ...),就能拿到你發的消息。

udp 發送接收過程

對於異常情況的處理

但如果不順利呢?

比如消息發到一半,丟包了呢?

丟包的原因有很多,之前寫過的《用了 TCP 協議,就一定不會丟包嗎?》有詳細聊到過,這裏就不再展開。

那 UDP 和 TCP 的態度就不太一樣了。

UDP 表示,"哦,是嗎?然後呢?關我 x 事"

TCP 態度就截然相反了,"啊?那可不行,是不是我發太快了呢?是不是鏈路太堵被別人影響到了呢?不過你放心,我肯定給你補發"

TCP 老實人石錘了。我們來看下這個老實人在背後都默默做了哪些事情。

重傳機制

對於 TCP,它會給發出的消息打上一個編號(sequence),接收方收到後回一個確認 (ack)。發送方可以通過ack的數值知道接收方收到了哪些sequence的包。

如果長時間等不到對方的確認,TCP 就會重新發一次消息,這就是所謂的重傳機制

TCP 重傳

流量控制機制

但重傳這件事本身對性能影響是比較嚴重的,所以是下下策

於是 TCP 就需要思考有沒有辦法可以儘量避免重傳

因爲數據發送方和接收方處理數據能力可能不同,因此如果可以根據雙方的能力去調整發送的數據量就好了,於是就有了發送和接收窗口,基本上從名字就能看出它的作用,比如接收窗口的大小就是指,接收方當前能接收的數據量大小發送窗口的大小就指發送方當前能發的數據量大小。TCP 根據窗口的大小去控制自己發送的數據量,這樣就能大大減少丟包的概率。

流量控制機制

滑動窗口機制

接收方的接收到數據之後,會不斷處理,處理能力也不是一成不變的,有時候處理的快些,那就可以收多點數據,處理的慢點那就希望對方能少發點數據。畢竟發多了就有可能處理不過來導致丟包,丟包會導致重傳,這可是下下策。因此我們需要動態的去調節這個接收窗口的大小,於是就有了滑動窗口機制

看到這裏大家可能就有點迷了,流量控制和滑動窗口機制貌似很像,它們之間是啥關係?我總結一下。其實現在 TCP 是通過滑動窗口機制來實現流量控制機制的

滑動窗口機制

擁塞控制機制

但這還不夠,有時候發生丟包,並不是因爲發送方和接收方的處理能力問題導致的。而是跟網絡環境有關,大家可以將網絡想象爲一條公路。馬路上可能堵滿了別人家的車,只留下一輛車的空間。那就算你家有 5 輛車,目的地也正好有 5 個停車位,你也沒辦法同時全部一起上路。於是 TCP 希望能感知到外部的網絡環境,根據網絡環境及時調整自己的發包數量,比如馬路只夠兩輛車跑,那我就只發兩輛車。但外部環境這麼複雜,TCP 是怎麼感知到的呢?

TCP 會先慢慢試探的發數據,不斷加碼數據量,越發越多,先發一個,再發 2 個,4 個…。直到出現丟包,這樣 TCP 就知道現在當前網絡大概喫得消幾個包了,這既是所謂的擁塞控制機制

不少人會疑惑流量控制和擁塞控制的關係。我這裏小小的總結下。流量控制針對的是單個連接數據處理能力的控制,擁塞控制針對的是整個網絡環境數據處理能力的控制。

1663598420295

分段機制

但上面提到的都是怎麼降低重傳的概率,似乎重傳這個事情就是無法避免的,那如果確實發生了,有沒有辦法降低它帶來的影響呢?

有。當我們需要發送一個超大的數據包時,如果這個數據包丟了,那就得重傳同樣大的數據包。但如果我能將其分成一小段一小段,那就算真丟了,那我也就只需要重傳那一小段就好了,大大減小了重傳的壓力,這就是 TCP 的分段機制

而這個所謂的一小段的長度,在傳輸層叫 MSSMaximum Segment Size),數據包長度大於 MSS 則會分成 N 個小於等於 MSS 的包。

MSS 分包

而在網絡層,如果數據包還大於 MTU(Maximum Transmit Unit),那還會繼續分包。

MTU 分包

一般情況下,MSS=MTU-40Byte,所以 TCP 分段後,到了 IP 層大概率就不會再分片了

MSS 和 MTU 的區別

亂序重排機制

既然數據包會被分段,鏈路又這麼複雜還會丟包,那數據包亂序也就顯得不奇怪了。比如發數據包 1,2,3。1 號數據包走了其他網絡路徑,2 和 3 數據包先到,1 數據包後到,於是數據包順序就成了 2,3,1。這一點 TCP 也考慮到了,依靠數據包的sequence,接收方就能知道數據包的先後順序。

後發的數據包先到是吧,那就先放到專門的亂序隊列中,等數據都到齊後,重新整理好亂序隊列的數據包順序後再給到用戶,這就是亂序重排機制

亂序隊列等待數據包的到來

連接機制

前面提到,UDP 是無連接的,而 TCP 是面向連接的。

這裏提到的連接到底是啥?

TCP 通過上面提到的各種機制實現了數據的可靠性。這些機制背後是通過一個個數據結構來實現的邏輯。而爲了實現這套邏輯,操作系統內核需要在兩端代碼裏維護一套複雜的狀態機(三次握手,四次揮手,RST,closing 等異常處理機制),這套狀態機其實就是所謂的 "連接"。這其實就是 TCP 的連接機制,而 UDP 用不上這套狀態機,因此它是 "無連接" 的。

網絡環境鏈路很長,還複雜,數據丟包是很常見的。

我們平常用 TCP 做各種數據傳輸,完全對這些事情無感知。

哪有什麼歲月靜好,是 TCP 替你負重前行。

這就是 TCP 三大特性 "面向連接、可靠的、基於字節流" 中 " 可靠 " 的含義。

不信你改用 UDP 試試,丟包那就是真丟了,丟到你懷疑人生。

用 UDP 就一定比用 TCP 快嗎?

這時候 UDP 就不服了:" 正因爲沒有這些複雜的 TCP 可靠性機制,所以我很快啊 "

嗯,這也是大部分人認爲 UDP 比 TCP 快的原因。

實際上大部分情況下也確實是這樣的。這話沒毛病。

那問題就來了。

有沒有用了 UDP 但卻比 TCP 慢的情況呢?

其實也有。

在回答這個問題前,我需要先說下 UDP 的用途

實際上,大部分人也不會嘗試直接拿裸 udp 放到生產環境中去做項目。

那 UDP 的價值在哪?

在我看來,UDP 的存在,本質是內核提供的一個最小網絡傳輸功能

很多時候,大家雖然號稱自己用了 UDP,但實際上都很忌憚它的丟包問題,所以大部分情況下都會在 UDP 的基礎上做各種不同程度的應用層可靠性保證。比如王者農藥用的KCP,以及最近很火的QUIC(HTTP3.0),其實都在 UDP 的基礎上做了重傳邏輯,實現了一套類似 TCP 那樣的可靠性機制。

教科書上最愛提 UDP 適合用於音視頻傳輸,因爲這些場景允許丟包。但其實也不是什麼包都能丟的,比如重要的關鍵幀啥的,該重傳還得重傳。除此之外,還有一些亂序處理機制。舉個例子吧。

打音視頻電話的時候,你可能遇到過丟失中間某部分信息的情況,但應該從來沒遇到過亂序的情況吧。

比如對方打網絡電話給你,說了:" 我好想給小白來個點贊在看!"

這時候網絡信號不好,你可能會聽到 "我…. 點贊在看"。

但卻從來沒遇到過 "在看小白好想贊" 這樣的亂序場景吧?

所以說,雖然選擇了使用 UDP,但一般還是會在應用層上做一些重傳機制的

於是問題就來了,如果現在我需要傳一個特別大的數據包

TCP裏,它內部會根據MSS的大小分段,這時候進入到 IP 層之後,每個包大小都不會超過MTU,因此 IP 層一般不會再進行分片。這時候發生丟包了,只需要重傳每個 MSS 分段就夠了。

TCP 分段

但對於UDP,其本身並不會分段,如果數據過大,到了 IP 層,就會進行分片。此時發生丟包的話,再次重傳,就會重傳整個大數據包

UDP 不分段

對於上面這種情況,使用 UDP 就比 TCP 要慢

當然,解決起來也不復雜。這裏的關鍵點在於是否實現了數據分段機制,使用 UDP 的應用層如果也實現了分段機制的話,那就不會出現上述的問題了

總結

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