好好回答下 TCP 和 UDP 的區別

寫了這麼多篇關於 TCP 和 UDP 的文章,還沒有好好聊過這兩個協議的區別,這篇文章我們就來開誠佈公的談一談。

關於 TCP 和 UDP ,想必大家都看過一張這樣的圖。

image-20220818182534778

有一個小姑娘在對着瓶口慢慢的喝水,下面寫着可靠的傳輸,少女的衣服沒有被水浸溼,這張圖被稱爲 TCP 。

然後又有一個小姑娘在舉着水瓶以很快的速度向下倒水,少女的頭髮凌亂,臉色泛紅,衣服也被水浸溼,這張圖被稱爲 UDP 。

這兩張圖我認爲是個程序員都能大致總結出來這兩個傳輸協議的不同點(畢竟圖上都寫的很清楚了)甚至不少同學對 UDP 產生了邪惡的念想,你說作者好好的畫個圖不行嗎,非要在臉上掛個紅,把衣服弄溼了纔行。。。。。。。

咳,咱們言歸正傳,TCP 和 UDP 的區別一直是面試的重點,也是經常被用來拿來各種比較的兩個協議。

建立連接的差異

TCP 建立連接需要經過三次握手,同時 TCP 斷開連接需要經過四次揮手,這也表示 TCP 是一種面向連接的協議,這個連接不是用一條網線或者一個管道把兩個通信雙方綁在一起,而是建立一條虛擬通信管道。

TCP 的三次握手流程(客戶端向服務器發送建立連接請求):

image-20220822145208949

  1. 服務端進程準備好接收來自外部的 TCP 連接,一般情況下是調用 bind、listen、socket 三個函數完成。這種打開方式被認爲是 被動打開(passive open)。然後服務端進程處於 LISTEN 狀態,等待客戶端連接請求。
  2. 客戶端通過 connect 發起主動打開(active open),向服務器發出連接請求,請求中首部同步位 SYN = 1,同時選擇一個初始序號 sequence ,簡寫 seq = x。SYN 報文段不允許攜帶數據,只消耗一個序號。此時,客戶端進入 SYN-SEND 狀態。
  3. 服務器收到客戶端連接後,需要確認客戶端的報文段。在確認報文段中,把 SYN 和 ACK 位都置爲 1 。確認號是 ack = x + 1,同時也爲自己選擇一個初始序號 seq = y。請注意,這個報文段也不能攜帶數據,但同樣要消耗掉一個序號。此時,TCP 服務器進入 SYN-RECEIVED(同步收到) 狀態。
  4. 客戶端在收到服務器發出的響應後,需要給出確認連接。確認連接中的 ACK 置爲 1 ,序號爲 seq = x + 1,確認號爲 ack = y + 1。TCP 規定,這個報文段可以攜帶數據也可以不攜帶數據,如果不攜帶數據,那麼下一個數據報文段的序號仍是 seq = x + 1。這時,客戶端進入 ESTABLISHED (已連接) 狀態。
  5. 服務器收到客戶的確認後,也進入 ESTABLISHED 狀態。

而 UDP 是面向數據報的協議,所以 UDP 壓根不會有連接的概念,也就不會有三次握手建立連接的過程。

數據傳輸結束後,通信雙方可以釋放連接。數據傳輸結束後的客戶端主機和服務端主機都處於 ESTABLISHED 狀態,然後進入釋放連接的過程。

(客戶端主機主動關閉連接)

image-20220820151725439

