chinese直男口爆体育生外卖, 99久久er热在这里只有精品99, 又色又爽又黄18禁美女裸身无遮挡, gogogo高清免费观看日本电视,私密按摩师高清版在线,人妻视频毛茸茸,91论坛 兴趣闲谈,欧美 亚洲 精品 8区,国产精品久久久久精品免费

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

TCP三次握手與四次揮手的詳細過程

馬哥Linux運維 ? 來源:馬哥Linux運維 ? 2026-02-25 10:38 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

一、概述

1.1 背景介紹

TCP 三次握手和四次揮手,大概是網(wǎng)絡(luò)領(lǐng)域被問爛了的面試題。但真正能把狀態(tài)變遷、序列號變化、抓包細節(jié)講清楚的人并不多。很多人背了八股文,一到生產(chǎn)環(huán)境看 Wireshark 抓包就懵了——SYN_RECV 隊列溢出怎么排查?TIME_WAIT 堆積幾萬個怎么處理?RST 到底是誰發(fā)的?這些問題光靠背書解決不了。

這篇文章從 TCP 報文格式講起,逐字節(jié)拆解三次握手和四次揮手的完整過程,配合 Wireshark 和 tcpdump 的實際抓包輸出,把每個狀態(tài)變遷都對應(yīng)到真實的網(wǎng)絡(luò)包上。同時覆蓋 TCP 重傳機制、擁塞控制算法對比(BBR vs Cubic),以及生產(chǎn)環(huán)境中最常見的 TCP 問題排查和內(nèi)核參數(shù)調(diào)優(yōu)。

1.2 技術(shù)特點

報文級拆解:從 TCP Header 的每個字段出發(fā),理解握手揮手的本質(zhì)而非死記硬背

抓包驅(qū)動:所有理論都配合 Wireshark/tcpdump 的真實抓包輸出驗證

狀態(tài)機完整:覆蓋 TCP 全部 11 種狀態(tài)及其轉(zhuǎn)換條件

生產(chǎn)導(dǎo)向:重點放在 TIME_WAIT 優(yōu)化、半連接隊列溢出、RST 排查等實際問題

1.3 適用場景

場景一:面試準備,需要深入理解 TCP 連接管理機制而非停留在八股文層面

場景二:生產(chǎn)環(huán)境 TCP 連接異常排查,需要通過抓包定位具體問題

場景三:高并發(fā)服務(wù)的內(nèi)核網(wǎng)絡(luò)參數(shù)調(diào)優(yōu),解決 TIME_WAIT 堆積、SYN Flood 等問題

場景四:微服務(wù)架構(gòu)下的連接管理優(yōu)化,理解連接復(fù)用和優(yōu)雅關(guān)閉

1.4 環(huán)境要求

組件 版本要求 說明
操作系統(tǒng) Ubuntu 24.04 LTS / RHEL 9.x 內(nèi)核 6.8+,BBR v3 支持
Wireshark 4.4+ GUI 抓包分析工具
tcpdump 4.99+ 命令行抓包工具
ss / iproute2 6.x+ 替代 netstat 的連接狀態(tài)查看工具
curl 8.x+ HTTP 請求測試

二、TCP 報文格式

在聊握手揮手之前,先把 TCP 報文頭的結(jié)構(gòu)搞清楚。不理解報文格式,后面看抓包就是看天書。

2.1 TCP Header 結(jié)構(gòu)

TCP 報文頭最小 20 字節(jié),最大 60 字節(jié)(含 Options)。結(jié)構(gòu)如下:

0          1          2          3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     Source Port     |    Destination Port    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|            Sequence Number            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          Acknowledgment Number           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data |    |C|E|U|A|P|R|S|F|                |
| Offset| Rsrvd |W|C|R|C|S|S|Y|I|      Window       |
|    |    |R|E|G|K|H|T|N|N|                |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|      Checksum      |     Urgent Pointer    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          Options (variable)             |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

2.2 關(guān)鍵字段解析

重點關(guān)注幾個和握手揮手直接相關(guān)的字段:

序列號(Sequence Number,32 位):標識從 TCP 發(fā)送端向接收端發(fā)送的數(shù)據(jù)字節(jié)流中的第一個字節(jié)的編號。握手階段的初始序列號(ISN)是隨機生成的,不是從 0 開始,這是為了防止歷史連接的殘留包干擾新連接。

確認號(Acknowledgment Number,32 位):期望收到對方下一個報文段的第一個數(shù)據(jù)字節(jié)的序號。只有 ACK 標志位為 1 時這個字段才有效。

標志位(Flags):這 6 個標志位是握手揮手的核心:

標志位 含義 握手/揮手中的作用
SYN 同步序列號 發(fā)起連接,攜帶初始序列號
ACK 確認 確認收到對方的數(shù)據(jù)
FIN 結(jié)束 請求關(guān)閉連接
RST 重置 強制中斷連接
PSH 推送 要求接收方立即將數(shù)據(jù)交給應(yīng)用層
URG 緊急 緊急指針有效

窗口大?。╓indow,16 位):接收方告訴發(fā)送方自己還能接收多少字節(jié)的數(shù)據(jù)。配合 Window Scale 選項可以擴展到 30 位。

2.3 TCP Options

握手階段的 SYN 包通常攜帶以下 Options:

MSS (Maximum Segment Size)  : 通告對方自己能接收的最大報文段長度,通常 1460(以太網(wǎng) MTU 1500 - IP頭20 - TCP頭20)
Window Scale         : 窗口縮放因子,允許窗口大小超過 65535
SACK Permitted        : 告知對方支持選擇性確認
Timestamps          : 用于 RTT 計算和 PAWS(防止序列號回繞)

三、三次握手詳細過程

3.1 握手全景

三次握手的本質(zhì)是:雙方交換初始序列號(ISN),并確認對方的接收能力。

  客戶端                     服務(wù)端
   |                        |
   | [CLOSED]              [LISTEN] |
   |                        |
   |----------- SYN, Seq=x ----------------------->|
   | [SYN_SENT]                  |
   |                  [SYN_RCVD] |
   |<---------- SYN+ACK, Seq=y, Ack=x+1 ----------|
? ? ? | ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
? ? ? |----------- ACK, Seq=x+1, Ack=y+1 ----------->|
   | [ESTABLISHED]         [ESTABLISHED] |
   |                        |

3.2 逐包拆解

第一次握手:客戶端發(fā)送 SYN

客戶端調(diào)用connect()系統(tǒng)調(diào)用,內(nèi)核構(gòu)造一個 SYN 報文發(fā)出去:

標志位:SYN=1

序列號:Seq=x(隨機生成的 ISN)

確認號:Ack=0(此時還沒有需要確認的數(shù)據(jù))

Options:MSS=1460, WS=7, SACK_PERM, TSval=xxx

客戶端狀態(tài)從 CLOSED 變?yōu)?strong>SYN_SENT。

Wireshark 中看到的樣子:

No. Time   Source    Destination  Protocol Info
1  0.000000 192.168.1.10 10.0.0.1   TCP    54321 > 443 [SYN] Seq=0 Win=65535 Len=0 MSS=1460 WS=128 SACK_PERM TSval=1234567

注意 Wireshark 默認顯示的是相對序列號(從 0 開始),實際的 ISN 是一個 32 位隨機數(shù)??梢栽?Edit -> Preferences -> Protocols -> TCP 中關(guān)閉 "Relative sequence numbers" 查看真實值。

第二次握手:服務(wù)端回復(fù) SYN+ACK

服務(wù)端收到 SYN 后,從半連接隊列(SYN Queue)中分配一個條目,構(gòu)造 SYN+ACK 報文:

標志位:SYN=1, ACK=1

序列號:Seq=y(服務(wù)端自己的 ISN)

確認號:Ack=x+1(確認收到客戶端的 SYN,期望下一個字節(jié)是 x+1)

Options:MSS=1460, WS=7, SACK_PERM, TSval=yyy

服務(wù)端狀態(tài)從 LISTEN 變?yōu)?strong>SYN_RCVD。

