跳转至

Wireshark 抓包分析入门

Fiddler/Charles 看应用层,Wireshark 看传输层与网络层。当接口"根本没发出去""连接建不起来""偶发超时但服务端没日志"时,只有它能给出证据。

一、先确定抓包点

抓错位置,后面全白费。

现象 抓哪里 说明
浏览器请求失败 客户端网卡 能看到 SYN 有没有发出
本机服务(localhost) Loopback 网卡 Windows 用 Npcap 的 Adapter for loopback traffic capture,Linux 用 lo
服务端偶发超时 服务端 tcpdump 区分"没收到"与"收到了没回"
网关后、LB 前 交换机镜像口 / SPAN 判断丢包发生在哪一跳
容器内服务 宿主机 veth 或容器内抓 注意 NAT 后地址变化

Windows 安装 Npcap 时要勾选 "Support loopback traffic capture",否则 127.0.0.1 抓不到。

# Linux 服务端抓包(生产慎用,限制体积与时长)
sudo tcpdump -i any -s 0 -w /tmp/cap.pcapng 'tcp port 8080 and host 10.0.0.5' -C 100 -W 5

参数含义:

  • -s 0:抓完整包,不截断(默认 262144 在现代版本已是全量,但显式写更稳)
  • -C 100 -W 5:每个文件 100MB,最多 5 个滚动
  • -i any:Linux cooked capture,能同时看多网卡;只关心单网卡时指定 eth0

二、捕获过滤器 vs 显示过滤器

类型 位置 语法 作用
捕获过滤器(BPF) 抓包前 tcp port 443 and host 10.0.0.5 减少文件体积,抓不到就永远没有
显示过滤器 抓包后 tls.handshake.extensions_server_name contains "api" 不影响抓包,只影响视图

捕获过滤器写错,包就永远丢了,排查阶段建议只按 host 粗过滤,甚至不过滤。

三、显示过滤器速查

# 连接与握手
tcp.flags.syn == 1 && tcp.flags.ack == 0          # 所有 SYN
tcp.flags.reset == 1                              # 所有 RST
tcp.analysis.retransmission                       # 重传
tcp.analysis.duplicate_ack                        # 重复 ACK
tcp.analysis.zero_window                          # 接收窗口为 0
tcp.analysis.window_full                          # 发送方窗口满
tcp.analysis.initial_rtt                          # 握手 RTT

# 应用层
http.request.method == "POST"
http.response.code >= 500
http.time > 1                                     # 应用处理超过 1 秒
http.request.uri contains "/api/order"
http.content_type contains "json"

# TLS / DNS
tls.handshake.type == 1                           # ClientHello
tls.handshake.extensions_server_name              # SNI
tls.alert_message                                  # 握手/解密告警
dns.qry.name contains "api.example.com"
dns.flags.rcode != 0                              # DNS 解析失败

# 组合
ip.addr == 10.0.0.5 && tcp.port == 8080 && http
frame contains "订单号"

tcp.analysis.* 这些字段需要 Wireshark 开启 TCP 分析(默认开启)。如果全是灰色不可用,检查 Analyze → Enabled Protocols → TCP。

四、TCP 基础:只看四个点

4.1 三次握手

Client → Server  SYN      seq=0        options: MSS=1460, SACK_PERM, WS=128
Server → Client  SYN,ACK  seq=0 ack=1  options: MSS=1440, SACK_PERM, WS=64
Client → Server  ACK      seq=1 ack=1

看三件事:

  • MSS 协商:两端取小值。若中间有 VPN/隧道,MSS 变小会显著影响大请求。
  • Window Scale:没有它,窗口最大 64KB,高延迟链路吞吐上不去。
  • 握手耗时:tcp.analysis.initial_rtt 就是一次 RTT,跨地域 30~80ms 很常见,直接影响 TPS。

4.2 四次挥手与 RST

Client → Server  FIN,ACK
Server → Client  ACK
Server → Client  FIN,ACK
Client → Server  ACK

真正麻烦的是 RST:

  • 服务端返回 RST:端口没监听、连接被防火墙重置、keep-alive 连接已被服务端关闭但客户端还在复用。
  • 客户端发 RST:应用崩溃、连接池主动关闭。

4.3 序号与重传

Frame 120: seq=1000 len=1460
Frame 121: seq=2460 len=1460
Frame 122: seq=1000 len=1460   ← tcp.analysis.retransmission