TCP 斷開連接需要歷經的過程如下

  1. 客戶端應用程序發出釋放連接的報文段,並停止發送數據,主動關閉 TCP 連接。客戶端主機發送釋放連接的報文段,報文段中首部 FIN 位置爲 1 ,不包含數據,序列號位 seq = u,此時客戶端主機進入 FIN-WAIT-1(終止等待 1) 階段。

  2. 服務器主機接受到客戶端發出的報文段後,即發出確認應答報文,確認應答報文中 ACK = 1,生成自己的序號位 seq = v,ack = u + 1,然後服務器主機就進入 CLOSE-WAIT(關閉等待) 狀態,這個時候客戶端主機 -> 服務器主機這條方向的連接就釋放了,客戶端主機沒有數據需要發送,此時服務器主機是一種半連接的狀態,但是服務器主機仍然可以發送數據。

  3. 客戶端主機收到服務端主機的確認應答後,即進入 FIN-WAIT-2(終止等待2) 的狀態。等待客戶端發出連接釋放的報文段。

  4. 當服務器主機沒有數據發送後,應用進程就會通知 TCP 釋放連接。這時服務端主機會發出斷開連接的報文段,報文段中 ACK = 1,序列號 seq = w,因爲在這之間可能已經發送了一些數據,所以 seq 不一定等於 v + 1。ack = u + 1,在發送完斷開請求的報文後,服務端主機就進入了 LAST-ACK(最後確認)的階段。

  5. 客戶端收到服務端的斷開連接請求後,客戶端需要作出響應,客戶端發出斷開連接的報文段,在報文段中,ACK = 1, 序列號 seq = u + 1,因爲客戶端從連接開始斷開後就沒有再發送數據,ack = w + 1,然後進入到 TIME-WAIT(時間等待) 狀態,請注意,這個時候 TCP 連接還沒有釋放。必須經過時間等待的設置,也就是 2MSL 後,客戶端纔會進入 CLOSED 狀態,時間 MSL 叫做最長報文段壽命(Maximum Segment Lifetime)

  6. 服務端主要收到了客戶端的斷開連接確認後,就會進入 CLOSED 狀態。因爲服務端結束 TCP 連接時間要比客戶端早,而整個連接斷開過程需要發送四個報文段,因此釋放連接的過程也被稱爲四次揮手。

UDP 不存在這條連接,所以它也不需要四次揮手操作。

所以總結一點:TCP 是面向連接的,它的數據傳輸前需要維護一條虛擬連接,數據傳輸需要在這條虛擬連接上進行,數據傳輸完畢後需要斷開這條連接,而 UDP 傳輸不是面向連接的,UDP 發送數據不會建立連接,也不會關心接收端的狀態。

可靠性的差異

TCP 和 UDP 一個主要拿來作對比的就是可靠性,TCP 是一種可靠性的傳輸層協議,UDP 是一種不可靠的傳輸層協議。TCP 的這種可靠性主要由下面這些特徵來保證:

通過序列號和應答號實現可靠性

計算機網絡主機之間的相互通信非常類似於我們日常生活中兩個人之間打電話,這種對話通常是一問一答形式,如果你講了一句話並沒有收到任何迴應,你通常需要再說一次來確保對方是否聽到,如果對方給你迴應了一句話,就說明他已經聽到你的講話了,這就是一個完整的通話流程(拋開建立連接不談,我們着重點放在建立連接之後)。

"對方給你的響應" 在計算機網絡中被稱爲確認應答(ACK),TCP 就是通過 ACK 來實現可靠的數據傳輸,也就是說,發送方在發出請求之後會等待目標主機的響應,如果沒有收到響應,發送方在經過一段時間後就會重傳請求。所以,即使在發送過程中產生丟包,TCP 仍然能夠通過重傳來實現可靠性。

image-20220820181532602

上面描述的情況屬於發送方請求丟失,還有一種情況屬於響應丟失,也就是說請求發送到目標主機後,目標主機會回發 ACK 給請求方,這個 ACK 也有可能丟失,如果 ACK 在鏈路中丟失,一段時間後請求方沒有收到目標主機的 ACK ,仍然會選擇重傳未收到 ACK 的這個請求。

image-20220820181643478

