目錄
TCP連接
TCP報文段結構
往返時間估計與超時
可靠數據傳輸?
回退N步or超時重傳
超時間隔加倍
快速重傳
流量控制
TCP連接管理?
三次握手
1. 客戶端 → 服務器:SYN 包
2. 服務器 → 客戶端:SYN+ACK 包
3. 客戶端 → 服務器:ACK 包
四次揮手
1. 主動關閉方 → 被動關閉方:FIN 包
2. 被動關閉方 → 主動關閉方:ACK 包
3. 被動關閉方 → 主動關閉方:FIN 包
4. 主動關閉方 → 被動關閉方:ACK 包
?為什么必須要三次握手?
為什么不能用兩次握手?
四次或更多次握手可行嗎?
為什么要四次揮手?
為什么不能用三次揮手?
兩次揮手可行嗎?
TCP是因特網運輸層面向連接的可靠傳輸協議。為了提供可靠數據傳輸,TCP依賴前文可靠數據傳輸原理?許多基本原理,其中包括差錯檢測,重傳,累積確認,定時器以及用于序號和確認號的首部字段等。
TCP連接
TCP 被稱為面向連接的協議,核心在于其通信過程需要經歷 “連接建立→數據傳輸→連接釋放”?的完整生命周期。TCP 通信前必須通過三次報文交互,在客戶端和服務器之間協商并確認連接參數,確保雙方具備通信能力(三次握手)。通信結束后,需通過四次報文交互主動釋放連接資源,確保雙方無數據殘留(四次揮手)。TCP 的 “面向連接” 本質是狀態化通信,連接是一種狀態實體,通信依賴連接狀態的一致性,錯誤的處理基于連接的上下文。
TCP連接提供的是全雙工服務,在一個 TCP 連接中,通信雙方(客戶端和服務器)可以同時進行數據的發送和接收,發送方和接收方的角色是動態的、非獨占的。TCP 連接包含兩條獨立的邏輯通道,兩條通道的數據流互不干擾,各自維護獨立的序列號、確認號、滑動窗口等狀態信息。
TCP被設計為點對點(Point-to-Point)協議,而非一對多(如組播或廣播),這是由其協議特性、設計目標和底層網絡架構共同決定的。TCP 的核心定位是在兩個端點之間建立一條邏輯連接,通過序列號、確認機制、滑動窗口等復雜機制實現端到端的可靠數據傳輸。這些機制依賴于唯一的雙向通信鏈路,要求發送方和接收方之間有明確的一一對應關系。若支持一對多(如同時向多個接收方發送數據),每個接收方的網絡狀態、接收能力、數據確認邏輯均不同,需為每個接收方單獨維護一套狀態(如序號、窗口、超時定時器等),這會導致協議復雜度呈指數級上升,甚至無法高效管理。
TCP 將數據視為無邊界的字節流,保證數據按發送順序到達接收方。在點對點場景中,通過單一序列號空間即可實現順序控制;但在一對多場景中,每個接收方的字節流順序獨立,發送方需為每個接收方維護獨立的序列號和確認邏輯,這與 TCP 的字節流抽象模型沖突。且一對多屬于網絡層或應用層的職責,而TCP 專注于傳輸層的端到端可靠性。
應用進程將數據寫入套接字后,會先存于 TCP 發送緩存,等待合適時機發送(數據暫存)。發送緩存配合滑動窗口機制,根據接收方通告的窗口大小,控制發送數據量。若接收方接收緩存快滿,通告小窗口值,發送方就減少從發送緩存提取數據發送的量,避免數據丟失(流量控制)。發送緩存中未被確認的數據,在超時未收到確認時會重傳(可靠傳輸保證)。?
TCP 報文段(TCP Segment)是 TCP在網絡中傳輸數據的基本單位,它由首部和數據部分?組成,承載著從發送端到接收端的數據以及用于實現可靠傳輸的控制信息。
TCP接收方將網絡中傳來的 TCP 報文段,經解析后數據存于接收緩存,供應用進程讀取(數據接收緩存)。當收到亂序的報文段,只要序號在接收窗口內,就暫存于接收緩存,等待正確順序時交付應用進程(亂序處理)。接收緩存剩余空間大小,決定接收方通告給發送方的窗口大小,以此反饋控制發送方發送速率(流量反饋依據)。
TCP可從緩存中取出并且放入報文段的數據數量受限于最大報文段長度(MSS)。MSS 指的是 TCP 報文段中所能承載的應用層數據的最大字節數,不包括 TCP 首部(通常 20 字節 )和 IP 首部(通常 20 字節 )。MSS通常根據最初確定的由本地發送主機發送的最大鏈路層幀長度,即所謂的最大傳輸單元(MTU)來設置。
MTU 指的是在數據鏈路層中,一個數據幀所能夠攜帶的最大有效載荷(不包含幀頭、幀尾等鏈路層控制信息)的字節數。不同的數據鏈路層協議,MTU 的默認值有所不同 。比如,以太網的 MTU 默認值是 1500 字節,PPP 協議的 MTU 默認值通常為 1500 字節或 296 字節,FDDI(光纖分布式數據接口)的 MTU 則為 4352 字節。
比如,若 MSS 值為 1460 字節,那么加上 20 字節的 TCP 首部和 20 字節的 IP 首部后,整個 IP 數據報長度就是 1500 字節,這恰好是以太網默認的最大傳輸單元(MTU)。
TCP報文段結構
TCP 報文段首部長度通常為 20 字節(不含TCP可選字段),最多可達 60 字節,包含了實現可靠傳輸、連接管理等功能的關鍵控制信息,具體如下:
- 源端口號和目的端口號:各占 16 比特,標識發送方和接收方應用進程所使用的端口,幫助數據準確交付給對應的應用程序。
- 序號:占 32 比特,標記本報文段在發送方數據字節流中的位置,用于接收方對數據進行排序。
- 確認號:占 32 比特,是接收方期望接收的下一個字節的序號,用于向發送方確認已收到的數據。
- 首部長度:占 4 比特,指示 TCP 首部的長度,以 4 字節為單位,用于明確首部結束和數據開始的位置。
- 標志位:占 6 比特,包含多個標志:
- URG:緊急標志,URG = 1 時表示報文中有緊急數據。
- ACK:確認標志,ACK = 1 時確認號字段才有效。
- PSH:推送標志,指示接收方盡快將數據交付給上層應用。
- RST:重置標志,用于異常時重置連接。
- SYN:同步標志,在連接建立時同步序號。
- FIN:結束標志,在連接釋放時通知對方本方無數據發送。
- 窗口大小:占 16 比特,接收方通過該字段告知發送方自己當前接收緩沖區的剩余空間大小,實現流量控制。
- 校驗和:占 16 比特,用于檢測 TCP 報文段在傳輸過程中是否出錯,計算范圍包括首部、數據部分以及一個偽首部。
- 緊急指針:占 16 比特,當 URG 標志位為 1 時有效,指出本報文段中緊急數據的最后一個字節的序號。
- 選項:長度可變,最多 40 字節,用于協商額外參數,如最大段大小(MSS)、窗口擴大因子、時間戳等 。
- 數據部分:承載的是應用層實際要傳輸的數據,長度可變,受最大段大小(MSS)限制。在連接建立階段,客戶端和服務器會協商 MSS 值,以確保加上 IP 首部后的整個報文長度不超過鏈路層的最大傳輸單元(MTU),避免 IP 分片。
在 TCP 報文段結構中,序號和確認號是實現可靠傳輸最核心的兩個字段。
TCP把數據看成無結構的,有序的字節流。序號是建立在傳送的字節流之上,而不是建立在傳送的報文段序列之上。因此一個報文段的序號就是該報文段首字節的字節流編號。
序號標識本報文段所攜帶數據在發送方字節流中的起始位置,用于解決數據分片后的排序問題。例如,若發送方發送的第一個報文段攜帶 1000 字節數據,序號為500
,則下一個報文段的序號為1500
。接收方通過序號判斷數據是否按序到達,對亂序數據進行緩存或重傳請求。在連接建立(三次握手)時,序號用于同步初始序列號(ISN),避免歷史數據干擾新連接。
接收方通過確認號告知發送方期望接收的下一個字節的序號,表示 “該序號之前的數據已全部正確接收”。例如,接收方收到序號1-1000
的數據后,會返回確認號1001
,表示希望接收1001
及之后的數據。發送方根據確認號判斷哪些數據已被成功接收,哪些需要重傳(基于超時重傳機制)。同時實現可靠傳輸的雙向確認:不僅發送方需要確認接收方收到數據,接收方也通過確認號主動告知發送方狀態。
TCP 通過 “序號 + 確認號” 機制實現數據的有序性、完整性和去重性,是可靠傳輸的基礎,其他字段均圍繞二者提供輔助或擴展功能。
往返時間估計與超時
顯然,由于路由器的擁塞和端系統負載的變化,TCP報文段中的rtt值也會隨之波動,所以任何給定的RTT值也許不是典型的,所以這也造就了一個明顯的問題:設置超時間隔的長度。因此為了估計一個典型的RTT,自然要對RTT進行一個均值操作。
TCP 采用一種基于采樣的方法來估計 RTT。最初,TCP 會對每個發送的報文段進行計時,當收到對應的 ACK 時,計算實際的往返時間。然后通過加權平均的方式更新對 RTT 的估計值,一旦獲得一個新的RTT,TCP就會用以下公式更新均值:
其中α是一個加權因子(通常取值在 0.125 左右 ),SampleRTT是當前測量得到的樣本往返時間。
除了估算RTT以外,測量RTT的變化也是很有必要的。DEVrtt
用于衡量往返時間估計值的波動情況,是計算超時時間的重要參數之一。
其中,β也是一個加權因子,一般取值為 0.25。該公式的作用是計算往返時間估計值的偏差。|SampleRTT - EstimatedRTT|
?表示樣本往返時間與估計往返時間的差值的絕對值,通過加權平均的方式更新?DEVrtt
?,從而反映出樣本往返時間圍繞估計往返時間的波動程度。?
既然已經給出了EstimateRTT和DevRTT的值,TCP 的超時間隔(Timeout Interval,簡稱?RTO)通常使用 :
其中:δ
?是一個經驗系數,?該系數用于放大?EstimatedRTT
?的權重,確保超時間隔足夠覆蓋網絡波動。?4
?是?δ
?的典型取值,通過將偏差放大 4 倍,使 RTO 對網絡延遲的波動更敏感。
可靠數據傳輸?
在 TCP 協議中,定時器的設置是保障可靠傳輸的關鍵機制之一。TCP 使用多種定時器來處理不同場景,其中最核心的是超時重傳定時器。
TCP采用的是單計時器模型,發送方每次發送?新數據?時,若當前沒有活躍的定時器,則啟動一個覆蓋?最早未確認數據?的定時器。當收到?最早未確認數據的 ACK?時,停止定時器。
第二個主要事件是超時處理,TCP通過超時重傳來響應超時事件,然后重啟定時器。?
回退N步or超時重傳
TCP 的可靠性機制部分借鑒了 GBN 和 SR 的思想,根據自身需求進行了優化和融合。?
TCP使用滑動窗口機制,但?默認采用累積確認(與 GBN 類似),TCP 的滑動窗口大小可變(動態調整),而 GBN 窗口大小固定。TCP同樣為未確認的數據設置定時器,超時后重傳?最早未確認的分組及之后的數據(類似 GBN 的回退重傳)。但是TCP 引入?快速重傳機制,減少不必要的超時等待。
TCP引入?選擇性確認(SACK)選項(類似SR),作為累積確認的補充。當接收方收到失序分組時,可通過 SACK 告知發送方具體哪些分段已接收,發送方只需重傳未被確認的分段。但SACK 是 TCP 的可選功能(需雙方協商啟用),而累積確認是默認機制。接收方同樣會緩存失序分組(通過接收窗口實現),并在收到丟失分組后按序交付。
超時間隔加倍
在 TCP 協議中,超時間隔加倍(Exponential Backoff)?是一種應對網絡擁塞或丟包的重要機制,屬于超時重傳策略的一部分。當 TCP 發送方多次嘗試重傳數據仍失敗時,會逐步指數級增大超時時間,避免因頻繁重傳加劇網絡擁塞。
當 TCP 發送一個報文段后,會啟動一個定時器。若在 RTO 時間內未收到該報文段的確認(ACK),則認為報文段丟失,觸發重傳。
首次超時按正常 RTO 值重傳,后續超時每次重傳后,將 RTO 值翻倍(即指數級增長)。為避免 RTO 無限增長,TCP 通常設置上限(如 64 秒)。當 RTO 達到上限后,繼續重傳但不再增加 RTO。
超時間隔加倍避免網絡擁塞,若每次超時都以固定時間重傳,可能導致大量重傳報文段同時涌入網絡,加劇擁塞(如 “擁塞崩潰”)。指數退避通過減緩重傳頻率,給網絡留出恢復時間。
區分臨時丟包和持續問題:
- 臨時丟包(如網絡抖動):較小的 RTO 即可快速恢復。
- 持續丟包(如鏈路故障):需要更長的 RTO 避免無效重傳。
超時重傳 → RTO指數級增長 → 降低重傳頻率 → 緩解網絡擁塞 這一機制是 TCP自適應網絡變化的關鍵,通過動態調整超時時間,在可靠性和效率之間找到平衡,尤其適用于高延遲或不穩定的網絡環境。
快速重傳
快速重傳是 TCP 協議中用于快速恢復丟失報文段的機制,亂序不影響最終確認,但是會觸發重傳機制。通過 重復確認冗余ACK(Duplicate ACK)?觸發重傳,無需等待超時定時器到期,從而減少丟包導致的延遲。
接收方通過累加確認,只會確認按序到達的連續字節流末尾,未按序到達的字節會導致后續確認被阻斷。
當接收方收到失序報文段時,會發送重復 ACK(即對最近連續收到的最高序號的確認)。發送方收到3 個重復 ACK,判定該序號的報文段丟失,立即重傳,無需等待超時。
快速重傳 = 3次重復ACK觸發 + 立即重傳丟失報文段 + 低延遲恢復傳輸?該機制是 TCP 實現可靠傳輸和高吞吐量的核心技術之一,通過快速響應輕度丟包,顯著提升了網絡傳輸效率,尤其適用于實時性要求較高的場景。
流量控制
TCP流量控制為了防止發送方發送速率超過接收方處理能力,避免接收方緩沖區溢出導致丟包。通過動態調整發送窗口大小,使發送速率與接收方處理能力匹配。
核心機制:滑動窗口協議
-
接收窗口(Advertised Window, rwnd)
- 接收方通過 ACK 報文攜帶
rwnd
字段,告知發送方自己當前可用的接收緩沖區大小(字節)。 - 計算公式:
rwnd = 接收緩沖區總大小 - 已占用空間
。
- 接收方通過 ACK 報文攜帶
-
發送窗口(Send Window)
- 發送方根據接收方的
rwnd
動態調整自己的發送窗口大小,確保已發送但未確認的數據量 ≤?rwnd
。 - 發送窗口大小 = min (
rwnd
,?cwnd
),其中cwnd
為擁塞窗口(由擁塞控制決定)。
- 發送方根據接收方的
初始協商:連接建立時,雙方通過 SYN 報文交換初始窗口大小(默認值通常為 4KB)。
動態調整:接收方每處理一批數據,通過 ACK 報文更新rwnd
。發送方根據最新rwnd
調整發送窗口,控制后續發送速率。
零窗口通知:當接收方緩沖區已滿,rwnd = 0
,發送方暫停發送,進入 ** 窗口探查(Window Probe)** 狀態。
窗口探查:發送方定期發送窗口探測報文(1 字節數據),觸發接收方回復最新rwnd
,避免死鎖。
該機制通過接收窗口實現端到端的速率匹配,確保接收方不會因過載而丟包。UDP沒有流量控制,報文段由于緩存溢出可能在接收方丟失。
TCP連接管理?
我們先來詳細看一下客戶端如何三次握手和服務器建立TCP連接以及四次揮手安全斷開連接:
三次握手
1. 客戶端 → 服務器:SYN 包
- 動作:
客戶端向服務器發送一個SYN 包(TCP 首部中的 SYN 標志位 = 1),表示請求建立連接。 - 攜帶信息:
- 初始序列號(ISN, Initial Sequence Number):客戶端隨機生成的一個 32 位序列號(如
x
),用于標識客戶端發送的數據字節流起點。 - 可選參數:如最大段大小(MSS)、窗口縮放因子等。
- 初始序列號(ISN, Initial Sequence Number):客戶端隨機生成的一個 32 位序列號(如
- 狀態變化:
客戶端進入SYN_SENT狀態,等待服務器響應。
2. 服務器 → 客戶端:SYN+ACK 包
- 動作:
服務器收到 SYN(同步) 包后,向客戶端發送一個SYN+ACK 包(SYN 和 ACK 標志位同時置 1)。 - 攜帶信息:
- 服務器的初始序列號(ISN):服務器隨機生成的序列號(如
y
)。 - 確認號(ACK Number):值為
x+1
,表示 “已收到客戶端的 SYN 包,期望接收客戶端下一個序列號為x+1
的數據”。 - 窗口大小(Window Size):服務器的接收窗口大小,告知客戶端自己的緩沖區容量。
- 服務器的初始序列號(ISN):服務器隨機生成的序列號(如
- 狀態變化:
服務器進入SYN_RCVD狀態,等待客戶端確認。
3. 客戶端 → 服務器:ACK 包
- 動作:
客戶端收到 SYN+ACK 包后,向服務器發送一個ACK 包(ACK 標志位 = 1),SYN=0。 - 攜帶信息:
- 確認號:值為
y+1
,表示 “已收到服務器的 SYN 包,期望接收服務器下一個序列號為y+1
的數據”。 - 序列號:客戶端使用
x+1
作為本次 ACK 的序列號(因 SYN 包本身占用一個序列號)。
- 確認號:值為
- 狀態變化:
客戶端和服務器均進入ESTABLISHED狀態,連接建立完成,開始數據傳輸。
四次揮手
1. 主動關閉方 → 被動關閉方:FIN 包
- 場景:
假設客戶端主動發起關閉(也可以是服務器主動關閉)。 - 動作:
客戶端向服務器發送一個FIN 包(TCP 首部中的 FIN 標志位 = 1),表示 “我已無數據要發送,可以關閉連接”。 - 攜帶信息:
- 序列號(Seq):客戶端當前的序列號
u
(FIN 包本身占用一個序列號,類似 SYN 包)。
- 序列號(Seq):客戶端當前的序列號
- 狀態變化:
客戶端進入FIN_WAIT_1狀態,等待服務器的確認。
2. 被動關閉方 → 主動關閉方:ACK 包
- 動作:
服務器收到 FIN 包后,立即向客戶端發送一個ACK 包(ACK 標志位 = 1),表示 “已收到關閉請求,正在處理剩余數據”。 - 攜帶信息:
- 確認號(Ack):值為
u+1
,表示 “已確認收到客戶端的 FIN 包”。 - 序列號(Seq):服務器當前的序列號
v
(未被 FIN 中斷,繼續沿用之前的序號)。
- 確認號(Ack):值為
- 狀態變化:
服務器進入CLOSE_WAIT狀態(半關閉狀態),此時服務器仍可向客戶端發送剩余數據,客戶端仍需接收。
客戶端收到 ACK 后,進入FIN_WAIT_2狀態,等待服務器發送 FIN 包。
3. 被動關閉方 → 主動關閉方:FIN 包
- 動作:
當服務器確認已發送完所有數據后,向客戶端發送第二個FIN 包(FIN 標志位 = 1),表示 “我已無數據可發送,正式請求關閉連接”。 - 攜帶信息:
- 序列號(Seq):服務器的下一個序列號
w
(通常w = v + 已發送數據字節數
,若中間無新數據,則w = v
)。
- 序列號(Seq):服務器的下一個序列號
- 狀態變化:
服務器進入LAST_ACK狀態,等待客戶端的最終確認。
客戶端收到第二個 FIN 后,進入TIME_WAIT狀態。
4. 主動關閉方 → 被動關閉方:ACK 包
- 動作:
客戶端收到服務器的 FIN 包后,向服務器發送最后一個ACK 包,表示 “已確認關閉請求”。 - 攜帶信息:
- 確認號(Ack):值為
w+1
,確認服務器的 FIN 包。 - 序列號(Seq):客戶端的下一個序列號
u+1
(FIN_WAIT_2 狀態時序列號未變化,因無新數據發送)。
- 確認號(Ack):值為
- 狀態變化:
- 客戶端:發送 ACK 后進入TIME_WAIT狀態,并在等待2 倍最大段生存時間(2MSL)后最終進入CLOSED狀態。
- 服務器:收到 ACK 后立即進入CLOSED狀態,連接徹底關閉。
客戶TCP經歷的典型TCP狀態序列:
服務器端TCP經歷的典型TCP狀態序列:?
?為什么必須要三次握手?
- 雙向確認連接可用性
- 客戶端確認服務器 “能接收數據 + 能發送數據”。
- 服務器確認客戶端 “能接收數據 + 能發送數據”。
- 同步初始序列號(Seq)
避免歷史殘留報文(延遲到達的舊連接數據包)干擾新連接。
為什么不能用兩次握手?
假設改為兩次握手(客戶端→服務器:SYN;服務器→客戶端:SYN+ACK):
存在的風險:
- 舊連接殘留報文導致的資源浪費
- 客戶端發送的舊 SYN 報文(如因網絡延遲滯留)被服務器誤認為是新連接請求。
- 服務器返回 SYN+ACK 并分配資源,但客戶端實際未發起新連接,導致服務器端資源(如連接隊列、內存)浪費(“半開連接” 問題)。
- 單向確認不完整
- 服務器僅能確認客戶端 “能發送數據”,但無法確認客戶端 “能接收數據”。
- 若客戶端實際無法接收(如接收緩沖區已滿),服務器發送的數據會被丟棄,導致連接不可靠。
結論:兩次握手無法避免舊報文干擾,且無法完成雙向確認,因此不可行。
四次或更多次握手可行嗎?
理論上可行,但存在明顯缺陷:
- 效率低下
每增加一次握手,就多一次往返時延(RTT)。例如四次握手需 2 倍 RTT,而三次握手僅需 1.5 倍 RTT,對于高延遲網絡(如衛星通信),效率損失顯著。 - 無額外收益
三次握手已能完成雙向確認和序列號同步,多余的握手次數不會提升可靠性,反而增加協議復雜度。
結論:三次握手是效率與可靠性的最優平衡,多于三次握手無實際意義。
為什么要四次揮手?
TCP 連接是全雙工的(雙向獨立傳輸),全雙工特性要求雙向數據流獨立關閉:
- 允許雙向數據流獨立關閉
一方(如客戶端)先發送 FIN 包請求關閉 “主動關閉方→被動關閉方” 的數據流,另一方仍可繼續發送數據。 - 確保數據完整傳輸
被動關閉方需先確認收到 FIN 包(避免丟包),待自身數據發送完畢后,再發送 FIN 包關閉反向數據流,確保無數據丟失。
為什么不能用三次揮手?
假設嘗試合并兩次揮手為一次(如服務器在 ACK 包中同時攜帶 FIN 標志):
存在的問題:
- 被動關閉方可能尚未完成數據發送
服務器收到客戶端的 FIN 包后,需先確認(ACK),但此時服務器可能仍有數據未發送完畢。若直接在 ACK 中攜帶 FIN,會導致服務器被迫中斷數據傳輸,違反 “允許單向數據流持續” 的原則。
示例場景:
客戶端發送 FIN 請求關閉 “客戶端→服務器” 方向,服務器回復 ACK(確認收到 FIN),并繼續發送剩余數據。待數據發送完畢后,服務器再發送 FIN 關閉 “服務器→客戶端” 方向。這一過程必須分為兩次獨立的報文(ACK 和 FIN),因此無法合并為三次揮手。
兩次揮手可行嗎?
僅在一種特殊場景下可行:被動關閉方在收到 FIN 包時,自身已無數據需要發送。此時可將 ACK 和 FIN 合并為一個報文,形成兩次揮手:
- 客戶端→服務器:FIN(請求關閉)。
- 服務器→客戶端:FIN+ACK(確認關閉并請求反向關閉)。
- 客戶端→服務器:ACK(確認反向關閉)。
但這是特例,而非通用情況,因為 TCP 無法保證被動關閉方在收到 FIN 時恰好無數據待發。
結論:通用場景下必須四次揮手,僅特殊場景可簡化為三次,但無法普遍適用兩次。
?🏻?🏻?🏻.