寫了這麼多篇關於 TCP 和 UDP 的文章,還沒有好好聊過這兩個協定的區別,這篇文章我們就來開誠佈公的談一談。
關於 TCP 和 UDP ,想必大家都看過一張這樣的圖。
有一個小姑娘在對著瓶口慢慢的喝水,下面寫著可靠的傳輸,少女的衣服沒有被水浸溼,這張圖被稱為 TCP 。
然後又有一個小姑娘在舉著水瓶以很快的速度向下倒水,少女的頭髮凌亂,臉色泛紅,衣服也被水浸溼,這張圖被稱為 UDP 。
這兩張圖我認為是個程式設計師都能大致總結出來這兩個傳輸協定的不同點(畢竟圖上都寫的很清楚了)甚至不少同學對 UDP 產生了邪惡的念想,你說作者好好的畫個圖不行嗎,非要在臉上掛個紅,把衣服弄溼了才行。。。。。。。
咳,咱們言歸正傳,TCP 和 UDP 的區別一直是面試的重點,也是經常被用來拿來各種比較的兩個協定。
TCP 建立連線需要經過三次握手,同時 TCP 斷開連線需要經過四次揮手,這也表示 TCP 是一種面向連線的協定,這個連線不是用一條網線或者一個管道把兩個通訊雙方綁在一起,而是建立一條虛擬
通訊管道。
TCP 的三次握手流程(使用者端向伺服器傳送建立連線請求):
被動開啟(passive open)
。然後伺服器端程序處於 LISTEN
狀態,等待使用者端連線請求。connect
發起主動開啟(active open)
,向伺服器發出連線請求,請求中首部同步位 SYN = 1,同時選擇一個初始序號 sequence ,簡寫 seq = x。SYN 報文段不允許攜帶資料,只消耗一個序號。此時,使用者端進入 SYN-SEND
狀態。SYN-RECEIVED(同步收到)
狀態。ESTABLISHED (已連線)
狀態。ESTABLISHED
狀態。而 UDP 是面向資料包的協定,所以 UDP 壓根不會有連線的概念,也就不會有三次握手建立連線的過程。
資料傳輸結束後,通訊雙方可以釋放連線。資料傳輸結束後的使用者端主機和伺服器端主機都處於 ESTABLISHED 狀態,然後進入釋放連線的過程。
(使用者端主機主動關閉連線)
TCP 斷開連線需要歷經的過程如下
使用者端應用程式發出釋放連線的報文段,並停止傳送資料,主動關閉 TCP 連線。使用者端主機傳送釋放連線的報文段,報文段中首部 FIN 位置為 1 ,不包含資料,序列號位 seq = u,此時使用者端主機進入 FIN-WAIT-1(終止等待 1)
階段。
伺服器主機接受到使用者端發出的報文段後,即發出確認應答報文,確認應答報文中 ACK = 1,生成自己的序號位 seq = v,ack = u + 1,然後伺服器主機就進入 CLOSE-WAIT(關閉等待)
狀態,這個時候使用者端主機 -> 伺服器主機這條方向的連線就釋放了,使用者端主機沒有資料需要傳送,此時伺服器主機是一種半連線的狀態,但是伺服器主機仍然可以傳送資料。
使用者端主機收到伺服器端主機的確認應答後,即進入 FIN-WAIT-2(終止等待2)
的狀態。等待使用者端發出連線釋放的報文段。
當伺服器主機沒有資料傳送後,應用程序就會通知 TCP 釋放連線。這時伺服器端主機會發出斷開連線的報文段,報文段中 ACK = 1,序列號 seq = w,因為在這之間可能已經傳送了一些資料,所以 seq 不一定等於 v + 1。ack = u + 1,在傳送完斷開請求的報文後,伺服器端主機就進入了 LAST-ACK(最後確認)
的階段。
使用者端收到伺服器端的斷開連線請求後,使用者端需要作出響應,使用者端發出斷開連線的報文段,在報文段中,ACK = 1, 序列號 seq = u + 1,因為使用者端從連線開始斷開後就沒有再傳送資料,ack = w + 1,然後進入到 TIME-WAIT(時間等待)
狀態,請注意,這個時候 TCP 連線還沒有釋放。必須經過時間等待的設定,也就是 2MSL
後,使用者端才會進入 CLOSED
狀態,時間 MSL 叫做最長報文段壽命(Maximum Segment Lifetime)
。
伺服器端主要收到了使用者端的斷開連線確認後,就會進入 CLOSED 狀態。因為伺服器端結束 TCP 連線時間要比使用者端早,而整個連線斷開過程需要傳送四個報文段,因此釋放連線的過程也被稱為四次揮手。
UDP 不存在這條連線,所以它也不需要四次揮手操作。
所以總結一點:TCP 是面向連線的,它的資料傳輸前需要維護一條虛擬連線,資料傳輸需要在這條虛擬連線上進行,資料傳輸完畢後需要斷開這條連線,而 UDP 傳輸不是面向連線的,UDP 傳送資料不會建立連線,也不會關心接收端的狀態。
TCP 和 UDP 一個主要拿來作對比的就是可靠性,TCP 是一種可靠性的傳輸層協定,UDP 是一種不可靠的傳輸層協定。TCP 的這種可靠性主要由下面這些特徵來保證:
通過序列號和應答號實現可靠性
計算機網路主機之間的相互通訊非常類似於我們日常生活中兩個人之間打電話,這種對話通常是一問一答形式,如果你講了一句話並沒有收到任何迴應,你通常需要再說一次來確保對方是否聽到,如果對方給你迴應了一句話,就說明他已經聽到你的講話了,這就是一個完整的通話流程(拋開建立連線不談,我們著重點放在建立連線之後)。
"對方給你的響應" 在計算機網路中被稱為確認應答(ACK)
,TCP 就是通過 ACK 來實現可靠的資料傳輸,也就是說,傳送方在發出請求之後會等待目標主機的響應
,如果沒有收到響應,傳送方在經過一段時間後就會重傳請求。所以,即使在傳送過程中產生丟包,TCP 仍然能夠通過重傳來實現可靠性。
上面描述的情況屬於傳送方請求丟失,還有一種情況屬於響應丟失,也就是說請求傳送到目標主機後,目標主機會回發 ACK 給請求方,這個 ACK 也有可能丟失,如果 ACK 在鏈路中丟失,一段時間後請求方沒有收到目標主機的 ACK ,仍然會選擇重傳未收到 ACK 的這個請求。
除了訊息丟失之外,還存在一種延遲到達的現象,延遲到達指的是傳送方傳送一個報文段之後,這個報文也許是由於網路抖動或者網路擁堵導致一個報文段遲遲沒有到達目標主機,或者目標主機的響應 ACK 遲遲沒有到達傳送方的現象。這個一段時間判斷的標準就是重傳時間,一旦過了重傳時間傳送方會重傳報文段,很可能存在重傳報文段到達之後,第一次傳送的報文段才剛到的情況,這就存在一個問題:目標主機收到了兩個相同的報文段。必須選擇一個報文段進行丟棄,但是應該選擇哪個報文段呢?
可以通過序列號(seq)來實現,序列號是按照順序給傳送資料的每一個位元組都標上號碼的編號。接收端通過查詢 TCP 首部中的序列號和資料的長度,將自己下一步應該接收的序列號作為確認應答返送回去。通過序列號和確認應答號,TCP 能夠識別是否已經接收資料,又能夠判斷是否需要接收,從而實現可靠傳輸。
如上圖所示,請求按照順序傳送的話是 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 報文段結構
源埠號(Source Port)
:這個欄位佔據 UDP 報文頭的前 16 位,通常包含傳送資料包的應用程式所使用的 UDP 埠。接收端的應用程式利用這個欄位的值作為傳送響應的目的地址。這個欄位是可選項,有時不會設定源埠號。沒有源埠號就預設為 0 ,通常用於不需要返回訊息的通訊中。目標埠號(Destination Port)
: 表示接收端埠,欄位長為 16 位。長度(Length)
: 該欄位佔據 16 位,表示 UDP 資料包長度,包含 UDP 報文頭和 UDP 資料長度。因為 UDP 報文頭長度是 8 個位元組,所以這個值最小為 8,最大長度為 2 ^ 16 = 65535 位元組。校驗和(Checksum)
:UDP 使用校驗和來保證資料安全性,UDP 的校驗和也提供了差錯檢測功能,差錯檢測用於校驗報文段從源到目標主機的過程中,資料的完整性是否發生了改變。TCP 報文段結構
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
標誌用於指示確認欄位中的值是有效的,這個報文段包括一個對已被成功接收報文段的確認;RST
、SYN
、FIN
標誌用於連線的建立和關閉;CWR
和 ECE
用於擁塞控制;PSH
標誌用於表示立刻將資料交給上層處理;URG
標誌用來表示資料中存在需要被上層處理的 緊急 資料。緊急資料最後一個位元組由 16 位元的緊急資料指標欄位(urgeent data pointer field)
指出。一般情況下,PSH 和 URG 並沒有使用。
所以從報文段結構的對比可以看出,TCP 相比 UDP 多了許多 Flags、序號和確認號,這些都屬於 TCP 的連線控制。除此之外還有接收視窗,這些屬於擁塞控制和流量控制的內容。TCP 的首部開銷要比 UDP 大,因為 TCP 首部固定有 20 位元組,UDP 首部固定才 8 位元組。TCP 和 UDP 都提供了資料校驗功能。
TCP 報文段的傳送採用的是"一問一答"形式的,每個請求都會被目標主機確認後再傳送下一條報文,效率很慢,後來為了解決這個問題,TCP 引入了 視窗
這個概念,即使在往返時間較長、頻次很多的情況下,它也能控制網路效能的下降。
我們之前每次請求傳送都是以報文段的形式進行的,引入視窗後,每次請求都可以傳送多個報文段,也就是說一個視窗可以傳送多個報文段。視窗大小就是指無需等待確認應答就可以繼續傳送報文段的最大值。
在這個視窗機制中,大量使用了 緩衝區
,通過對多個段同時進行確認應答的功能。
如下圖所示,傳送報文段中高亮部分即是我們提到的視窗,在視窗內,即是沒有收到確認應答也可以把請求傳送出去。不過,在整個視窗的確認應答沒有到達之前,如果部分報文段丟失,那麼傳送方將仍會重傳。為此,傳送方需要設定快取來保留這些需要重傳的報文段,直到收到他們的確認應答。
在滑動視窗以外的部分是尚未傳送的報文段和已經接受到的報文段,如果報文段已經收到確認則不可進行重發,此時報文段就可以從緩衝區中清除。
在收到確認的情況下,會將視窗滑動到確認應答中確認號的位置,如上圖所示,這樣可以順序的將多個段同時傳送,用以提高通訊效能,這種視窗也叫做 滑動視窗(Sliding window)
。
UDP 傳送的報文段不需要確認,也就沒有視窗的概念,所以 UDP 傳輸效率比較高。
TCP 和 UDP 在效率、報文段、流量控制、連線管理上均存在差異,由於這些差異導致了應用場景要有不同的選擇,由於 TCP 每個包都需要進行確認,因此 TCP 不適合告訴傳輸資料的場景,像是這種場景使用 UDP 就好了;像是 Ping 和 DNS Lookup,這型別的操作只需要一次簡單的請求/返回,不需要建立連線,用 UDP 就足夠了。比如 HTTP 協定需要考慮請求響應的可靠性,這種場景應該使用 TCP 協定,但是像 HTTP 3.0 這類應用層協定,從功能性上思考,暫時沒有找到太多的優化點,但是想要把網路優化到極致,就會用 UDP 作為底層技術,然後在 UDP 基礎上解決可靠性。
作者:cxuan 出處:https://www.cnblogs.com/cxuanBlog/ 本文版權歸作者和部落格園共有,未經作者允許不能轉載,轉載需要聯絡微信: becomecxuan,否則追究法律責任的權利。 如果文中有什麼錯誤,歡迎指出。以免更多的人被誤導。 |