No. Time   Source    Destination  Protocol Info
2  0.000543 10.0.0.1   192.168.1.10 TCP    443 > 54321 [SYN, ACK] Seq=0 Ack=1 Win=65535 Len=0 MSS=1460 WS=128 SACK_PERM

第三次握手:客戶端發(fā)送 ACK

客戶端收到 SYN+ACK 后,connect()返回成功,同時發(fā)送最后一個 ACK:

標志位:ACK=1

序列號:Seq=x+1

確認號:Ack=y+1(確認收到服務(wù)端的 SYN)

客戶端狀態(tài)變?yōu)?strong>ESTABLISHED。服務(wù)端收到這個 ACK 后,從半連接隊列移到全連接隊列(Accept Queue),狀態(tài)也變?yōu)?strong>ESTABLISHED。

No. Time   Source    Destination  Protocol Info
3  0.000012 192.168.1.10 10.0.0.1   TCP    54321 > 443 [ACK] Seq=1 Ack=1 Win=65536 Len=0

這個 ACK 包是可以攜帶數(shù)據(jù)的(TCP Fast Open 就利用了這一點),但通常情況下 Len=0。

3.3 為什么是三次而不是兩次

這是面試高頻問題,標準答案有兩個層面:

層面一:防止歷史連接的初始化

假設(shè)只有兩次握手,客戶端發(fā)了一個 SYN 包因為網(wǎng)絡(luò)延遲滯留在網(wǎng)絡(luò)中,客戶端超時后重新發(fā)起連接并完成通信。之后那個滯留的 SYN 包到達服務(wù)端,服務(wù)端以為是新連接請求,直接進入 ESTABLISHED 狀態(tài)分配資源——但客戶端根本不知道這個連接的存在。三次握手中,服務(wù)端回復(fù) SYN+ACK 后必須等客戶端的 ACK 確認,客戶端收到一個不認識的 SYN+ACK 會回復(fù) RST,避免了這個問題。

層面二:雙方都需要確認對方的收發(fā)能力

建立可靠連接需要確認四件事:客戶端的發(fā)送能力、客戶端的接收能力、服務(wù)端的發(fā)送能力、服務(wù)端的接收能力。三次握手剛好完成這四個確認:

第一次握手(SYN)   :服務(wù)端確認 -> 客戶端的發(fā)送能力 OK
第二次握手(SYN+ACK) :客戶端確認 -> 服務(wù)端的接收能力 OK + 服務(wù)端的發(fā)送能力 OK
第三次握手(ACK)   :服務(wù)端確認 -> 客戶端的接收能力 OK

兩次握手少了最后一步,服務(wù)端無法確認客戶端的接收能力。

3.4 半連接隊列與全連接隊列

這兩個隊列是理解 SYN Flood 攻擊和連接建立失敗的關(guān)鍵:

半連接隊列(SYN Queue):存放收到 SYN 但還沒收到第三次 ACK 的連接,狀態(tài)為 SYN_RCVD。隊列長度由tcp_max_syn_backlog控制。

全連接隊列(Accept Queue):存放已完成三次握手但還沒被accept()取走的連接,狀態(tài)為 ESTABLISHED。隊列長度由min(backlog, somaxconn)決定。

# 查看半連接隊列溢出次數(shù)
netstat -s | grep"SYNs to LISTEN"

# 查看全連接隊列溢出次數(shù)
netstat -s | grep"overflowed"

# 用 ss 查看當前隊列狀態(tài)
# Recv-Q: 當前全連接隊列中的連接數(shù)
# Send-Q: 全連接隊列的最大長度
ss -lnt | grep :443

隊列溢出時的表現(xiàn):半連接隊列滿了,新的 SYN 包會被直接丟棄(客戶端表現(xiàn)為連接超時);全連接隊列滿了,服務(wù)端會根據(jù)tcp_abort_on_overflow參數(shù)決定是丟棄 ACK 還是發(fā)送 RST。

四、四次揮手詳細過程

4.1 揮手全景

TCP 連接的關(guān)閉需要四次揮手,因為 TCP 是全雙工的——每個方向的關(guān)閉需要獨立進行。主動關(guān)閉方發(fā) FIN 只是說"我不再發(fā)數(shù)據(jù)了",但還可以接收數(shù)據(jù),對方可能還有數(shù)據(jù)沒發(fā)完。

  主動關(guān)閉方                   被動關(guān)閉方
   |                        |
   | [ESTABLISHED]         [ESTABLISHED] |
   |                        |
   |----------- FIN, Seq=u ----------------------->|
   | [FIN_WAIT_1]                 |
   |                [CLOSE_WAIT]  |
   |<---------- ACK, Seq=v, Ack=u+1 --------------|
? ? ? | ?[FIN_WAIT_2] ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
? ? ? | ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
? ? ? | ? ? ? ? ? ? ?(被動方繼續(xù)發(fā)送剩余數(shù)據(jù)...) ? ? ? ? |
? ? ? | ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
? ? ? |<---------- FIN, Seq=w, Ack=u+1 --------------|
? ? ? | ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?[LAST_ACK] ? ? |
? ? ? | ?[TIME_WAIT] ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?|
? ? ? |----------- ACK, Seq=u+1, Ack=w+1 ----------->|
   |                  [CLOSED]  |
   | (等待 2MSL)                  |
   | [CLOSED]                   |

4.2 逐包拆解

第一次揮手:主動關(guān)閉方發(fā)送 FIN

應(yīng)用層調(diào)用close()或shutdown(SHUT_WR),內(nèi)核發(fā)送 FIN 報文:

標志位:FIN=1, ACK=1

序列號:Seq=u(當前發(fā)送序列號)

確認號:Ack=v(確認對方最后收到的數(shù)據(jù))

主動關(guān)閉方狀態(tài)從 ESTABLISHED 變?yōu)?strong>FIN_WAIT_1。

No. Time   Source    Destination  Protocol Info
10  5.234100 192.168.1.10 10.0.0.1   TCP    54321 > 443 [FIN, ACK] Seq=500 Ack=800 Win=65536 Len=0

第二次揮手:被動關(guān)閉方回復(fù) ACK

被動方內(nèi)核收到 FIN 后自動回復(fù) ACK,應(yīng)用層通過read()返回 0 感知到對方關(guān)閉:

標志位:ACK=1

確認號:Ack=u+1

被動關(guān)閉方狀態(tài)變?yōu)?strong>CLOSE_WAIT,主動關(guān)閉方收到后變?yōu)?strong>FIN_WAIT_2。

No. Time   Source    Destination  Protocol Info
11  5.234650 10.0.0.1   192.168.1.10 TCP    443 > 54321 [ACK] Seq=800 Ack=501 Win=65536 Len=0

第三次揮手:被動關(guān)閉方發(fā)送 FIN

被動方處理完剩余數(shù)據(jù)后調(diào)用close(),內(nèi)核發(fā)送 FIN:

標志位:FIN=1, ACK=1

序列號:Seq=w(可能在第二次和第三次揮手之間還發(fā)送了數(shù)據(jù))

被動關(guān)閉方狀態(tài)變?yōu)?strong>LAST_ACK。

No. Time   Source    Destination  Protocol Info
15  5.340200 10.0.0.1   192.168.1.10 TCP    443 > 54321 [FIN, ACK] Seq=900 Ack=501 Win=65536 Len=0

第四次揮手:主動關(guān)閉方回復(fù) ACK

主動方回復(fù)最后一個 ACK,進入TIME_WAIT狀態(tài):

No. Time   Source    Destination  Protocol Info
16  5.340250 192.168.1.10 10.0.0.1   TCP    54321 > 443 [ACK] Seq=501 Ack=901 Win=65536 Len=0

被動方收到 ACK 后直接進入 CLOSED。主動方需要等待 2MSL(Maximum Segment Lifetime)后才進入 CLOSED。

4.3 四次揮手能變成三次嗎

可以。如果被動關(guān)閉方在收到 FIN 時恰好也沒有數(shù)據(jù)要發(fā)了,內(nèi)核會把 ACK 和 FIN 合并成一個包(FIN+ACK),這就變成了三次揮手。在 Wireshark 中經(jīng)常能看到這種情況,Linux 內(nèi)核默認開啟了 TCP 延遲確認(Delayed ACK),會嘗試合并 ACK 和 FIN。

