tshark 命令行抓包分析实战:HTTP 状态码、TLS SNI 与 TCP 重传定位全流程

为什么服务器排障需要 tshark,而不只是 tcpdump

tcpdump 是每个运维都装在服务器上的第一工具,但它有个明显的短板:它只负责"抓",不负责"看懂"。抓下来的 pcap 是二进制流,你只能靠脑袋记 TCP 序号、靠肉眼对齐 16 进制偏移。当问题从"网络通不通"升级到"这条 HTTPS 请求为什么慢"、"这个握手为什么重传"、"响应里到底返回了什么状态码"时,tcpdump 的裸输出就不够用了。

tshark 是 Wireshark 的命令行版本,它把 Wireshark 那套强大的协议解析引擎(Dissector)搬到了终端。同样是抓包,tshark 能直接把每个包翻译成"HTTP 200 / TLS Client Hello / DNS query"这样的人话,还支持显示过滤器、字段抽取、统计输出。最关键的是,它不需要图形界面,可以装在任何纯 SSH 的 Linux 服务器上,也能直接读 Wireshark 生成的 pcap 文件。本文不空讲概念,全部围绕"服务器上真实会遇到的排障场景"来写。

安装与版本差异

# Debian/Ubuntu
apt install -y tshark
# 安装时会弹窗问是否允许非 root 抓包,选 Yes 会把当前用户加进 wireshark 组
# 如果错过了这个弹窗,之后手动加:
usermod -aG wireshark $USER

tshark -v | head -3
# 查看支持的网卡
tshark -D

注意 tshark -D 列出的接口带编号(如 1. eth0),抓包时可以用编号也可以用名字。-i any 表示抓所有接口,但要注意 any 这个伪接口在部分内核上会把链路层头信息裁掉,如果需要看 MAC 地址,必须指定真实网卡名。

场景一:抓 HTTP 请求看真实状态码和响应头

最常见的需求:某个接口偶尔返回 500,但应用日志没记全,或者你想确认 Nginx 到底有没有把缓存命中的响应发出去。直接在服务器上抓明文 HTTP:

# 抓 eth0 上目标端口 80、host 是 example.com 的包,只显示 HTTP 层
tshark -i eth0 -f "tcp port 80" -Y "http" -V

# -f 是捕获过滤器(BPF 语法,在抓之前就过滤,省 CPU、省磁盘)
# -Y 是显示过滤器(Wireshark 语法,在抓之后过滤,能做深度字段匹配)
# -V 展开每个包的完整协议树,能看到所有 HTTP 头

只想看关键字段、不想被协议树淹没,用 -T fields 精确抽取:

tshark -i eth0 -f "tcp port 80" -Y "http.response" \
  -T fields -e ip.src -e ip.dst -e http.response.code -e http.content_length

# 输出示例:
# 10.0.0.1   10.0.0.2   200   3412
# 10.0.0.1   10.0.0.2   500   0

-e 后面跟的是字段名,字段名从哪来?看 -V 输出里每个字段前面方括号里的名字,或者查 Wireshark 的 Display Filter Reference。-T fields 最大的价值是输出可以直接喂给 awk/sort/uniq 做统计,例如统计各类状态码出现次数:

tshark -i eth0 -f "tcp port 80" -Y "http.response" \
  -T fields -e http.response.code | sort | uniq -c | sort -rn

场景二:HTTPS 也能看到域名和 SNI

很多人以为 HTTPS 抓包什么都看不到。其实 TLS 握手过程中的 Client Hello 里包含 SNI(Server Name Indication),也就是客户端想访问哪个域名,这在 TLS 1.3 里依然是明文的;证书里的域名同样可见。用于排查"到底连到了哪个站点"、"证书是不是过期了"非常有效:

# 抓所有 TLS 握手里的 SNI
tshark -i eth0 -f "tcp port 443" -Y "tls.handshake.extensions_server_name" \
  -T fields -e ip.src -e tls.handshake.extensions_server_name

# 看服务端证书的 CN 和有效期
tshark -i eth0 -f "tcp port 443" -Y "tls.handshake.type == 11" \
  -T fields -e tls.handshake.certificate