重传间隔通常是 1s、3s、7s(指数退避)。看到连续重传,优先怀疑丢包或服务端未及时 ACK,而不是应用慢。

4.4 零窗口

tcp.analysis.zero_window 表示接收方缓冲区满了,发送方必须停。根因通常是应用读得太慢(GC、锁、慢 SQL),不是网络。这时看服务端线程栈比看包更有效。

五、RT 分解:把"慢"拆成四段

一个 HTTPS 请求的耗时:

DNS 解析  +  TCP 握手  +  TLS 握手  +  请求发送到首字节(TTFB)  +  内容传输

在 Wireshark 里逐段读时间戳:

  1. dns.qry.name == "api.example.com" → 看 Query 与 Response 的时间差;
  2. 找到对应 IP 的 SYN 与 SYN,ACK → 差值即 TCP 握手;
  3. tls.handshake.type == 1(ClientHello)到 type == 2(ServerHello)再到 Finished → TLS 握手;
  4. http.request 到 http.response 的第一帧 → TTFB;
  5. 最后一帧 DATA 到连接空闲 → 传输时间。

用 Frame → Time Display Format → Seconds Since Previous Displayed Packet 可以快速看相邻包间隔。更精确的做法是 Statistics → Conversations 按流看 duration。

六、五个真实故障的定位过程

6.1 连接超时:SYN 发出但没有 SYN,ACK

No. 1  10.0.0.5 → 10.0.0.9  SYN
No. 2  10.0.0.5 → 10.0.0.9  SYN   (1.0s 后重传)
No. 3  10.0.0.5 → 10.0.0.9  SYN   (3.0s 后重传)

判定:

  • 只有 SYN、无回应 → 中间防火墙丢包、安全组未放行、服务未监听(但服务未监听通常回 RST)。
  • 有 RST 立即返回 → 端口未开或 LB 后端全挂。
  • 服务端同时抓包:服务端能看到 SYN → 问题在回程或被本机防火墙丢;看不到 → 问题在去程链路。

6.2 TLS 握手失败

ClientHello  (supported_versions: TLS 1.3, cipher_suites: ...)
Alert (Level: Fatal, Description: Protocol Version)

或:

Alert (Level: Fatal, Description: Handshake Failure)
  • Protocol Version:客户端与服务端没有交集,常见于老客户端/新服务端强制 TLS1.2+。
  • Handshake Failure:没有共同 cipher suite,或客户端证书缺失(mTLS)。
  • Unknown CA:客户端不信任服务端证书链(自签、缺中间证书)。

用 openssl 交叉验证:

openssl s_client -connect api.example.com:443 -servername api.example.com -tls1_2 </dev/null
openssl s_client -connect api.example.com:443 -servername api.example.com -showcerts </dev/null 2>&1 | openssl x509 -noout -dates -subject -issuer

6.3 偶发 502:keep-alive 复用到了已关闭的连接

Frame 500: GET /api/order HTTP/1.1   (复用连接)
Frame 501: 服务端 → 客户端  FIN,ACK
Frame 502: 客户端 → 服务端  GET /api/order   ← 发到了已关闭的连接
Frame 503: 服务端 → 客户端  RST

根因:LB 的空闲超时(如 60s)短于客户端连接池的 keep-alive(如 90s)。客户端复用了服务端已关闭的连接。

修复:

  • 客户端 keep-alive < LB idle timeout(如 50s);
  • 开启 TCP keepalive;
  • 对幂等请求做一次重试。

这类问题在应用日志里只看到"connection reset",只有抓包能看清顺序。

6.4 大 POST 卡住:MTU 与分片

Frame 1: POST /upload  len=1460
...
Frame N: 服务端无响应
ICMP: Destination Unreachable (Fragmentation needed, MTU 1400)

判定:请求体超过路径 MTU 且 DF 位被设置,中间设备不回 ICMP 或 ICMP 被过滤 → 黑洞。

# 探测路径 MTU(Linux)
ping -M do -s 1472 api.example.com      # 1472 + 28 = 1500
ping -M do -s 1400 api.example.com

修复:调小接口 MTU/MSS clamp,或在应用层分片上传。

6.5 重传风暴:丢包还是服务端慢