No. Time   Source    Destination  Protocol Info
10  5.234100 192.168.1.10 10.0.0.1   TCP    54321 > 443 [FIN, ACK] Seq=500 Ack=800
11  5.234650 10.0.0.1   192.168.1.10 TCP    443 > 54321 [FIN, ACK] Seq=800 Ack=501  <-- ACK和FIN合并
12 ? 5.234700 192.168.1.10 ?10.0.0.1 ? ? ?TCP ? ? ? 54321 > 443 [ACK] Seq=501 Ack=801

4.4 TIME_WAIT 狀態(tài)詳解

TIME_WAIT 是 TCP 狀態(tài)機中最容易引發(fā)生產(chǎn)問題的狀態(tài)。主動關(guān)閉方在發(fā)送最后一個 ACK 后進入 TIME_WAIT,持續(xù) 2MSL(Linux 上硬編碼為 60 秒)。

為什么需要 TIME_WAIT:

確保最后一個 ACK 到達:如果最后的 ACK 丟失,被動方會重傳 FIN,主動方需要在 TIME_WAIT 狀態(tài)下重新發(fā)送 ACK。如果直接進入 CLOSED,收到重傳的 FIN 后會回復(fù) RST,導(dǎo)致被動方異常關(guān)閉。

讓舊連接的殘留包在網(wǎng)絡(luò)中消亡:TCP 用四元組(源IP、源端口、目的IP、目的端口)標識連接。如果舊連接關(guān)閉后立即用相同四元組建立新連接,網(wǎng)絡(luò)中殘留的舊包可能被新連接錯誤接收。等待 2MSL 確保舊包全部過期。

TIME_WAIT 堆積的危害:

# 查看 TIME_WAIT 連接數(shù)量
ss -s
# 或者
ss -ant | awk'{print $1}'| sort | uniq -c | sort -rn

# 典型輸出
 28453 TIME-WAIT
 1024 ESTABLISHED
  12 LISTEN
   3 FIN-WAIT-2

每個 TIME_WAIT 連接占用約 0.25KB 內(nèi)存(內(nèi)核用 inet_timewait_sock 結(jié)構(gòu)體,比完整的 tcp_sock 小得多),28000 個也就 7MB,內(nèi)存不是主要問題。真正的問題是端口耗盡——客戶端的臨時端口范圍默認是 32768-60999,總共 28232 個,如果對同一個目標 IP:Port 的 TIME_WAIT 連接占滿了這個范圍,新連接就建不了了。

TIME_WAIT 優(yōu)化方案:

# 方案一:開啟 tcp_tw_reuse(推薦)
# 允許在 TIME_WAIT 狀態(tài)的端口被新的出站連接復(fù)用
# 前提是新連接的 timestamp 大于舊連接的最后 timestamp
sysctl -w net.ipv4.tcp_tw_reuse=1

# 方案二:縮短 FIN_TIMEOUT(影響 FIN_WAIT_2 超時,不影響 TIME_WAIT)
sysctl -w net.ipv4.tcp_fin_timeout=15

# 方案三:擴大臨時端口范圍
sysctl -w net.ipv4.ip_local_port_range="1024 65535"

# 方案四:使用連接池復(fù)用長連接(應(yīng)用層方案,最推薦)
# HTTP Keep-Alive / gRPC 長連接 / 數(shù)據(jù)庫連接池

注意:tcp_tw_recycle在 Linux 4.12 之后已經(jīng)被移除了,不要再用這個參數(shù)。它在 NAT 環(huán)境下會導(dǎo)致大量連接失敗。

五、Wireshark 抓包實戰(zhàn)

5.1 抓包準備

# 在服務(wù)器上用 tcpdump 抓包保存為 pcap 文件,然后用 Wireshark 打開分析
# -i eth0: 指定網(wǎng)卡
# -s 0: 抓完整包
# -w: 保存到文件
# port 443: 只抓 443 端口的流量
sudo tcpdump -i eth0 -s 0 -w /tmp/tcp-handshake.pcap port 443

# 另一個終端發(fā)起請求
curl -v https://example.com

# 抓完后 Ctrl+C 停止,把 pcap 文件下載到本地用 Wireshark 打開

5.2 Wireshark 過濾器速查

Wireshark 的顯示過濾器是分析抓包的核心技能:

# 基礎(chǔ)過濾
tcp.port == 443          # 源或目的端口是 443
tcp.dstport == 80         # 目的端口是 80
ip.addr == 192.168.1.10      # 源或目的 IP

# 標志位過濾(抓握手揮手的關(guān)鍵)
tcp.flags.syn == 1 && tcp.flags.ack == 0  # 只看 SYN 包(第一次握手)
tcp.flags.syn == 1 && tcp.flags.ack == 1  # 只看 SYN+ACK 包(第二次握手)
tcp.flags.fin == 1             # 只看 FIN 包
tcp.flags.reset == 1            # 只看 RST 包(排查異常斷連)

# 連接狀態(tài)過濾
tcp.analysis.retransmission    # 重傳包
tcp.analysis.duplicate_ack     # 重復(fù) ACK
tcp.analysis.zero_window      # 零窗口(接收方緩沖區(qū)滿)
tcp.analysis.window_update     # 窗口更新

# 組合過濾
tcp.flags.syn == 1 && ip.dst == 10.0.0.1  # 發(fā)往特定服務(wù)器的 SYN
tcp.stream eq 5               # 只看第 5 條 TCP 流

5.3 TCP 流追蹤

在 Wireshark 中右鍵任意一個 TCP 包,選擇Follow -> TCP Stream,可以看到整條連接從握手到揮手的完整生命周期。這是分析單個連接問題最高效的方式。

流追蹤視圖會用不同顏色區(qū)分兩個方向的數(shù)據(jù),底部可以切換顯示格式(ASCII/Hex/Raw)。在 Stream 編號旁邊的下拉框可以快速切換不同的 TCP 流。

5.4 實戰(zhàn):分析一次完整的 HTTPS 連接

用 Wireshark 打開抓包文件后,一次完整的 HTTPS 連接包含以下階段:

包序號 方向       內(nèi)容          說明
------ ----       ----          ----
1    Client -> Server [SYN]          TCP 三次握手開始
2    Server -> Client [SYN, ACK]
3    Client -> Server [ACK]          TCP 連接建立完成
4    Client -> Server Client Hello      TLS 握手開始
5    Server -> Client Server Hello, Cert...
6    Client -> Server Key Exchange, Finished
7    Server -> Client Finished        TLS 握手完成
8    Client -> Server Application Data    加密的 HTTP 請求
9    Server -> Client Application Data    加密的 HTTP 響應(yīng)
10   Client -> Server [FIN, ACK]       TCP 四次揮手開始
11   Server -> Client [FIN, ACK]       合并的 FIN+ACK
12   Client -> Server [ACK]          連接關(guān)閉完成

重點關(guān)注包 1-3 的時間差:第一次握手到第二次握手的時間差就是一個 RTT(Round Trip Time),這是評估網(wǎng)絡(luò)延遲的直接指標。如果這個值超過 100ms,說明網(wǎng)絡(luò)延遲較高,需要考慮就近部署或 CDN 加速。

六、tcpdump 命令行抓包技巧

生產(chǎn)服務(wù)器通常沒有 GUI,tcpdump 是唯一的抓包手段。掌握 tcpdump 的過濾語法能大幅提升排查效率。

6.1 常用抓包命令

# 抓取指定端口的 SYN 包(只看新連接建立)
sudo tcpdump -i any'tcp[tcpflags] & (tcp-syn) != 0'and port 80 -nn

# 抓取 RST 包(排查連接異常斷開)
sudo tcpdump -i any'tcp[tcpflags] & (tcp-rst) != 0'-nn

# 抓取 SYN 但不包含 ACK 的包(純 SYN,第一次握手)
sudo tcpdump -i any'tcp[tcpflags] & (tcp-syn|tcp-ack) == tcp-syn'-nn

# 抓取特定主機之間的流量并保存
sudo tcpdump -i eth0 host 10.0.0.1 and port 443 -s 0 -w /tmp/debug.pcap -c 10000