# 统计 TLS 握手失败的次数(alert 包)
tshark -i eth0 -f "tcp port 443" -Y "tls.alert_message" -T fields -e tls.alert_message.desc

这套排查思路的实战价值:某次发现服务器上有异常外联,用第一条命令抓 SNI,几秒钟就能看清是哪个进程在连哪个域名——这比对着 IP 反查归属地准得多,因为在 CDN 场景下一个 IP 后面挂着成千上万个域名。

场景三:找出"慢"到底慢在哪一段

"网站慢"是运维最难接的一类投诉。tshark 能做一件 tcpdump 做不到的事:把一次 TCP 会话的各阶段耗时拆开。先抓包存盘,再用统计功能分析:

# 后台抓 200 个包到文件
tshark -i eth0 -f "tcp port 443" -c 200 -w /tmp/slow.pcap

# 打印每个 TCP 流的会话统计:包数、字节数、起止时间、持续时间
tshark -r /tmp/slow.pcap -q -z conv,tcp

# 输出示例:
# TCP Conversations
#                               |       <-      | |       ->      | |     Total     |   Relative    |
# 10.0.0.1:52344 <-> 10.0.0.2:443  15 2.1 kB 12 18 kB 27 20 kB  0.1521  1.2034
# 最后一列就是这条连接从第一个包到最后一个包持续了 1.2 秒

看到某条连接持续了很久,下一步用 -z io,stat 看吞吐随时间的变化,或者直接看 TCP 分析专表:

# TCP 专家信息:重传、乱序、零窗口、连接重置一目了然
tshark -r /tmp/slow.pcap -q -z expert

# 只挑出重传的包
tshark -r /tmp/slow.pcap -Y "tcp.analysis.retransmission" \
  -T fields -e frame.number -e ip.src -e tcp.seq -e tcp.len

# 看是否存在零窗口(对端处理不过来)
tshark -r /tmp/slow.pcap -Y "tcp.analysis.zero_window" \
  -T fields -e ip.src -e tcp.window_size_value

这三个诊断点各有明确指向:大量重传说明链路丢包或对端拥塞;零窗口说明接收端应用处理不过来(往往是应用层 bug,不是网络问题);连接重置(RST)可能是对端主动拒绝、也可能是中间设备拦截。学会读这几种标记,你就能在"网络问题"和"应用问题"之间做出正确归因,而不是把锅乱甩。

场景四:DNS 解析异常

DNS 慢或者解析错是网站间歇性失败的常见元凶。tshark 抓 DNS 一抓一个准:

# 抓所有 DNS 查询和响应,看应答时间与返回地址
tshark -i eth0 -f "port 53" -Y "dns" \
  -T fields -e frame.time_relative \
              -e dns.qry.name \
              -e dns.a \
              -e dns.time

# 只看解析失败的(NXDOMAIN 或响应码非 0)
tshark -i eth0 -f "port 53" -Y "dns.flags.rcode != 0" \
  -T fields -e ip.src -e dns.qry.name -e dns.flags.rcode

dns.time 字段直接给出"从查询发出到收到响应"的毫秒数。如果某个域名解析耗时动辄几百毫秒甚至超时重试,问题在 DNS 服务器而不是网站本身,对应到实际就是首页首字节时间(TTFB)被无谓拉长。这个字段能帮你把"配置了错误的 DNS 服务器"这种低级但高频的错误直接抓出来。

抓包落盘与分析分离:生产环境的正确姿势

在繁忙的服务器上直接跑 tshark 实时看输出是不现实的——终端刷屏速度比你看的速度快得多,而且一旦你退出 SSH,前台进程就被杀掉了。正确做法是抓包落盘、离线分析:抓的时候只负责把流量写进文件,分析的时候再把文件读出来慢慢看,两个阶段彻底分开。这样做还有一个额外好处:抓下来的 pcap 是一份可以反复回看、可以在团队间传阅的证据,而不是一闪而过的终端输出。

# 限制文件大小和数量,避免把磁盘写满(这是最容易被忽略的坑)
tshark -i eth0 -f "tcp port 443" \
  -w /tmp/cap.pcap \
  -b filesize:102400 \
  -b files:10