除了消息丟失之外,還存在一種延遲到達的現象,延遲到達指的是發送方發送一個報文段之後,這個報文也許是由於網絡抖動或者網絡擁堵導致一個報文段遲遲沒有到達目標主機,或者目標主機的響應 ACK 遲遲沒有到達發送方的現象。這個一段時間判斷的標準就是重傳時間,一旦過了重傳時間發送方會重傳報文段,很可能存在重傳報文段到達之後,第一次發送的報文段纔剛到的情況,這就存在一個問題:目標主機收到了兩個相同的報文段。必須選擇一個報文段進行丟棄,但是應該選擇哪個報文段呢?

可以通過序列號(seq)來實現,序列號是按照順序給發送數據的每一個字節都標上號碼的編號。接收端通過查詢 TCP 首部中的序列號和數據的長度,將自己下一步應該接收的序列號作爲確認應答返送回去。通過序列號和確認應答號,TCP 能夠識別是否已經接收數據,又能夠判斷是否需要接收,從而實現可靠傳輸

image-20220820185332201

如上圖所示,請求按照順序發送的話是 seq = 1 ,這個請求會把第 1 字節到第 n 字節的數據一起發送過去,等待目標主機一次確認每個字節後,再發送 seq = n + 1 的請求,確認完成後再發送 seq = m + 1 的請求,這樣能夠保證序列號不會重複。

UDP 沒有所謂的序列號和確認號,所以不會對數據進行確認,數據丟失後也不會進行重傳,所以 UDP 是一種不可靠的協議。

如果使用 TCP 和 UDP 來比喻開發人員:TCP 就是那種凡事都要設計好,沒設計不會進行開發的工程師,需要把一切因素考慮在內後再開幹!所以非常靠譜;而 UDP 就是那種上來直接乾乾幹,接到項目需求馬上就開幹,也不管設計,也不管技術選型,就是幹,這種開發人員非常不靠譜,但是適合快速迭代開發,因爲可以馬上上手!

有序性差異

我們上面說到,TCP 會對請求分開發送,每次請求所攜帶的數據都會被目標主機進行確認,目標主機依次確認每個請求後,就會對請求中的數據進行重組,由於請求是由 seq 的,所以 TCP 在重組這些數據時,也會按照順序進行重組,而 UDP 沒有有序性的這種保證。

報文段的差異

TCP 和 UDP 同屬於傳輸層協議,傳輸層協議傳輸的數據統稱爲報文段,TCP 和 UDP 的報文段的主要差異如下。

UDP 報文段結構

image-20220822174732062

  • 源端口號(Source Port) :這個字段佔據 UDP 報文頭的前 16 位,通常包含發送數據報的應用程序所使用的 UDP 端口。接收端的應用程序利用這個字段的值作爲發送響應的目的地址。這個字段是可選項,有時不會設置源端口號。沒有源端口號就默認爲 0 ,通常用於不需要返回消息的通信中。
  • 目標端口號(Destination Port): 表示接收端端口,字段長爲 16 位。
  • 長度(Length): 該字段佔據 16 位,表示 UDP 數據報長度,包含 UDP 報文頭和 UDP 數據長度。因爲 UDP 報文頭長度是 8 個字節,所以這個值最小爲 8,最大長度爲 2 ^ 16 = 65535 字節。
  • 校驗和(Checksum):UDP 使用校驗和來保證數據安全性,UDP 的校驗和也提供了差錯檢測功能,差錯檢測用於校驗報文段從源到目標主機的過程中,數據的完整性是否發生了改變。

TCP 報文段結構

image-20220822173508999

