国产精品久久久aaaa,日日干夜夜操天天插,亚洲乱熟女香蕉一区二区三区少妇,99精品国产高清一区二区三区,国产成人精品一区二区色戒,久久久国产精品成人免费,亚洲精品毛片久久久久,99久久婷婷国产综合精品电影,国产一区二区三区任你鲁

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

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

3天內不再提示

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

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

掃碼添加小助手

加入工程師交流群

一、概述

1.1 背景介紹

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

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

1.2 技術特點

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

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

狀態機完整:覆蓋 TCP 全部 11 種狀態及其轉換條件

生產導向:重點放在 TIME_WAIT 優化、半連接隊列溢出、RST 排查等實際問題

1.3 適用場景

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

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

場景三:高并發服務的內核網絡參數調優,解決 TIME_WAIT 堆積、SYN Flood 等問題

場景四:微服務架構下的連接管理優化,理解連接復用和優雅關閉

1.4 環境要求

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

二、TCP 報文格式

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

2.1 TCP Header 結構

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

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 關鍵字段解析

重點關注幾個和握手揮手直接相關的字段:

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

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

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

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

窗口大小(Window,16 位):接收方告訴發送方自己還能接收多少字節的數據。配合 Window Scale 選項可以擴展到 30 位。

2.3 TCP Options

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

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

三、三次握手詳細過程

