TCP 連接管理 之 三次握手詳解
(一)TCP三次握手詳細過程及狀態變化
1. 第一次握手(客戶端 → 服務器)
- 報文標志位:
SYN=1
(同步序列號),ACK=0
(首次握手無確認) - 序列號:客戶端隨機生成初始序列號(例如
seq=x
) - 過程:客戶端發送
SYN
報文,進入SYN_SENT
狀態 - 狀態變化:
- 客戶端:
CLOSED
→SYN_SENT
- 服務器:
LISTEN
(等待連接)
- 客戶端:
2. 第二次握手(服務器 → 客戶端)
- 報文標志位:
SYN=1
,ACK=1
(確認客戶端的SYN) - 序列號:服務器隨機生成初始序列號(例如
seq=y
),確認號為ack=x+1
(第一次握手不攜帶數據,但SYN=1
會消耗一個序列號) - 過程:服務器發送
SYN+ACK
報文,進入SYN_RCVD
狀態 - 狀態變化:
- 服務器:
LISTEN
→SYN_RCVD
- 客戶端:保持
SYN_SENT
- 服務器:
3. 第三次握手(客戶端 → 服務器)
- 報文標志位:
ACK=1
,SYN=0
(非同步) - 序列號:
seq=x+1
(收到的確認號),確認號為ack=y+1
(第二次握手不攜帶數據,但SYN=1
會消耗一個序列號) - 過程:客戶端發送
ACK
報文,雙方進入ESTABLISHED
狀態 - 狀態變化:
- 客戶端:
SYN_SENT
→ESTABLISHED
- 服務器:
SYN_RCVD
→ESTABLISHED
- 客戶端:
(二)連接建立后數據傳輸的序列號規則
1. 客戶端先發送數據
- 客戶端發送數據:
seq=x+1
(第三次握手沒有數據部分,沿用第三次握手的序列號)ack=y+1
(保持對服務器序列號的確認)
- 服務器響應ACK:
seq=y+1
(服務器初始序列號+1)ack=x+1+數據長度
(確認客戶端數據)
2. 服務器先推送數據
- 服務器發送數據:
seq=y+1
(握手最后使用的序列號)ack=x+1
(第三次握手沒有數據部分,保持對客戶端序列號的確認)
- 客戶端響應ACK:
seq=x+1
(客戶端初始序列號+1)ack=y+1+數據長度
(確認服務器數據)
3. 關鍵點總結
- 初始序列號:雙方隨機生成,防止歷史連接沖突。
- ACK確認機制:總是對已接收數據長度+1進行確認(期待下一個字節的序列號)。
- 數據傳輸:先發送數據的一方使用自己最后一次ACK的序列號作為起始值。
(三)TCP三次握手服務端資源建立的詳細過程
1. 服務端內核在第一次握手(收到客戶端的SYN
報文)時為客戶端生成套接字
2. 詳細過程分析
① 第一次握手前(服務端狀態:LISTEN
)
- 服務端調用
listen()
后,進入LISTEN
狀態,此時僅有一個監聽套接字(Listening Socket),用于接收所有客戶端的連接請求。 - 監聽套接字不直接關聯具體客戶端,而是等待
SYN
報文觸發新連接。
② 第一次握手(客戶端發送SYN
,服務端收到)
-
內核動作:
當服務端內核收到客戶端的SYN
報文(標志位SYN=1
)時,會立即創建一個普通套接字(Socket),稱為未完成連接套接字(Incomplete Connection Socket),并進入SYN_RCVD
狀態。該套接字記錄客戶端的IP、端口、初始序列號(
client_isn
)等信息。
此時套接字尚未完全建立(未通過第三次握手),但已占用資源(進入了半連接隊列)。 -
為什么此時生成?
資源預留:為防止SYN Flood攻擊,內核需要限制半連接隊列大小(
net.ipv4.tcp_max_syn_backlog
)。
狀態跟蹤:服務端需維護客戶端的seq
和連接狀態,以完成后續握手。
③ 第二次握手(服務端發送SYN+ACK
)
- 服務端使用新生成的套接字發送
SYN+ACK
(seq=server_isn
,ack=client_isn+1
),但仍處于SYN_RCVD
狀態。 - 此時套接字仍在半連接隊列中,等待客戶端的最終
ACK
。
④ 第三次握手(客戶端發送ACK
,連接完成)
- 服務端收到
ACK
后,將套接字從半連接隊列移到全連接隊列(Accept Queue),狀態變為ESTABLISHED
。 - 當服務端應用調用
accept()
時,從全連接隊列中取出該套接字,交給應用程序使用。
3. 總結
階段 | 服務端動作 | 套接字狀態 |
---|---|---|
第一次握手前 | 僅有監聽套接字(LISTEN ) | 無客戶端套接字 |
第一次握手 | 收到SYN 后,內核生成新套接字 | 半連接隊列(SYN_RCVD ) |
第三次握手后 | 套接字移至全連接隊列,等待accept() | 可用的ESTABLISHED 套接字 |
① 為什么不在第三次握手后再生成套接字?
- 如果在第三次握手后才生成,服務端無法在第二次握手時發送
SYN+ACK
(需要記錄客戶端信息)。 - 提前生成可確保資源分配和狀態跟蹤,但需防范SYN Flood攻擊(通過半連接隊列限制)。
② 半連接隊列 vs 全連接隊列
- 半連接隊列(SYN Queue):存儲未完成三次握手的套接字(
SYN_RCVD
狀態),隊列大小由net.ipv4.tcp_max_syn_backlog
參數決定。 - 全連接隊列(Accept Queue):存儲已完成握手但未被
accept()
的套接字(ESTABLISHED
狀態),隊列大小由backlog
參數和net.core.somaxconn
決定。
③ 如果第三次握手的ACK
丟失怎么辦?
- 服務端會重傳
SYN+ACK
(次數由net.ipv4.tcp_synack_retries
控制),超時后刪除半連接套接字。