TCP 報文段結構相比 UDP 報文結構多了很多內容。但是前兩個 32 比特的字段是一樣的。它們是 源端口號目標端口號。另外,和 UDP 一樣,TCP 也包含校驗和(checksum field) ,除此之外,TCP 報文段首部還有下面這些

  • 32 比特的序號字段(sequence number field) 和 32 比特的確認號字段(acknowledgment number field) 。這些字段被 TCP 發送方和接收方用來實現可靠的數據傳輸。

  • 4 比特的首部字段長度字段(header length field),這個字段指示了以 32 比特的字爲單位的 TCP 首部長度。TCP 首部的長度是可變的,但是通常情況下,選項字段爲空,所以 TCP 首部字段的長度是 20 字節。

  • 16 比特的 接受窗口字段(receive window field) ,這個字段用於流量控制。它用於指示接收方能夠/願意接受的字節數量

  • 可變的選項字段(options field),這個字段用於發送方和接收方協商最大報文長度,也就是 MSS 時使用

  • 6 比特的 標誌字段(flag field)ACK 標誌用於指示確認字段中的值是有效的,這個報文段包括一個對已被成功接收報文段的確認;RSTSYNFIN 標誌用於連接的建立和關閉;CWRECE 用於擁塞控制;PSH 標誌用於表示立刻將數據交給上層處理;URG 標誌用來表示數據中存在需要被上層處理的 緊急 數據。緊急數據最後一個字節由 16 比特的緊急數據指針字段(urgeent data pointer field) 指出。一般情況下,PSH 和 URG 並沒有使用。

所以從報文段結構的對比可以看出,TCP 相比 UDP 多了許多 Flags、序號和確認號,這些都屬於 TCP 的連接控制。除此之外還有接收窗口,這些屬於擁塞控制和流量控制的內容。TCP 的首部開銷要比 UDP 大,因爲 TCP 首部固定有 20 字節,UDP 首部固定才 8 字節。TCP 和 UDP 都提供了數據校驗功能。

效率的差異

TCP 報文段的發送採用的是"一問一答"形式的,每個請求都會被目標主機確認後再發送下一條報文,效率很慢,後來爲了解決這個問題,TCP 引入了 窗口 這個概念,即使在往返時間較長、頻次很多的情況下,它也能控制網絡性能的下降。

我們之前每次請求發送都是以報文段的形式進行的,引入窗口後,每次請求都可以發送多個報文段,也就是說一個窗口可以發送多個報文段。窗口大小就是指無需等待確認應答就可以繼續發送報文段的最大值。

在這個窗口機制中,大量使用了 緩衝區 ,通過對多個段同時進行確認應答的功能。

如下圖所示,發送報文段中高亮部分即是我們提到的窗口,在窗口內,即是沒有收到確認應答也可以把請求發送出去。不過,在整個窗口的確認應答沒有到達之前,如果部分報文段丟失,那麼發送方將仍會重傳。爲此,發送方需要設置緩存來保留這些需要重傳的報文段,直到收到他們的確認應答。

image-20220820151419312

在滑動窗口以外的部分是尚未發送的報文段和已經接受到的報文段,如果報文段已經收到確認則不可進行重發,此時報文段就可以從緩衝區中清除。

在收到確認的情況下,會將窗口滑動到確認應答中確認號的位置,如上圖所示,這樣可以順序的將多個段同時發送,用以提高通信性能,這種窗口也叫做 滑動窗口(Sliding window)

UDP 發送的報文段不需要確認,也就沒有窗口的概念,所以 UDP 傳輸效率比較高。

使用場景的差異

TCP 和 UDP 在效率、報文段、流量控制、連接管理上均存在差異,由於這些差異導致了應用場景要有不同的選擇,由於 TCP 每個包都需要進行確認,因此 TCP 不適合告訴傳輸數據的場景,像是這種場景使用 UDP 就好了;像是 Ping 和 DNS Lookup,這類型的操作只需要一次簡單的請求/返回,不需要建立連接,用 UDP 就足夠了。比如 HTTP 協議需要考慮請求響應的可靠性,這種場景應該使用 TCP 協議,但是像 HTTP 3.0 這類應用層協議,從功能性上思考,暫時沒有找到太多的優化點,但是想要把網絡優化到極致,就會用 UDP 作爲底層技術,然後在 UDP 基礎上解決可靠性。

本人寫的計算機網絡系列文章

如果對你有幫助,大家可以點贊評論,你的支持就是我更新最大的動力。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章