3.1 握手全景

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

  客戶端                     服務端
   |                        |
   | [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 逐包拆解

第一次握手:客戶端發送 SYN

客戶端調用connect()系統調用,內核構造一個 SYN 報文發出去:

標志位:SYN=1

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

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

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

客戶端狀態從 CLOSED 變為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 位隨機數。可以在 Edit -> Preferences -> Protocols -> TCP 中關閉 "Relative sequence numbers" 查看真實值。

第二次握手:服務端回復 SYN+ACK

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

標志位:SYN=1, ACK=1

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

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

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

服務端狀態從 LISTEN 變為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

第三次握手:客戶端發送 ACK

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

標志位:ACK=1

序列號:Seq=x+1

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

客戶端狀態變為ESTABLISHED。服務端收到這個 ACK 后,從半連接隊列移到全連接隊列(Accept Queue),狀態也變為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 包是可以攜帶數據的(TCP Fast Open 就利用了這一點),但通常情況下 Len=0。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

四、四次揮手詳細過程

4.1 揮手全景

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

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

4.2 逐包拆解

第一次揮手:主動關閉方發送 FIN

應用層調用close()或shutdown(SHUT_WR),內核發送 FIN 報文:

標志位:FIN=1, ACK=1

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

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

主動關閉方狀態從 ESTABLISHED 變為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

第二次揮手:被動關閉方回復 ACK

被動方內核收到 FIN 后自動回復 ACK,應用層通過read()返回 0 感知到對方關閉:

標志位:ACK=1

確認號:Ack=u+1

被動關閉方狀態變為CLOSE_WAIT,主動關閉方收到后變為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

第三次揮手:被動關閉方發送 FIN

被動方處理完剩余數據后調用close(),內核發送 FIN:

標志位:FIN=1, ACK=1

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

被動關閉方狀態變為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

第四次揮手:主動關閉方回復 ACK

主動方回復最后一個 ACK,進入TIME_WAIT狀態:

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 四次揮手能變成三次嗎

可以。如果被動關閉方在收到 FIN 時恰好也沒有數據要發了,內核會把 ACK 和 FIN 合并成一個包(FIN+ACK),這就變成了三次揮手。在 Wireshark 中經常能看到這種情況,Linux 內核默認開啟了 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 狀態詳解

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

為什么需要 TIME_WAIT:

確保最后一個 ACK 到達:如果最后的 ACK 丟失,被動方會重傳 FIN,主動方需要在 TIME_WAIT 狀態下重新發送 ACK。如果直接進入 CLOSED,收到重傳的 FIN 后會回復 RST,導致被動方異常關閉。

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

TIME_WAIT 堆積的危害:

# 查看 TIME_WAIT 連接數量
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 內存(內核用 inet_timewait_sock 結構體,比完整的 tcp_sock 小得多),28000 個也就 7MB,內存不是主要問題。真正的問題是端口耗盡——客戶端的臨時端口范圍默認是 32768-60999,總共 28232 個,如果對同一個目標 IP:Port 的 TIME_WAIT 連接占滿了這個范圍,新連接就建不了了。

TIME_WAIT 優化方案:

# 方案一:開啟 tcp_tw_reuse(推薦)
# 允許在 TIME_WAIT 狀態的端口被新的出站連接復用
# 前提是新連接的 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"

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

注意:tcp_tw_recycle在 Linux 4.12 之后已經被移除了,不要再用這個參數。它在 NAT 環境下會導致大量連接失敗。

五、Wireshark 抓包實戰

5.1 抓包準備

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

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

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

5.2 Wireshark 過濾器速查

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

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

# 標志位過濾(抓握手揮手的關鍵)
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 包(排查異常斷連)

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

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

5.3 TCP 流追蹤

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

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

5.4 實戰:分析一次完整的 HTTPS 連接

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

包序號 方向       內容          說明
------ ----       ----          ----
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 響應
10   Client -> Server [FIN, ACK]       TCP 四次揮手開始
11   Server -> Client [FIN, ACK]       合并的 FIN+ACK
12   Client -> Server [ACK]          連接關閉完成

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

六、tcpdump 命令行抓包技巧

生產服務器通常沒有 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

# 只抓包頭不抓數據(節省磁盤空間)
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 的包(零窗口,接收方緩沖區滿)
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

# 按時間輪轉保存(長時間抓包)
# -G 3600: 每小時輪轉一次
# -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 的可靠傳輸依賴重傳機制。理解重傳對排查網絡丟包、延遲抖動等問題至關重要。

7.1 超時重傳(RTO Retransmission)

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

RTO 的計算基于 RTT 的采樣值,Linux 使用 Jacobson 算法動態調整:

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 翻倍(指數退避),依次是 200ms、400ms、800ms、1.6s...直到達到最大重傳次數(tcp_retries2默認 15 次,總計約 15 分鐘)。

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

7.2 快速重傳(Fast Retransmit)

等 RTO 超時太慢了。快速重傳機制在收到 3 個重復 ACK(Duplicate ACK)時立即重傳丟失的數據段,不用等定時器超時。

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

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

7.3 SACK(選擇性確認)

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

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

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

八、TCP 擁塞控制:BBR vs Cubic

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

8.1 Cubic(Linux 默認)

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

工作階段:

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

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

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

Cubic 的問題:

在高帶寬高延遲(長肥管道)網絡中,丟包恢復太慢,帶寬利用率低

把丟包等同于擁塞,但現代網絡中丟包可能是隨機的(無線網絡)

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

8.2 BBR(Bottleneck Bandwidth and RTT)

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

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

BBR 的優勢:

高帶寬利用率:在長肥管道中表現遠優于 Cubic

低延遲:不會填滿路由器緩沖區

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

BBR 的注意事項:

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

在低延遲局域網中,BBR 和 Cubic 差異不大

需要內核 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 高延遲網絡下帶寬利用率遠高于 Cubic
CDN 邊緣節點 BBR 面對各種網絡質量的客戶端,BBR 適應性更強
數據中心內部 Cubic 低延遲環境差異不大,Cubic 公平性更好
視頻流媒體 BBR 低延遲 + 高吞吐的組合適合實時傳輸
與大量 Cubic 流共存 Cubic 或 BBR v3 BBR v1/v2 的公平性問題在 v3 中有改善

九、最佳實踐

9.1 連接管理最佳實踐

服務端:

# /etc/sysctl.conf 推薦配置

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

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

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

# TIME_WAIT 相關
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

應用層:

使用 HTTP/2 或 gRPC 多路復用,減少連接數

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

使用連接池管理數據庫和緩存連接

優雅關閉:先shutdown(SHUT_WR)再close(),給對方發送剩余數據的機會

9.2 抓包分析最佳實踐

抓包時用 BPF 過濾器縮小范圍,避免抓到無關流量導致文件過大

生產環境抓包設置-c(包數量限制)或-G -W(時間輪轉),防止磁盤寫滿

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

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

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

# 用 tshark 導出特定流的數據
tshark -r capture.pcap -Y"tcp.stream eq 5"-w stream5.pcap

十、常見 TCP 問題排查

10.1 連接超時(Connection Timeout)

現象:客戶端connect()長時間無響應,最終超時報錯。

排查思路:

# 第一步:確認服務端端口是否在監聽
ss -lnt | grep :443

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

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

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

常見原因和解決方案:

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

10.2 RST 包排查

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

常見 RST 場景:

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

# 場景二:應用異常關閉連接
# 應用設置了 SO_LINGER l_onoff=1, l_linger=0,close() 時發 RST 而非 FIN
# 或者應用在接收緩沖區還有未讀數據時調用 close()

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

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

RST 排查命令:

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

# 統計 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 的統計
ss -s
# 關注 "reset" 相關的計數

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

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

檢測方法:

# 查看 SYN_RECV 狀態的連接數
ss -ant state syn-recv | wc -l

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

# 查看 SYN Cookie 觸發次數
netstat -s | grep"SYN cookies"

# 用 nstat 看增量統計(比 netstat -s 更適合監控)
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 重傳次數(加快清理無效半連接)
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

十一、內核參數調優完整方案

11.1 生產環境推薦配置

以下是經過大量生產驗證的 TCP 內核參數配置,適用于高并發 Web 服務場景:

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

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

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

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

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

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

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

# ============ 其他優化 ============
# 開啟 TCP Fast Open(客戶端和服務端都支持)
net.ipv4.tcp_fastopen = 3
# 開啟 SACK
net.ipv4.tcp_sack = 1
# 開啟窗口縮放
net.ipv4.tcp_window_scaling = 1
# 全連接隊列溢出時的行為(0=丟棄ACK,1=發RST)
# 生產環境建議設為 0,讓客戶端重試而非收到 RST
net.ipv4.tcp_abort_on_overflow = 0
# 應用配置
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 參數調優注意事項

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

11.3 Kubernetes 環境特殊處理

在 K8s 環境中,Pod 的內核參數需要通過 securityContext 設置:

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 默認只允許設置 "safe" sysctls(net.ipv4.ip_local_port_range等),"unsafe" sysctls(如net.ipv4.tcp_syncookies)需要在 kubelet 配置中顯式允許。

十二、總結

12.1 技術要點回顧

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

三次握手:本質是交換 ISN + 確認雙方收發能力,半連接隊列和全連接隊列是生產問題的高發區

四次揮手:全雙工關閉需要雙向獨立進行,TIME_WAIT 是主動關閉方的必經狀態

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

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

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

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

擁塞控制:跨地域選 BBR,數據中心內部 Cubic 夠用,BBR v3 改善了公平性

內核調優:somaxconn+tcp_max_syn_backlog+tcp_tw_reuse是高并發服務的必調參數

12.2 面試高頻問題速查

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

12.3 參考資料

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

BBR Congestion Control - Google Research

Wireshark TCP Analysis Documentation

Linux Kernel Networking - TCP Implementation

tcpdump Manual Page

附錄

A. TCP 狀態機完整圖

               +---------+ ---------   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. 命令速查表

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

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

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

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

六、總結

6.1 技術要點回顧

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

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

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

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

6.2 進階學習方向

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

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

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

eBPF 網絡追蹤(bpftrace / tcplife / tcpconnect)

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

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

內核網絡棧源碼閱讀

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

推薦從 Linux 6.x 內核入手,代碼結構比早期版本清晰很多,注釋也更完善。

6.3 參考資料

RFC 793 - Transmission Control Protocol- TCP 協議的原始規范,三次握手和四次揮手的權威定義,狀態機圖就出自這里

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

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

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

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

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

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

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 網絡
    +關注

    關注

    14

    文章

    8264

    瀏覽量

    94715
  • TCP
    TCP
    +關注

    關注

    8

    文章

    1424

    瀏覽量

    83501
  • Wireshark
    +關注

    關注

    0

    文章

    51

    瀏覽量

    6960

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

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

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

掃碼添加小助手

加入工程師交流群

    評論

    相關推薦
    熱點推薦

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

    如果你學過網絡基礎知識,那么你一定對TCP三次握手不陌生。今天我想用通俗的話來給大家講一講TCP三次握手
    的頭像 發表于 02-03 10:43 ?3412次閱讀
    講一講的<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>

    三次握手四次揮手你懂嗎

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

    TCP三次握手過程描述

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

    TCP/IP協議工作過程三次握手四次揮手

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

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

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

    TCP三次握手四次揮手以及11種狀態資料下載

    電子發燒友網為你提供TCP三次握手四次揮手以及11種狀態資料下載的電子資料下載,更有其他相關的電路圖、源代碼、課件教程、中文資料、英文資料
    發表于 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種狀態資料下載

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

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

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

    WireShark是一種非常方便的網絡抓包工具,下面演示,使用WireShark來抓取TCP三次握手過程
    的頭像 發表于 11-01 09:50 ?2884次閱讀

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

    具備上述個條件后A獲取B的信息是有要求的,根本上的要求是數據信道可靠,就是平時所說的可靠連接,那么如何保證連接的可靠性呢,TCP協議就是靠確認應答機制、超時重傳機制等保證連接可靠性的,接下來就通過TCP協議的
    的頭像 發表于 03-23 15:57 ?2044次閱讀
    <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三次握手過程?為什么是三次而不是兩四次
    的頭像 發表于 02-04 11:03 ?1894次閱讀

    TCP三次握手詳細過程

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

    TCP三次握手協議的作用

    連接,確保數據傳輸的可靠性。 TCP三次握手協議的基本概念 TCP三次握手協議是一種用于在兩個網
    的頭像 發表于 01-03 17:15 ?1673次閱讀

    TCP三次握手安全性分析

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

    如何監測TCP三次握手過程

    在計算機網絡中,傳輸控制協議(TCP)是確保數據可靠傳輸的關鍵協議之一。TCP通過三次握手過程來建立兩個端點之間的連接,這個
    的頭像 發表于 01-06 09:20 ?1567次閱讀

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

    TCP連接的建立和釋放分別通過“三次握手”和“四次揮手”來完成。三次
    的頭像 發表于 04-24 19:33 ?1543次閱讀
    <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>,這樣解釋太通俗易懂了!