# 只抓包頭不抓數(shù)據(jù)(節(jié)省磁盤空間)
sudo tcpdump -i eth0 -s 96 port 3306 -w /tmp/mysql-headers.pcap

# 實時查看 TCP 標志位和序列號
sudo tcpdump -i any port 80 -nn -S -tttt
# -nn: 不解析主機名和端口名
# -S: 顯示絕對序列號
# -tttt: 顯示完整時間戳

6.2 tcpdump 輸出解讀

# 典型的三次握手輸出
1401.123456 IP 192.168.1.10.54321 > 10.0.0.1.443: Flags [S], seq 1234567890, win 65535, options [mss 1460,sackOK,TS val 123456 ecr 0,nop,wscale 7], length 0
1401.124012 IP 10.0.0.1.443 > 192.168.1.10.54321: Flags [S.], seq 987654321, ack 1234567891, win 65535, options [mss 1460,sackOK,TS val 654321 ecr 123456,nop,wscale 7], length 0
1401.124050 IP 192.168.1.10.54321 > 10.0.0.1.443: Flags [.], ack 987654322, win 512, length 0

Flags 字段的含義:[S]= SYN,[S.]= SYN+ACK,[.]= ACK,[F.]= FIN+ACK,[R]= RST,[P.]= PSH+ACK。

6.3 高級過濾技巧

# 抓取 TCP 窗口為 0 的包(零窗口,接收方緩沖區(qū)滿)
sudo tcpdump -i any'tcp[14:2] = 0'-nn

# 抓取包含特定 TCP Option 的包(比如 Window Scale)
sudo tcpdump -i any'tcp[tcpflags] & tcp-syn != 0 and tcp[20] = 3'-nn

# 抓取大于指定長度的包(排查大包問題)
sudo tcpdump -i any'tcp and greater 1400'-nn

# 按時間輪轉(zhuǎn)保存(長時間抓包)
# -G 3600: 每小時輪轉(zhuǎn)一次
# -W 24: 最多保留 24 個文件
sudo tcpdump -i eth0 port 443 -w /tmp/capture-%Y%m%d-%H%M%S.pcap -G 3600 -W 24

七、TCP 重傳機制

TCP 的可靠傳輸依賴重傳機制。理解重傳對排查網(wǎng)絡(luò)丟包、延遲抖動等問題至關(guān)重要。

7.1 超時重傳(RTO Retransmission)

發(fā)送方發(fā)出數(shù)據(jù)后啟動一個重傳定時器(RTO,Retransmission Timeout)。如果在 RTO 時間內(nèi)沒有收到 ACK,就重傳該數(shù)據(jù)段。

RTO 的計算基于 RTT 的采樣值,Linux 使用 Jacobson 算法動態(tài)調(diào)整:

SRTT = (1 - α) * SRTT + α * RTT_sample    (α = 1/8)
RTTVAR = (1 - β) * RTTVAR + β * |SRTT - RTT|  (β = 1/4)
RTO = SRTT + 4 * RTTVAR

RTO 的最小值是 200ms(TCP_RTO_MIN),最大值是 120s(TCP_RTO_MAX)。每次超時重傳后 RTO 翻倍(指數(shù)退避),依次是 200ms、400ms、800ms、1.6s...直到達到最大重傳次數(shù)(tcp_retries2默認 15 次,總計約 15 分鐘)。

# 查看當前連接的 RTO 值
ss -ti dst 10.0.0.1:443
# 輸出中的 rto:204 表示當前 RTO 為 204ms

7.2 快速重傳(Fast Retransmit)

等 RTO 超時太慢了??焖僦貍鳈C制在收到 3 個重復(fù) ACK(Duplicate ACK)時立即重傳丟失的數(shù)據(jù)段,不用等定時器超時。