tcp.analysis.retransmission 大量出现
tcp.analysis.duplicate_ack 大量出现
  • 重传 + dup ack + 窗口正常 → 链路丢包(看 Statistics → TCP Stream Graphs → Time-Sequence (tcptrace))。
  • 重传 + 零窗口 → 接收端应用慢。
  • 只有 dup ack 没有重传 → 对端在等缺失段,可能也是丢包。

Statistics → TCP Stream Graphs → Throughput 能看到吞吐随时间的变化,丢包会表现为锯齿。

七、解密 HTTPS:SSLKEYLOGFILE

Wireshark 不能"破解"TLS,但可以用客户端导出的密钥日志解密。

# 让 curl / Chrome / Firefox 导出握手密钥
export SSLKEYLOGFILE=/tmp/sslkey.log
curl https://api.example.com/health

Wireshark 配置:

Preferences → Protocols → TLS → (Pre)-Master-Secret log filename: /tmp/sslkey.log

之后同一会话的流量会自动解密成 HTTP/2 或 HTTP/1.1。

限制:

  • 必须在抓包前设置环境变量,事后无法补;
  • TLS 1.3 同样支持(key log 里包含 handshake secrets),但需要 Wireshark 3.0+;
  • Java 应用需要 -Djavax.net.ssl.keyStore 之外的方式:JDK 不支持 SSLKEYLOGFILE,只能改代码或使用 jSSLKeyLog agent;
  • 移动 App 需要可调试包 + 注入,通常改用 Fiddler/Burp 代理。

八、HTTP/2 与 WebSocket

8.1 HTTP/2

Wireshark 显示 HTTP2,需要看:

  • HEADERS 帧里的 :method、:path、:authority
  • DATA 帧的 stream id(同一连接多路复用)
  • SETTINGS、WINDOW_UPDATE、RST_STREAM

常见问题:RST_STREAM 表示单个流被取消(超时、客户端主动取消),连接本身没断。只看 TCP 层会误判为"没断"。

8.2 WebSocket

GET /ws HTTP/1.1
Upgrade: websocket
Connection: Upgrade
...
HTTP/1.1 101 Switching Protocols

之后是 WebSocket 帧,Follow → TCP Stream 仍能看到原始文本。

九、导出与命令行统计

9.1 导出对象

File → Export Objects → HTTP     # 导出图片、JSON、JS
File → Export Specified Packets  # 只导出过滤后的包

9.2 tshark 批量分析

# 列出所有 5xx 请求
tshark -r cap.pcapng -Y "http.response.code >= 500" \
  -T fields -e frame.time_relative -e ip.src -e http.request.uri

# 统计每个域名的请求数
tshark -r cap.pcapng -Y "http.request" -T fields -e http.host \
  | sort | uniq -c | sort -rn | head

# 统计重传次数
tshark -r cap.pcapng -Y "tcp.analysis.retransmission" -T fields -e ip.src \
  | sort | uniq -c

9.3 关键统计

Statistics → Conversations     # 每条流的字节数、时长、包数
Statistics → HTTP → Requests   # 请求分布
Statistics → Service Response Time → HTTP   # 各 URI 的响应时间
Statistics → TCP Stream Graphs # 吞吐、往返时间、窗口

Service Response Time 能按 URI 聚合,比逐条看快得多。

十、常见误区

  1. 在客户端抓包判断"服务端没收到":只能证明包发出去了,服务端是否收到必须在服务端抓。
  2. 捕获过滤器写得过窄:排查时漏掉 DNS、ICMP、重传。
  3. 把 RST 当成业务错误:RST 是连接层,应用日志往往没有记录。
  4. 看到重传就怪网络:零窗口导致的停顿是应用慢。
  5. 忽略时间基准:客户端与服务端时钟不同步,跨机对比必须先校时(NTP)。
  6. 抓包文件过大:生产上用 -C/-W 滚动,先粗过滤再细看。

十一、检查清单

  • 抓包点选对(客户端/服务端/loopback)
  • 已复现问题并记录了准确时间点
  • 捕获过滤器尽量宽,显示过滤器再收窄
  • 检查 SYN 是否有回应、是否有 RST
  • 检查 TLS alert 与证书链
  • 用 tcp.analysis.* 看过重传、dup ack、零窗口
  • RT 已按 DNS/TCP/TLS/TTFB 分段
  • 需要看明文时已提前设置 SSLKEYLOGFILE
  • 结论有双向抓包或服务端日志佐证

相关阅读