🌊 TCP三次握手與四次揮手全解析(含序列號動態追蹤)
🔑 TCP 協議核心機制
- 序列號 (seq):數據字節流的唯一標識(32位循環計數器)
- 確認號 (ack):期望接收的下一個序列號(ack = 接收方seq + 1)
- 標志位:SYN(建立連接)、ACK(確認)、FIN(關閉連接)
🛠? 三次握手詳解(連接建立)
序列號變化追蹤(假設初始序列號:Client=1000, Server=5000)
步驟 | 方向 | 標志位 | seq 值 | ack 值 | 關鍵說明 |
---|---|---|---|---|---|
1 | C → S | SYN=1 | 1000 | - | 客戶端隨機初始化seq |
2 | S → C | SYN=1,ACK=1 | 5000 | 1001 | 服務端ack=客戶端seq+1 |
3 | C → S | ACK=1 | 1001 | 5001 | 客戶端seq+1,ack=服務端seq+1 |
📌 SYN消耗序列號:發送SYN會使序列號+1(SYN標志位占據1字節序列空間)
🚪 四次揮手詳解(連接釋放)
序列號變化追蹤(通信結束時:Client seq=8000, Server seq=12000)
步驟 | 方向 | 標志位 | seq 值 | ack 值 | 狀態變化 |
---|---|---|---|---|---|
1 | C → S | FIN=1 | 8000 | - | Client: ESTAB→FIN_WAIT_1 |
2 | S → C | ACK=1 | 12000 | 8001 | Server: ESTAB→CLOSE_WAIT Client: FIN_WAIT_1→FIN_WAIT_2 |
3 | S → C | FIN=1,ACK=1 | 12001 | 8001 | Server: CLOSE_WAIT→LAST_ACK |
4 | C → S | ACK=1 | 8001 | 12002 | Client: FIN_WAIT_2→TIME_WAIT Server: CLOSED 等待2MSL后Client關閉 |
?? 序列號遞增規則:
- FIN標志位消耗1序列號(與SYN相同)
- 發送數據時:seq增加 = 數據字節數
- 發送控制位:seq增加 = 標志位數量
🔥 關鍵機制深度解析
1. 初始序列號 (ISN) 的隨機性
- 生成算法:ISN = (計時器 × 加密因子) mod 232
(現代系統使用安全隨機數生成) - 目的:防止前序連接的報文混淆(舊連接相同端口復用)
2. TIME_WAIT 狀態(2MSL等待)
// Linux內核參數配置
net.ipv4.tcp_fin_timeout = 60 // 控制TIME_WAIT持續時間
net.ipv4.tcp_max_tw_buckets = 18000 // 最大TIME_WAIT連接數
- MSL定義:Max Segment Lifetime(報文最大生存時間,通常30秒)
- 2MSL = 60秒:保證網絡中殘余報文消亡
3. 半關閉狀態(Half-Close)
當一方發送FIN后進入半關閉狀態:
4. 序列號回繞處理
32位序列號在高速網絡中可能溢出(10Gbps≈1.2GB/s):
// Linux內核序列號比較函數
static inline bool tcp_before_seq(u32 seq1, u32 seq2) {return (s32)(seq1 - seq2) < 0;
}
// 處理示例:
// seq1=0xFFFFFF00, seq2=0x00000010 → 判斷seq1 < seq2(溢出場景)
?? 高頻面試問題
-
為什么是三次握手?
阻止歷史重復連接初始化(防止舊SYN干擾新連接)
-
為什么需要TIME_WAIT狀態?
確保被動關閉方能收到最終ACK(防止LAST_ACK超時重傳FIN)
-
服務器如何應對大量TIME_WAIT?
# 內核優化命令 echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse # 重用TIME_WAIT連接 echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle # 快速回收(慎用)
-
握手時的序列號為什么隨機?
避免TCP序列號預測攻擊(如IP欺騙攻擊)
-
為什么揮手是四次而不是三次?
TCP連接允許單向關閉(半關閉狀態),需要獨立確認雙向關閉