發(fā)送方                接收方
 |--- Seq=1, Len=1000 --->      |
 |--- Seq=1001, Len=1000 --->     | (丟失)
 |--- Seq=2001, Len=1000 --->     |
 |<-- ACK=1001 (Dup ACK?#1) ---------| ?收到 Seq=2001 但缺 1001,回復(fù) ACK=1001
? |--- Seq=3001, Len=1000 --->     |
 |<-- ACK=1001 (Dup ACK?#2) ---------|
? |--- Seq=4001, Len=1000 --->     |
 |<-- ACK=1001 (Dup ACK?#3) ---------| ?第 3 個重復(fù) ACK
? |--- Seq=1001, Len=1000 --->     | 快速重傳!
 |<-- ACK=5001 ----------------------| ?SACK 機制下一次確認所有已收到的數(shù)據(jù)

在 Wireshark 中,快速重傳的包會被標記為[TCP Fast Retransmission],重復(fù) ACK 會被標記為[TCP Dup ACK]。

7.3 SACK(選擇性確認)

沒有 SACK 的情況下,快速重傳只能重傳一個包,后續(xù)丟失的包還得等超時。SACK 允許接收方告訴發(fā)送方"我收到了哪些數(shù)據(jù)段",發(fā)送方只需要重傳真正丟失的部分。

# 確認 SACK 是否啟用
sysctl net.ipv4.tcp_sack
# net.ipv4.tcp_sack = 1 (默認開啟)

在 Wireshark 中查看 SACK 信息:過濾tcp.options.sack,可以看到 SACK 塊的左右邊界,精確標識了接收方已收到的數(shù)據(jù)范圍。

八、TCP 擁塞控制:BBR vs Cubic

擁塞控制決定了 TCP 發(fā)送數(shù)據(jù)的速率。選錯算法,帶寬利用率可能差幾倍。

8.1 Cubic(Linux 默認)

Cubic 是基于丟包的擁塞控制算法,Linux 從 2.6.19 開始默認使用。核心思路是:沒丟包就加速,丟包了就減速。

工作階段:

慢啟動(Slow Start):窗口從 initcwnd(默認 10 個 MSS)開始,每收到一個 ACK 窗口加 1,指數(shù)增長

擁塞避免(Congestion Avoidance):窗口超過 ssthresh 后改為線性增長

丟包響應(yīng):檢測到丟包后,窗口乘以一個系數(shù)(Cubic 用三次函數(shù)恢復(fù))

Cubic 的問題:

在高帶寬高延遲(長肥管道)網(wǎng)絡(luò)中,丟包恢復(fù)太慢,帶寬利用率低

把丟包等同于擁塞,但現(xiàn)代網(wǎng)絡(luò)中丟包可能是隨機的(無線網(wǎng)絡(luò))

緩沖區(qū)膨脹(Bufferbloat):在路由器緩沖區(qū)很大的情況下,Cubic 會把緩沖區(qū)填滿才觸發(fā)丟包,導(dǎo)致延遲飆升

8.2 BBR(Bottleneck Bandwidth and RTT)

BBR 是 Google 在 2016 年提出的擁塞控制算法,Linux 4.9 開始支持。BBR v3 在 Linux 6.x 內(nèi)核中已經(jīng)相當成熟。

核心思路:不依賴丟包信號,而是主動探測瓶頸帶寬(BtlBw)和最小 RTT(RTprop),讓發(fā)送速率 = BtlBw,inflight 數(shù)據(jù)量 = BtlBw * RTprop。

BBR 的優(yōu)勢:

高帶寬利用率:在長肥管道中表現(xiàn)遠優(yōu)于 Cubic

低延遲:不會填滿路由器緩沖區(qū)

抗隨機丟包:不把隨機丟包當作擁塞信號

BBR 的注意事項:

BBR 在與 Cubic 共存時可能搶占帶寬(公平性問題),BBR v3 對此有改善

在低延遲局域網(wǎng)中,BBR 和 Cubic 差異不大

需要內(nèi)核 6.x+ 才能用到 BBR v3 的完整特性

# 查看當前使用的擁塞控制算法
sysctl net.ipv4.tcp_congestion_control

# 查看可用的算法
sysctl net.ipv4.tcp_available_congestion_control

# 切換到 BBR
sysctl -w net.ipv4.tcp_congestion_control=bbr

# 持久化配置
cat >> /etc/sysctl.conf <

8.3 如何選擇

場景 推薦算法 理由
跨地域/跨國傳輸 BBR 高延遲網(wǎng)絡(luò)下帶寬利用率遠高于 Cubic
CDN 邊緣節(jié)點 BBR 面對各種網(wǎng)絡(luò)質(zhì)量的客戶端,BBR 適應(yīng)性更強
數(shù)據(jù)中心內(nèi)部 Cubic 低延遲環(huán)境差異不大,Cubic 公平性更好
視頻流媒體 BBR 低延遲 + 高吞吐的組合適合實時傳輸
與大量 Cubic 流共存 Cubic 或 BBR v3 BBR v1/v2 的公平性問題在 v3 中有改善

九、最佳實踐

9.1 連接管理最佳實踐

服務(wù)端:

# /etc/sysctl.conf 推薦配置

# 全連接隊列長度,配合應(yīng)用的 listen backlog 使用
net.core.somaxconn = 65535

# 半連接隊列長度
net.ipv4.tcp_max_syn_backlog = 65535

# 開啟 SYN Cookie 防御 SYN Flood
net.ipv4.tcp_syncookies = 1

# TIME_WAIT 相關(guān)
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15
net.ipv4.ip_local_port_range = 1024 65535
net.ipv4.tcp_max_tw_buckets = 262144

應(yīng)用層:

使用 HTTP/2 或 gRPC 多路復(fù)用,減少連接數(shù)

配置合理的 Keep-Alive 超時,避免空閑連接占用資源

使用連接池管理數(shù)據(jù)庫和緩存連接

優(yōu)雅關(guān)閉:先shutdown(SHUT_WR)再close(),給對方發(fā)送剩余數(shù)據(jù)的機會

9.2 抓包分析最佳實踐

抓包時用 BPF 過濾器縮小范圍,避免抓到無關(guān)流量導(dǎo)致文件過大

生產(chǎn)環(huán)境抓包設(shè)置-c(包數(shù)量限制)或-G -W(時間輪轉(zhuǎn)),防止磁盤寫滿

分析時先用capinfos查看 pcap 文件概況,再用 Wireshark 的 Statistics -> Conversations 看連接分布

用tshark(Wireshark 的命令行版本)做批量分析和統(tǒng)計

# 用 tshark 統(tǒng)計重傳率
tshark -r capture.pcap -q -z io,stat,0,"tcp.analysis.retransmission"

# 用 tshark 導(dǎo)出特定流的數(shù)據(jù)
tshark -r capture.pcap -Y"tcp.stream eq 5"-w stream5.pcap

十、常見 TCP 問題排查

10.1 連接超時(Connection Timeout)

現(xiàn)象:客戶端connect()長時間無響應(yīng),最終超時報錯。

排查思路:

# 第一步:確認服務(wù)端端口是否在監(jiān)聽
ss -lnt | grep :443

# 第二步:在客戶端抓包看 SYN 是否發(fā)出去了
sudo tcpdump -i any'tcp[tcpflags] & tcp-syn != 0'and dst host 10.0.0.1 -nn

# 第三步:在服務(wù)端抓包看 SYN 是否到達
sudo tcpdump -i any'tcp[tcpflags] & tcp-syn != 0'and dst port 443 -nn

# 第四步:檢查防火墻規(guī)則
iptables -L -n -v | grep 443
nft list ruleset | grep 443

常見原因和解決方案:

原因 診斷方法 解決方案
防火墻攔截 SYN 客戶端有 SYN 發(fā)出但服務(wù)端沒收到 檢查中間防火墻/安全組規(guī)則
半連接隊列溢出 netstat -s | grep "SYNs to LISTEN" 計數(shù)增長 增大tcp_max_syn_backlog
全連接隊列溢出 netstat -s | grep "overflowed" 計數(shù)增長 增大somaxconn+ 應(yīng)用 backlog
服務(wù)端 CPU 打滿 SYN+ACK 延遲回復(fù) 排查服務(wù)端性能問題
路由不通 traceroute 中間斷開 檢查路由表和網(wǎng)絡(luò)設(shè)備

10.2 RST 包排查

RST 是 TCP 的"緊急剎車",收到 RST 意味著連接被強制中斷。排查 RST 的關(guān)鍵是搞清楚誰發(fā)的、為什么發(fā)

常見 RST 場景:

# 場景一:連接不存在的端口
# 服務(wù)端沒有進程監(jiān)聽該端口,內(nèi)核直接回復(fù) RST
curl http://10.0.0.1:9999
# 抓包看到:SYN -> RST,ACK

# 場景二:應(yīng)用異常關(guān)閉連接
# 應(yīng)用設(shè)置了 SO_LINGER l_onoff=1, l_linger=0,close() 時發(fā) RST 而非 FIN
# 或者應(yīng)用在接收緩沖區(qū)還有未讀數(shù)據(jù)時調(diào)用 close()

# 場景三:防火墻/LB 超時
# 中間設(shè)備(防火墻、負載均衡器)的連接跟蹤表超時,后續(xù)包被回復(fù) RST
# 典型表現(xiàn):空閑一段時間后的第一個請求收到 RST

# 場景四:全連接隊列溢出且 tcp_abort_on_overflow=1
sysctl net.ipv4.tcp_abort_on_overflow
# 如果為 1,隊列滿時服務(wù)端對第三次握手的 ACK 回復(fù) RST

RST 排查命令:

# 抓取所有 RST 包并顯示詳細信息
sudo tcpdump -i any'tcp[tcpflags] & tcp-rst != 0'-nn -tttt

# 統(tǒng)計 RST 包的來源分布
sudo tcpdump -i any'tcp[tcpflags] & tcp-rst != 0'-nn -c 1000 2>/dev/null | 
 awk'{print $3}'| cut -d. -f1-4 | sort | uniq -c | sort -rn

# 用 ss 查看連接被 RST 的統(tǒng)計
ss -s
# 關(guān)注 "reset" 相關(guān)的計數(shù)

10.3 半連接隊列溢出(SYN Flood)

SYN Flood 攻擊通過大量偽造源 IP 的 SYN 包填滿服務(wù)端的半連接隊列,導(dǎo)致正常連接無法建立。

檢測方法:

# 查看 SYN_RECV 狀態(tài)的連接數(shù)
ss -ant state syn-recv | wc -l

# 查看半連接隊列溢出計數(shù)(持續(xù)增長說明有問題)
netstat -s | grep"SYNs to LISTEN"

# 查看 SYN Cookie 觸發(fā)次數(shù)
netstat -s | grep"SYN cookies"

# 用 nstat 看增量統(tǒng)計(比 netstat -s 更適合監(jiān)控)
nstat -az TcpExtListenDrops TcpExtListenOverflows TcpExtSyncookiesSent

防御方案:

# 開啟 SYN Cookie(必須開啟)
sysctl -w net.ipv4.tcp_syncookies=1

# 增大半連接隊列
sysctl -w net.ipv4.tcp_max_syn_backlog=65535

# 減少 SYN+ACK 重傳次數(shù)(加快清理無效半連接)
sysctl -w net.ipv4.tcp_synack_retries=2

# 配合 iptables 限速
iptables -A INPUT -p tcp --syn -mlimit--limit500/s --limit-burst 1000 -j ACCEPT
iptables -A INPUT -p tcp --syn -j DROP

十一、內(nèi)核參數(shù)調(diào)優(yōu)完整方案

11.1 生產(chǎn)環(huán)境推薦配置

以下是經(jīng)過大量生產(chǎn)驗證的 TCP 內(nèi)核參數(shù)配置,適用于高并發(fā) Web 服務(wù)場景:

# /etc/sysctl.d/99-tcp-tuning.conf

# ============ 連接隊列 ============
# 全連接隊列最大長度(需要應(yīng)用 listen backlog 配合)
net.core.somaxconn = 65535
# 半連接隊列最大長度
net.ipv4.tcp_max_syn_backlog = 65535

# ============ TIME_WAIT 優(yōu)化 ============
# 允許 TIME_WAIT 端口復(fù)用(僅對出站連接有效)
net.ipv4.tcp_tw_reuse = 1
# FIN_WAIT_2 超時時間(秒)
net.ipv4.tcp_fin_timeout = 15
# TIME_WAIT 狀態(tài)的最大數(shù)量
net.ipv4.tcp_max_tw_buckets = 262144
# 臨時端口范圍
net.ipv4.ip_local_port_range = 1024 65535

# ============ SYN Flood 防御 ============
net.ipv4.tcp_syncookies = 1
# SYN+ACK 重傳次數(shù)
net.ipv4.tcp_synack_retries = 2
# SYN 重傳次數(shù)(影響 connect 超時時間)
net.ipv4.tcp_syn_retries = 3

# ============ Keep-Alive ============
# 空閑多久后開始發(fā)送 Keep-Alive 探測(秒)
net.ipv4.tcp_keepalive_time = 600
# 探測間隔(秒)
net.ipv4.tcp_keepalive_intvl = 15
# 探測次數(shù),超過后斷開連接
net.ipv4.tcp_keepalive_probes = 5

# ============ 緩沖區(qū) ============
# TCP 接收緩沖區(qū)(最小值、默認值、最大值,單位字節(jié))
net.ipv4.tcp_rmem = 4096 87380 16777216
# TCP 發(fā)送緩沖區(qū)
net.ipv4.tcp_wmem = 4096 65536 16777216
# 全局接收/發(fā)送緩沖區(qū)
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216

# ============ 擁塞控制 ============
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr

# ============ 其他優(yōu)化 ============
# 開啟 TCP Fast Open(客戶端和服務(wù)端都支持)
net.ipv4.tcp_fastopen = 3
# 開啟 SACK
net.ipv4.tcp_sack = 1
# 開啟窗口縮放
net.ipv4.tcp_window_scaling = 1
# 全連接隊列溢出時的行為(0=丟棄ACK,1=發(fā)RST)
# 生產(chǎn)環(huán)境建議設(shè)為 0,讓客戶端重試而非收到 RST
net.ipv4.tcp_abort_on_overflow = 0
# 應(yīng)用配置
sudo sysctl -p /etc/sysctl.d/99-tcp-tuning.conf

# 驗證配置生效
sysctl net.core.somaxconn net.ipv4.tcp_tw_reuse net.ipv4.tcp_congestion_control

11.2 參數(shù)調(diào)優(yōu)注意事項

參數(shù) 踩坑點 建議
somaxconn 只改內(nèi)核不改應(yīng)用的 listen backlog 沒用 Nginx:listen 80 backlog=65535
tcp_tw_reuse 只對出站連接(客戶端角色)有效 服務(wù)端 TIME_WAIT 多要從應(yīng)用層解決
tcp_max_tw_buckets 超過限制后直接銷毀 TIME_WAIT,會打日志 設(shè)大一點,別讓它觸發(fā)
tcp_keepalive_time 應(yīng)用層的 Keep-Alive 設(shè)置會覆蓋內(nèi)核參數(shù) 優(yōu)先在應(yīng)用層配置
tcp_rmem/wmem 最大值設(shè)太大會導(dǎo)致內(nèi)存占用過高 根據(jù)實際連接數(shù)和可用內(nèi)存計算
tcp_fastopen 需要客戶端和服務(wù)端都支持,且中間設(shè)備不能剝離 TFO Cookie 先在非關(guān)鍵服務(wù)上測試

11.3 Kubernetes 環(huán)境特殊處理

在 K8s 環(huán)境中,Pod 的內(nèi)核參數(shù)需要通過 securityContext 設(shè)置:

apiVersion:v1
kind:Pod
metadata:
name:web-server
spec:
securityContext:
 sysctls:
 -name:net.core.somaxconn
  value:"65535"
 -name:net.ipv4.tcp_tw_reuse
  value:"1"
 -name:net.ipv4.ip_local_port_range
  value:"1024 65535"
containers:
-name:nginx
 image:nginx:1.27

注意:K8s 默認只允許設(shè)置 "safe" sysctls(net.ipv4.ip_local_port_range等),"unsafe" sysctls(如net.ipv4.tcp_syncookies)需要在 kubelet 配置中顯式允許。

十二、總結(jié)

12.1 技術(shù)要點回顧

TCP 報文格式:理解 Sequence Number、Acknowledgment Number、Flags 這三個字段是看懂抓包的基礎(chǔ)

三次握手:本質(zhì)是交換 ISN + 確認雙方收發(fā)能力,半連接隊列和全連接隊列是生產(chǎn)問題的高發(fā)區(qū)

四次揮手:全雙工關(guān)閉需要雙向獨立進行,TIME_WAIT 是主動關(guān)閉方的必經(jīng)狀態(tài)

TIME_WAIT 優(yōu)化:tcp_tw_reuse+ 擴大端口范圍 + 連接池是三板斧,tcp_tw_recycle已廢棄

Wireshark 過濾器:tcp.flags.syn、tcp.flags.reset、tcp.analysis.retransmission是排查三件套

tcpdump:-nn -S -tttt是標準參數(shù)組合,BPF 過濾器語法必須掌握

重傳機制:超時重傳兜底,快速重傳加速,SACK 精確定位丟失數(shù)據(jù)段

擁塞控制:跨地域選 BBR,數(shù)據(jù)中心內(nèi)部 Cubic 夠用,BBR v3 改善了公平性

內(nèi)核調(diào)優(yōu):somaxconn+tcp_max_syn_backlog+tcp_tw_reuse是高并發(fā)服務(wù)的必調(diào)參數(shù)

12.2 面試高頻問題速查

問題 關(guān)鍵答案
為什么三次握手不是兩次 防止歷史連接初始化 + 確認雙方收發(fā)能力
為什么揮手是四次不是三次 全雙工關(guān)閉,被動方可能還有數(shù)據(jù)要發(fā)(但可以合并為三次)
TIME_WAIT 存在的意義 確保最后 ACK 到達 + 讓舊包在網(wǎng)絡(luò)中消亡
SYN Flood 怎么防御 SYN Cookie + 增大半連接隊列 + 限速
RST 和 FIN 的區(qū)別 FIN 是優(yōu)雅關(guān)閉(四次揮手),RST 是強制中斷(不等對方確認)
BBR 和 Cubic 的區(qū)別 Cubic 基于丟包,BBR 基于帶寬和延遲探測
全連接隊列滿了會怎樣 默認丟棄 ACK(客戶端重試),tcp_abort_on_overflow=1時發(fā) RST

12.3 參考資料

RFC 9293 - TCP 規(guī)范(2022 年更新版,合并了多個舊 RFC)

BBR Congestion Control - Google Research

Wireshark TCP Analysis Documentation

Linux Kernel Networking - TCP Implementation

tcpdump Manual Page

附錄

A. TCP 狀態(tài)機完整圖

               +---------+ ---------   active OPEN
               | CLOSED |        -----------
               +---------+<--------- ?  ? create TCB
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | ? ? ^ ? ? ? ? ? ? ? ?  ?snd SYN
? ? ? ? ? ? ? ? ? ?passive OPEN | ? ? | ? CLOSE ? ? ? ? ? 
? ? ? ? ? ? ? ? ? ?------------ | ? ? | ---------- ? ? ?  ? 
? ? ? ? ? ? ? ? ? ? create TCB ?| ? ? | delete TCB ? ? ? ?  ? 
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? V ? ? | ? ? ? ? ? ? ? ? ? ? ? ? V
? ? ? ? ? ? ? ? ? +----------+ ? ? ? +----------+ ? ? ? +---------+
? ? ? ? ? ? ? ? ? | ?LISTEN ?| ? ? ? | FIN_WAIT | ? ? ? | SYN ? ? |
? ? ? ? ? ? ? ? ? +----------+ ? ? ? | ? ?_2 ? ?| ? ? ? | SENT ? ?|
? ? ? ?rcv SYN ? | ? ? | ? ?| ? ? ? +----------+ ? ? ? +---------+
? ? ? ---------- | ? ? | ? ?| ?CLOSE ? | ? ? ^ ? ?rcv SYN+ACK ?|
? ? ? snd SYN,ACK/ ? ? | ? ?| ------- | ? ? | ? ?---------- ? |
? ? ? ? ? ? ? ? ?/ ? ? ?| ? ?| snd FIN | ? ? | ? ? snd ACK ? ?|
? ? ? ? ? ? ? ? V ? ? ? | ? ?V ? ? ? ? | ? ? | ? ? ? ? ? ? ? ?|
? ? ? ? ?+---------+ ? ?| ?+-------+ ? | ? ? | ? ? ? ? ? ? ? ?V
? ? ? ? ?|SYN_RCVD | ? ?| ?|CLOSING| ? | ? ? | ? ? ? ? +---------+
? ? ? ? ?+---------+ ? ?| ?+-------+ ? | ? ? | ? ? ? ? | ?ESTAB ?|
? ? ? ? ? ?| rcv ACK ? ?| ?rcv ACK| ? ?| ? ? | ? ? ? ? +---------+
? ? ? ? ? ?| ------- ? ?| ?-------| rcv FIN ?| ?CLOSE ? ?| rcv FIN
? ? ? ? ? ?| x ? ? ? ? ?| ?x ? ? ?| ------- ?| ?------- ?| -------
? ? ? ? ? ?V ? ? ? ? ? ?| ? ? ? ? V snd ACK ?| ?snd FIN ?V snd ACK
? ? ? ? ?+---------+ ? ?| ?+---------+ ? ? ? | ? ? ? ? +---------+
? ? ? ? ?| ?ESTAB ?| ? ?| ?|TIME_WAIT| ? ? ? | ? ? ? ? |CLOSE_ ? |
? ? ? ? ?+---------+ ? ?| ?+---------+ ? ? ? | ? ? ? ? | ?WAIT ? |
? ? ? ? ? ? ? | ? ? ? ? | ? ?| 2MSL timeout ?| ? ? ? ? +---------+
? ? ? ? ? ? ? | ? ? ? ? | ? ?| ---------- ? ?| ? ? ? ? CLOSE |
? ? ? ? ? ? ? | ? ? ? ? | ? ?| delete TCB ? ?| ? ? ? ? ------|
? ? ? ? ? ? ? | ? ? ? ? | ? ?V ? ? ? ? ? ? ? | ? ? ? ? snd FIN
? ? ? ? ? ? ? | ? ? ? ? | +---------+ ? ? ? ?| ? ? ? ? ? ? ? V
? ? ? ? ? ? ? | ? ? ? ? +>| CLOSED |    |     +---------+
       |      +---------+    +-------->|LAST_ACK |
       |                    +---------+
       |                     rcv ACK |
       |                     --------|
       |                     x    V
       |                    +---------+
       +--------------------------------------->| CLOSED |
                           +---------+

B. 命令速查表

# 連接狀態(tài)查看
ss -ant                  # 查看所有 TCP 連接狀態(tài)
ss -s                   # TCP 連接統(tǒng)計摘要
ss -lnt                  # 查看監(jiān)聽端口和隊列狀態(tài)
ss -ant state time-wait | wc -l      # 統(tǒng)計 TIME_WAIT 數(shù)量
ss -ti dst 10.0.0.1            # 查看到特定目標的連接詳情(含 RTT/RTO)

# 抓包
tcpdump -i any port 80 -nn -S -tttt   # 標準抓包參數(shù)組合
tcpdump -i any'tcp[tcpflags] & tcp-syn != 0'-nn # 只抓 SYN 包
tcpdump -i any'tcp[tcpflags] & tcp-rst != 0'-nn # 只抓 RST 包

# 內(nèi)核統(tǒng)計
nstat -az TcpExtListenDrops        # 全連接隊列溢出次數(shù)
nstat -az TcpExtListenOverflows      # 全連接隊列溢出次數(shù)(另一個計數(shù)器)
nstat -az TcpExtTCPSynRetrans      # SYN 重傳次數(shù)
nstat -az TcpExtTCPTimeouts       # TCP 超時次數(shù)

# 內(nèi)核參數(shù)查看
sysctl -a | grep tcp           # 查看所有 TCP 相關(guān)參數(shù)
sysctl net.core.somaxconn         # 查看全連接隊列上限
sysctl net.ipv4.tcp_congestion_control  # 查看擁塞控制算法

六、總結(jié)

6.1 技術(shù)要點回顧

三次握手/四次揮手的狀態(tài)機是 TCP 排障的基本功。生產(chǎn)環(huán)境里遇到連接異常,第一反應(yīng)應(yīng)該是ss -ant看狀態(tài)分布,而不是盲目抓包。SYN_RECV 堆積指向半連接隊列溢出或 SYN Flood,CLOSE_WAIT 堆積說明應(yīng)用層沒有正確關(guān)閉連接,F(xiàn)IN_WAIT_2 長期存在則要檢查對端是否還活著。狀態(tài)機搞清楚了,排查方向就不會跑偏。

TIME_WAIT 不是病,但大量堆積需要干預(yù)。TIME_WAIT 存在的意義是防止舊連接的延遲報文干擾新連接,2MSL 的等待時間是協(xié)議設(shè)計的一部分。真正需要處理的是短連接高并發(fā)場景下 TIME_WAIT 數(shù)量達到幾萬甚至十幾萬的情況。優(yōu)先考慮tcp_tw_reuse(安全地復(fù)用 TIME_WAIT 端口),配合連接池和長連接從根源上減少連接創(chuàng)建頻率。tcp_tw_recycle在 NAT 環(huán)境下會導(dǎo)致丟包,內(nèi)核 4.12 之后已經(jīng)移除,別再用了。

BBR 擁塞控制在高延遲高丟包場景下優(yōu)勢明顯。傳統(tǒng)的 Cubic 算法基于丟包檢測來調(diào)整窗口,在跨地域?qū)>€、衛(wèi)星鏈路等高 RTT 場景下表現(xiàn)保守,帶寬利用率上不去。BBR 通過主動探測瓶頸帶寬和最小 RTT 來驅(qū)動發(fā)送速率,不依賴丟包信號,在這類場景下吞吐量提升顯著。但 BBR 也不是銀彈——在淺緩沖交換機環(huán)境下可能造成較高的排隊延遲,部署前需要實測驗證。

Wireshark 和 tcpdump 是網(wǎng)絡(luò)排障的瑞士軍刀。tcpdump 負責在服務(wù)器上輕量抓包,Wireshark 負責離線深度分析。掌握 BPF 過濾表達式、TCP 流追蹤、IO Graph、RTT 統(tǒng)計這幾個核心功能,絕大多數(shù) TCP 層面的問題都能定位到根因。關(guān)鍵是養(yǎng)成習慣:先用ss/nstat看宏觀統(tǒng)計,縮小范圍后再針對性抓包,避免在海量數(shù)據(jù)里大海撈針。

6.2 進階學(xué)習方向

QUIC 協(xié)議(HTTP/3 的傳輸層)

QUIC 把 TCP 的連接管理、TLS 握手、多路復(fù)用全部搬到了用戶態(tài) UDP 之上,從根本上消除了隊頭阻塞問題。三次握手 + TLS 1.3 握手合并為 1-RTT 甚至 0-RTT 建連,對移動網(wǎng)絡(luò)和弱網(wǎng)環(huán)境的體驗提升非常大。理解了 TCP 狀態(tài)機之后再看 QUIC 的連接遷移(Connection Migration)和丟包恢復(fù)機制,會更容易理解它的設(shè)計取舍。

實踐建議:用 Wireshark 4.x 抓取 HTTP/3 流量,對比同一請求在 TCP+TLS 1.3 和 QUIC 下的握手耗時差異。

eBPF 網(wǎng)絡(luò)追蹤(bpftrace / tcplife / tcpconnect)

傳統(tǒng)的 tcpdump 抓包開銷不小,而且只能看到報文層面的信息。eBPF 可以直接在內(nèi)核態(tài)掛載探針,零拷貝地追蹤 TCP 連接的生命周期、RTT 變化、重傳事件,開銷比抓包低一個數(shù)量級。BCC 工具集里的tcplife能實時輸出每條連接的存活時間和傳輸字節(jié)數(shù),tcpretrans能精確定位重傳發(fā)生在哪條流上,排障效率遠超傳統(tǒng)方式。

實踐建議:從bpftrace -e 'kprobe:tcp_retransmit_skb { printf("retrans: %s:%d ", ntop(args->sk->__sk_common.skc_daddr), args->sk->__sk_common.skc_dport); }'這類單行腳本入手,逐步深入。

內(nèi)核網(wǎng)絡(luò)棧源碼閱讀

讀過net/ipv4/tcp_input.c和tcp_output.c之后,很多之前靠經(jīng)驗判斷的問題都能找到確定性答案。比如 SYN Cookie 的具體實現(xiàn)邏輯、快速重傳的觸發(fā)條件、窗口縮放因子的協(xié)商過程,這些在源碼里都是幾十行代碼的事情。配合ftrace或perf跟蹤內(nèi)核函數(shù)調(diào)用路徑,能把抓包現(xiàn)象和內(nèi)核行為完整對應(yīng)起來。

推薦從 Linux 6.x 內(nèi)核入手,代碼結(jié)構(gòu)比早期版本清晰很多,注釋也更完善。

6.3 參考資料

RFC 793 - Transmission Control Protocol- TCP 協(xié)議的原始規(guī)范,三次握手和四次揮手的權(quán)威定義,狀態(tài)機圖就出自這里

RFC 8312 - CUBIC for Fast Long-Distance Networks- Linux 默認擁塞控制算法 CUBIC 的規(guī)范,理解其凹凸函數(shù)窗口增長模型

RFC 9002 - QUIC Loss Detection and Congestion Control- QUIC 的丟包檢測和擁塞控制機制,BBR 在 QUIC 中的應(yīng)用參考

Wireshark User's Guide- Wireshark 官方用戶手冊,TCP 流分析和過濾器語法的權(quán)威參考

tcpdump & libpcap- tcpdump 手冊頁,BPF 過濾表達式的完整語法說明

《TCP/IP 詳解 卷1:協(xié)議》(W. Richard Stevens 著) - TCP 協(xié)議學(xué)習的經(jīng)典教材,第 17-24 章覆蓋了 TCP 連接管理、重傳、擁塞控制的完整細節(jié)

Linux 內(nèi)核源碼 net/ipv4/tcp*.c- Bootlin 提供的在線內(nèi)核源碼瀏覽,直接看 TCP 實現(xiàn)比讀任何二手資料都準確

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 網(wǎng)絡(luò)
    +關(guān)注

    關(guān)注

    14

    文章

    8233

    瀏覽量

    94493
  • TCP
    TCP
    +關(guān)注

    關(guān)注

    8

    文章

    1423

    瀏覽量

    83404
  • Wireshark
    +關(guān)注

    關(guān)注

    0

    文章

    51

    瀏覽量

    6958

原文標題:TCP 三次握手與四次揮手:Wireshark 抓包分析面試必考題

文章出處:【微信號:magedu-Linux,微信公眾號:馬哥Linux運維】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關(guān)推薦
    熱點推薦

    講一講的TCP三次握手四次揮手

    如果你學(xué)過網(wǎng)絡(luò)基礎(chǔ)知識,那么你一定對TCP三次握手不陌生。今天我想用通俗的話來給大家講一講TCP三次握手
    的頭像 發(fā)表于 02-03 10:43 ?3399次閱讀
    講一講的<b class='flag-5'>TCP</b><b class='flag-5'>三次</b><b class='flag-5'>握手</b>和<b class='flag-5'>四次</b><b class='flag-5'>揮手</b>

    三次握手,四次揮手你懂嗎

    程序員面試被問到“三次握手四次揮手”怎么辦?
    發(fā)表于 04-08 07:23

    TCP三次握手過程描述

    本文檔主要描述TCP三次握手過程,一個完整的三次握手也就是 請求---應(yīng)答---再次確認
    發(fā)表于 03-02 15:37 ?8次下載

    TCP/IP協(xié)議工作過程三次握手四次揮手

    )、第三次握手:Client收到確認后,檢查ACK是否為1,如果正確則將標志位ACK置為1,并將該數(shù)據(jù)包發(fā)送給Server,Server檢查ACK是否為1,如果正確則連接建立成功,Client
    的頭像 發(fā)表于 10-25 09:49 ?7557次閱讀

    TCP三次握手過程四次揮手過程說明

    連接 三次握手過程說明: 1. 由客戶端發(fā)送建立 TCP 連接的請求報文,其中報文中包含 seq 序列號,是由發(fā)送端隨機生成的,并且將報文中的 SYN 字段置為 1,表示需要建立
    的頭像 發(fā)表于 03-01 12:00 ?4817次閱讀

    TCP三次握手四次揮手以及11種狀態(tài)資料下載

    電子發(fā)燒友網(wǎng)為你提供TCP三次握手四次揮手以及11種狀態(tài)資料下載的電子資料下載,更有其他相關(guān)的電路圖、源代碼、課件教程、中文資料、英文資料
    發(fā)表于 04-15 08:41 ?2次下載
    <b class='flag-5'>TCP</b><b class='flag-5'>三次</b><b class='flag-5'>握手</b>和<b class='flag-5'>四次</b><b class='flag-5'>揮手</b>以及11種狀態(tài)資料下載

    TCP三次握手四次揮手過程中的異常情況

    TCP 三次握手四次揮手過程中,途中某一步的報文丟失了,會發(fā)生什么?
    的頭像 發(fā)表于 09-05 10:23 ?2011次閱讀

    如何使用WireShark進行TCP三次握手

    WireShark是一種非常方便的網(wǎng)絡(luò)抓包工具,下面演示,使用WireShark來抓取TCP三次握手過程。
    的頭像 發(fā)表于 11-01 09:50 ?2875次閱讀

    TCP建立連接概述及三次握手四次揮手的流程

    具備上述個條件后A獲取B的信息是有要求的,根本上的要求是數(shù)據(jù)信道可靠,就是平時所說的可靠連接,那么如何保證連接的可靠性呢,TCP協(xié)議就是靠確認應(yīng)答機制、超時重傳機制等保證連接可靠性的,接下來就通過TCP協(xié)議的
    的頭像 發(fā)表于 03-23 15:57 ?2027次閱讀
    <b class='flag-5'>TCP</b>建立連接概述及<b class='flag-5'>三次</b><b class='flag-5'>握手</b>、<b class='flag-5'>四次</b><b class='flag-5'>揮手</b>的流程

    說說TCP三次握手過程?為什么是三次而不是兩、四次

    說說TCP三次握手過程?為什么是三次而不是兩、四次
    的頭像 發(fā)表于 02-04 11:03 ?1884次閱讀

    TCP三次握手詳細過程

    TCP三次握手詳細過程: 1. 第一握手:SY
    的頭像 發(fā)表于 01-03 17:11 ?2137次閱讀

    TCP三次握手協(xié)議的作用

    連接,確保數(shù)據(jù)傳輸?shù)目煽啃浴?TCP三次握手協(xié)議的基本概念 TCP三次握手協(xié)議是一種用于在兩個網(wǎng)
    的頭像 發(fā)表于 01-03 17:15 ?1644次閱讀

    TCP三次握手安全性分析

    TCP(傳輸控制協(xié)議)的三次握手是建立可靠連接的重要機制,它確保了通信雙方在數(shù)據(jù)傳輸前的連接狀態(tài)是可靠和準確的。然而,從安全性的角度來分析,TCP
    的頭像 發(fā)表于 01-03 18:10 ?1824次閱讀

    如何監(jiān)測TCP三次握手過程

    在計算機網(wǎng)絡(luò)中,傳輸控制協(xié)議(TCP)是確保數(shù)據(jù)可靠傳輸?shù)年P(guān)鍵協(xié)議之一。TCP通過三次握手過程來建立兩個端點之間的連接,這個
    的頭像 發(fā)表于 01-06 09:20 ?1550次閱讀

    TCP三次握手四次揮手,這樣解釋太通俗易懂了!

    TCP連接的建立和釋放分別通過“三次握手”和“四次揮手”來完成。三次
    的頭像 發(fā)表于 04-24 19:33 ?1515次閱讀
    <b class='flag-5'>TCP</b><b class='flag-5'>三次</b><b class='flag-5'>握手</b>和<b class='flag-5'>四次</b><b class='flag-5'>揮手</b>,這樣解釋太通俗易懂了!