# filesize 单位是 KB,这里每个文件 100MB,最多保留 10 个循环覆盖
# 抓完后把 pcap 拉回本地用 Wireshark 图形界面细看,体验最好

想让它长期在后台盯着,就包成 systemd 服务或者放进 tmux;想定时抓一段就写进 cron,配合 -a duration:60 抓满 60 秒自动停止。这套"落盘 + 定时"的组合,能让服务器的网络流量从"看不见"变成"随时可回溯"。

捕获过滤器 -f 的写法直接决定 CPU 和磁盘开销。它的 BPF 语法和 tcpdump 完全一致,常见的写法有:host 10.0.0.5、net 10.0.0.0/24、tcp port 443、port 80 or port 443、not port 22(排除自己的 SSH,否则你的操作会把自己刷屏)。尽量在 -f 里就过滤掉无关流量,因为 -Y 显示过滤器是在内核抓到包之后才生效的,包已经被复制到用户态、已经占用了 CPU 和内存。

另外要理解一个常被忽略的点:-f 过滤在内核抓包阶段执行,被过滤掉的包根本不会进入用户态,所以它对性能几乎无损;而 -Y 过滤在tshark 用户态执行,即使最终一条都不显示,那些包也已经完整读进了内存。所以面对高速流量时,-f 越精准越省资源,-Y 只适合做二次精筛,绝不能用它来代替 -f 做流量裁剪。

用 tshark 做流量的"取文件"操作

tshark 还能从 pcap 里重新导出传输的文件,这在排查"对端到底返回了什么"时很有用:

# 导出所有 HTTP 对象到 /tmp/export 目录
tshark -r /tmp/cap.pcap --export-objects http,/tmp/export

# 列出 pcap 里出现过的所有 HTTP 请求 URL
tshark -r /tmp/cap.pcap -Y "http.request" \
  -T fields -e http.host -e http.request.uri

# 导出证书文件,方便用 openssl 进一步检查
tshark -r /tmp/cap.pcap -Y "tls.handshake.certificate" \
  -T fields -e tls.handshake.certificate | head -1 | \
  tr -d '\n' | openssl x509 -inform DER -noout -text

常见坑位

  • 权限不足: 非 root 用户需要属于 wireshark 组,或者用 setcap cap_net_raw,cap_net_admin+eip /usr/bin/dumpcap 单独授权。直接 sudo tshark 也行但不推荐在生产上长期这么干。
  • 抓包把磁盘写满: 这是 tshark 最危险的地方。高流量接口上不加 -b filesize 限制,几分钟就能写几十 G。永远给 tshark 加 -b 循环参数和 -c 包数上限。
  • 捕获过滤器写错语法直接报错退出: -f 用的是 BPF 语法(tcp port 443),-Y 用的是 Wireshark 语法(tcp.port == 443)。两者不是一回事,混用一定报错,这是新手最高频的困惑点。
  • -i any 丢失链路层信息: 需要看 MAC、VLAN 时务必指定真实网卡名,不要用 any。
  • 抓不到自己 SSH 之外的流量: 如果服务器在虚拟化环境里,网卡可能处于混杂模式受限状态,需要确认虚拟交换机的镜像端口配置,否则只能看到本机收发的包。
  • HTTPS 全量解密: tshark 本身不能解密别人的 HTTPS,除非你有服务器私钥(配 SSLKEYLOGFILE 或 RSA 私钥),否则只能看到元数据。不要被"tshark 能抓 HTTPS 内容"的标题党误导。

小结

tshark 的定位很清晰:它是把 Wireshark 的协议理解能力带进终端的工具,解决了"服务器上只能抓不能看"的痛点。记住四个高频组合就有 80% 的实战价值:-T fields -e 精确抽字段、-z conv,tcp 看会话耗时、-z expert 看 TCP 异常、-b filesize 防写满磁盘。再配合"抓包落盘 + 拉回本地分析"的姿势,你就能在服务器上把从前只能靠猜的网络问题,变成一条条有字段、有时间的证据链。

Last modification:October 10th, 2026 at 09:23 pm

Leave a Comment