刚接一个线上问题,服务端接口偶发超时,日志打了,代码翻了几遍也没看出毛病。同事随口一句"抓包看看呗",我熟练地在终端敲下一行命令就开始抓包,心里却突然想起几年前刚接触 tcpdump 时,对着 man 手册一脸懵的样子。那时候连 -i any 都不知道,抓了半天全是错误提示。
tcpdump 是 Linux 下最经典的命令行抓包工具,没有图形界面,但胜在轻量、灵活、覆盖面广。它既能抓普通网卡流量,也能抓 loopback 环回口,还能配合各种过滤表达式精准定位某一类报文。无论是排查网络不通、定位接口超时,还是分析 TCP 握手异常、验证协议实现是否符合预期,tcpdump 都是第一选择。这篇文章就把它从安装到实战、从参数到排障完整过一遍,适合刚上手的新人,也适合已经用过但想系统补漏的同学。
1. 为什么是 tcpdump:一次抓包救场的经历
1.1 从一次线上事故说起
那次接口超时的排查,最后靠 tcpdump 收尾。客户端调用服务端接口,偶发性延迟 3 到 5 秒,但又不是每次都发生。代码里查不到可疑日志,数据库慢查询也没有,监控面板上 CPU、内存、带宽都正常。当时我们的第一反应是代码问题,结果越查越偏。
后来在服务端网卡上抓包,命令很简单:
bash复制tcpdump -i eth0 -nn 'tcp port 8080' -w /tmp/issue.pcap -c 5000
抓了五分钟,拿 pcap 文件用 Wireshark 打开,看 TCP 流的时序图。问题一下就清楚了:客户端发送请求后,服务端一直没有回复 ACK,隔了 3 秒才出现一个 TCP Dup ACK,随后重传。继续往上追,发现服务端进程确实收到了数据,但业务线程池在等一个分布式锁,锁等待超时才返回。这就把问题从"网络层"拉回了"应用层",但如果没有抓包定位到这个现象,我们可能还在代码里瞎猜。
这就是 tcpdump 最核心的价值:在网络层和应用层之间架起一座看得见的桥。代码把数据交给内核协议栈之后,到底发生了什么,日志往往看不到,tcpdump 能看到。
1.2 它和 Wireshark、Charles 这类工具怎么分工
很多新人会问:有 Wireshark 图形界面,为什么还要用 tcpdump?
实际场景里,生产服务器大多没有图形界面,只有命令行。Wireshark 再强大,你也得先把流量抓下来再分析,而抓流量这个动作本身就是 tcpdump 的活。所以常见的组合是:服务器上用 tcpdump 抓包保存成 pcap 文件,传到本地用 Wireshark 做可视化分析。tcpdump 负责采集,Wireshark 负责分析,两者是上下游关系。
至于 Charles、Fiddler 这类工具,它们主要针对 HTTP/HTTPS 层面的抓包,能看请求头、响应体、Cookie,还能做断点修改。但它们的实现原理是代理,只处理应用层协议,对 TCP 重传、IP 分片、ARP 请求这些网络层问题完全无能为力。tcpdump 是直接挂在数据链路层上的,能看到 MAC 帧、IP 报文、TCP/UDP 报文段,属于更底层的抓包方式。
用一句话总结:Charles/Fiddler 是"应用层调试器",Wireshark 是"协议分析器",tcpdump 是"网络报文采集器"。三者的定位不同,适用的场景也不同。如果你连不上数据库、ping 不通网关、TCP 握手异常,第一反应应该是 tcpdump,而不是 Charles。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备:从安装到第一条抓包命令
2.1 在线安装与版本确认
大部分 Linux 发行版都自带 tcpdump,或者可以通过官方源直接安装。Debian/Ubuntu 系用 apt,CentOS/RHEL 系用 yum 或 dnf。
bash复制# Debian / Ubuntu
sudo apt-get update
sudo apt-get install -y tcpdump
# CentOS / RHEL 7/8
sudo yum install -y tcpdump
# CentOS / RHEL 9
sudo dnf install -y tcpdump
安装完确认版本,编译选项决定了功能差异,建议确认一下:
bash复制tcpdump --version
输出大致长这样:
text复制tcpdump version 4.9.3
libpcap version 1.9.1
OpenSSL 1.1.1k FIPS 26 Mar 2021
这里有两个信息要关注。第一是 libpcap 的版本,tcpdump 自身的版本号只是前端,真正的抓包能力由 libpcap 库提供。第二是 OpenSSL 字段,如果有这个字段说明支持加密流量解码的部分功能,但实际很有限,主要影响 -w 写文件时是否支持加密的 pcap 文件(极少用)。
注意:tcpdump 和 libpcap 是配套关系。如果后续发现抓包行为异常,先确认这两个版本是否兼容,尤其是离线安装时容易踩版本不匹配的坑。
2.2 离线安装的两种做法:rpm 包与源码编译
内网服务器没法访问外网源是常态,离线安装 tcpdump 有两个常用思路:
方式一:找同版本系统的 rpm/deb 包,离线安装
bash复制# 在有网的机器上下载,再拷贝到目标机器
yumdownloader --resolve tcpdump
# 或者
apt-get download tcpdump libpcap0.8
# 目标机器上安装
sudo rpm -ivh tcpdump-*.rpm libpcap-*.rpm
sudo dpkg -i tcpdump*.deb libpcap*.deb
这里的关键是 libpcap 必须一起装上,很多人只装 tcpdump 导致报 libpcap.so.1: cannot open shared object file,这就是典型的依赖缺失。
方式二:源码编译静态链接
如果目标机器的系统版本比较老,软件源里找不到合适的二进制包,源码编译更稳妥:
bash复制# 下载 libpcap 和 tcpdump 源码,版本要匹配
wget https://www.tcpdump.org/release/libpcap-1.10.4.tar.gz
wget https://www.tcpdump.org/release/tcpdump-4.99.4.tar.gz
# 先编译安装 libpcap
tar xzf libpcap-1.10.4.tar.gz
cd libpcap-1.10.4
./configure --prefix=/usr/local
make -j$(nproc)
sudo make install
# 再编译 tcpdump
export LDFLAGS="-L/usr/local/lib"
export CPPFLAGS="-I/usr/local/include"
tar xzf tcpdump-4.99.4.tar.gz
cd tcpdump-4.99.4
./configure --prefix=/usr/local
make -j$(nproc)
sudo make install
编译静态版本可以直接把 libpcap 编进去,这样拷贝到任意 Linux 机器都能跑:
bash复制cd libpcap-1.10.4
./configure --disable-shared --enable-static
make -j$(nproc)
cd ../tcpdump-4.99.4
LDFLAGS="-static" ./configure --prefix=/usr/local
make -j$(nproc)
静态编译产物体积会大不少,但对内网排查来说省了很多麻烦,一个二进制扔过去就能用。
2.3 权限与基本验证
tcpdump 抓包需要访问原始套接字,普通用户无法直接执行,一般用 root 或 sudo。生产环境不想给 root 权限时,可以用 setcap 给 tcpdump 二进制单独授权:
bash复制sudo setcap cap_net_raw,cap_net_admin=eip /usr/sbin/tcpdump
设置好后,普通用户就能跑 tcpdump,但要注意如果系统里 libpcap 使用了一些需要 root 的 BPF 特性,仍然可能报权限错误,届时需要回退到 sudo 方式。
验证能不能正常抓包,最简单的办法是抓环回口:
bash复制sudo tcpdump -i lo -c 10
没有任何流量时,命令会一直等。另开一个终端执行 ping 127.0.0.1,就能看到 ICMP 报文:
text复制09:42:15.123456 IP 127.0.0.1 > 127.0.0.1: ICMP echo request, id 1, seq 1, length 64
09:42:15.123478 IP 127.0.0.1 > 127.0.0.1: ICMP echo reply, id 1, seq 1, length 64
能看到类似输出,说明 tcpdump 已经正常工作。此时再按 Ctrl+C 结束。
3. 常用参数逐个拆解:从入门到会用
3.1 接口选择:-i 和自动选择机制
tcpdump 最基础的参数是 -i,指定从哪个网络接口抓包。不指定时,tcpdump 会从接口列表里选择一个编号最小的非 loopback 接口,这往往是 eth0,但在多网卡机器上可能选错。
bash复制# 查看可用接口
tcpdump --list-interfaces
# 或者
tcpdump -D
输出类似:
text复制1.eth0 [Up, Running]
2.eth1 [Up, Running]
3.lo [Up, Running, Loopback]
4.any [Pseudo-device that captures on all interfaces]
强烈建议养成本来就用 -i 指定接口的习惯,尤其在有多个网卡的机器上。一个容易踩的坑是把接口名写错,比如 eth0 实际叫 ens33。接口名不对时 tcpdump 会报:
text复制tcpdump: eth0: No such device exists
(SIOCGIFHWADDR: No such device)
另外 -i any 是个好东西,它同时抓所有接口的报文,在不确定流量走哪个网卡时非常有用。但要注意 -i any 抓到的报文不带数据链路层头部,所以某些依赖 -e 参数查看 MAC 地址的场景不适用。
3.2 输出控制:-nn、-v、-e、-X、-A
新手最常见的问题就是 tcpdump 默认把 IP 反解析成域名,把端口显示成服务名,看起来费劲还慢。
bash复制# 默认反解析
tcpdump -i eth0 port 80
# 输出:IP host-123.example.com.80 > 192.168.1.1.54320
# 禁止反解析
sudo tcpdump -i eth0 -nn port 80
# 输出:IP 10.0.0.5.80 > 192.168.1.1.54320
-nn 两个 n,第一个禁止域名解析,第二个禁止端口服务名解析,抓包排查时基本是必带参数。加了之后输出更简洁,同时避免了 DNS 查询带来的额外延迟。
-v 系列控制详细程度,从 -v 到 -vv 到 -vvv 逐步增加信息量。排查 TCP 问题时建议用 -vvv,它会把 TCP 选项字段(如 MSS、窗口缩放因子、时间戳)都显示出来。但信息太多也不好阅读,通常是普通抓包用 -v,需要深挖时再上 -vvv。
-e 显示数据链路层头部,也就是 MAC 地址:
bash复制sudo tcpdump -i eth0 -nn -e
这在排查二层问题时非常关键,比如确认报文是不是经过了某个网关、MAC 地址是否匹配预期。-X 和 -A 用于查看报文内容,-A 只显示 ASCII 内容,-X 同时显示十六进制和 ASCII:
bash复制sudo tcpdump -i eth0 -nn -A port 8080
sudo tcpdump -i eth0 -nn -X port 8080
-A 适合看 HTTP 请求内容,-X 适合分析二进制协议或加密前报文。但要注意,tcpdump 设计上不会无限完整打印每一个包的全部内容,默认每行有长度限制,大报文会截断显示,此时需要配合 -s 参数调整快照长度。
3.3 保存与读取:-w、-r、-C、-G、-W、-Z
生产环境排查时,终端直接打印报文意义不大,正确的做法是保存成 pcap 文件,回头再用 Wireshark 分析。
bash复制# 保存抓包文件
sudo tcpdump -i eth0 -nn -s 0 -w /tmp/capture.pcap 'tcp port 8080'
-s 0 表示抓取完整报文,不截断。旧版本 tcpdump 默认只抓 262144 字节之前的头部,很多协议信息会丢失,-s 0 直接关掉限制。
-r 参数读取 pcap 文件,用法和抓包一样,所有过滤表达式照样生效:
bash复制sudo tcpdump -r /tmp/capture.pcap -nn 'tcp port 8080'
按体积自动切割和循环覆盖是生产环境抓长时段的利器:
bash复制# 每个文件 100MB,最多保留 10 个文件,循环覆盖
sudo tcpdump -i eth0 -nn -s 0 -C 100 -W 10 -w /tmp/trace.pcap
参数逻辑:
-C 100:单个文件到达 100MB 时切换新文件,文件名自动变成 trace.pcap1、trace.pcap2-W 10:最多生成 10 个文件,写满后覆盖最早的-G 300 -w /tmp/trace_%Y%m%d%H%M%S.pcap:按指定秒数轮转,文件名为时间模板,适合按时间段组织
降权运行同样重要。tcpdump 以 root 启动后,-Z 参数可以让它把权限降为一个非 root 用户,降低安全风险:
bash复制sudo tcpdump -i eth0 -nn -Z tcpdump -w /tmp/cap.pcap
注意 -Z 指定的用户需要对输出目录有写权限,否则抓包开始后写文件会失败。
3.4 时间戳与缓冲相关的细节
排查时经常需要确认包的具体到达时间,tcpdump 默认显示的是系统时间,精确到微秒。-tttt 参数可以显示带日期的完整时间戳:
bash复制sudo tcpdump -i eth0 -nn -tttt 'tcp port 8080'
另一个和排查关系很大的是 -l 参数。默认情况下 tcpdump 输出走缓冲区,管道处理会有延迟。加了 -l 使用行缓冲模式,每抓到一个包立刻输出,配合 grep 或 tee 时避免抓完才开始处理:
bash复制# 实时过滤包含特定关键词的 HTTP 请求
sudo tcpdump -i eth0 -nn -A -l 'tcp port 80' | grep -i 'Authorization'
-U 参数对写 pcap 文件同样有用,它让 tcpdump 每个包写完后立即刷到磁盘,而不是积攒一批再写。抓包途中机器断电或进程崩溃时,-U 能减少数据丢失。但代价是写盘频率变高,抓大流量时可能影响性能。
4. 过滤表达式:只抓你关心的包
tcpdump 最强大的地方,也最容易被初学者忽略的地方,是它那套基于 BPF(Berkeley Packet Filter)的过滤语法。不写过滤表达式时,所有流量都打到屏幕上,根本看不过来。写在 -w 和 -r 之后的过滤条件就是 BPF 表达式。
4.1 基础语法:host、port、net、proto
BPF 的基本过滤元素是"类型",常见的有 host、port、portrange、net、proto。写法比较简单直接:
bash复制# 抓指定 IP 的进出流量
sudo tcpdump -i eth0 -nn 'host 192.168.1.100'
# 抓指定端口
sudo tcpdump -i eth0 -nn 'port 443'
# 抓端口范围
sudo tcpdump -i eth0 -nn 'portrange 8000-9000'
# 抓网段
sudo tcpdump -i eth0 -nn 'net 10.0.0.0/24'
# 抓指定协议
sudo tcpdump -i eth0 -nn 'icmp'
sudo tcpdump -i eth0 -nn 'tcp'
sudo tcpdump -i eth0 -nn 'udp'
这里有个细节:tcp 和 port 80 的区别。tcp 指的是 IP 协议号为 6 的所有 TCP 报文,不管源端口还是目的端口;port 80 则修饰源端口或目的端口之一等于 80 的所有报文,不管是 TCP 还是 UDP。实际使用时经常组合,比如 tcp port 443 表示 TCP 协议且端口为 443,这也是最常用的写法。
4.2 逻辑组合:and、or、not 与括号
单个条件不够用,需要组合。BPF 支持 and、or、not 三种逻辑运算符,简写为 &&、||、!。组合的优先级从上到下是:not > and > or。涉及复杂组合时建议用括号明确优先级,Shall 里括号要转义或者用引号包住整个表达式。
bash复制# 抓发的 IP 访问 8080 和 443 端口的流量
sudo tcpdump -i eth0 -nn 'host 192.168.1.100 and (port 8080 or port 443)'
# 抓不是来自 10.0.0.1 的 TCP 流量
sudo tcpdump -i eth0 -nn 'tcp and not src host 10.0.0.1'
注意在双引号里的括号不需要转义,在单引号里也不需要。但如果在 bash 里不带引号直接执行,括号会被 shell 解析导致语法错误。规范起见,过滤表达式一律用引号包起来。
4.3 高级过滤:src、dst、长度、TCP 标记、VLAN
除了最基础的 host 和 port,BPF 还可以指定方向。src 和 dst 分别表示源和目的:
bash复制# 只看从 192.168.1.100 发出的包
sudo tcpdump -i eth0 -nn 'src host 192.168.1.100'
# 只看发往 192.168.1.100 的包
sudo tcpdump -i eth0 -nn 'dst host 192.168.1.100'
按报文长度过滤适合排查 PMTU 黑洞或异常大包小包:
bash复制# 抓长度大于 1000 字节的 IP 报文
sudo tcpdump -i eth0 -nn 'len > 1000'
字段级过滤是最强大的能力,BPF 支持直接访问报文头的字段。比如抓 TCP SYN 包做握手分析,或者抓带 RST 标志的重置报文:
bash复制# 只抓 TCP SYN 包
sudo tcpdump -i eth0 -nn 'tcp[13] & 2 != 0'
# 只抓 TCP RST 包
sudo tcpdump -i eth0 -nn 'tcp[13] & 4 != 0'
# 只抓 TCP ACK 包
sudo tcpdump -i eth0 -nn 'tcp[13] & 16 != 0'
TCP 头第 13 个字节是控制标志位,bit1 是 SYN,bit2 是 RST,bit3 是 PSH,bit4 是 ACK。这些写法的价值在于排查握手失败和连接被重置的问题,比盲抓所有 TCP 报文高效很多。
VLAN 场景下,过滤条件要加上 vlan 关键字,否则 host/port 过滤可能失效:
bash复制# 抓 VLAN 100 内的 HTTP 报文
sudo tcpdump -i eth0 -nn 'vlan 100 and tcp port 80'
关于 VLAN 有一个经典困惑:如果不加 vlan 关键字,port 80 在带 VLAN tag 的报文上经常匹配不到。原因是 VLAN tag 改变了报文头的偏移量,tcpdump 的端口检查找不到正确位置。抓不到包时,先检查是否存在 VLAN。
5. 实战场景:从抓包到分析
5.1 抓 HTTP 请求:看完整交互流程
排查 Web 接口超时、HTTP 状态码异常、响应内容不符合预期时,直接抓 80 或 8080 端口:
bash复制sudo tcpdump -i eth0 -nn -A -s 0 'tcp port 8080' -w /tmp/http.pcap
-A 参数让 tcpdump 直接打印 ASCII 内容,可以在终端看到 HTTP 的 Header 和 Body。但实际情况是 HTTP 报文可能被 TCP 分段,一个完整的 HTTP 请求会被拆成多个 TCP segment,终端里看会很乱。更靠谱的做法是保存 pcap 后用 Wireshark 的 Follow HTTP Stream 功能,它会把分散在多个 TCP 段里的 HTTP 内容重组起来,一目了然。
如果想在终端快速确认某个接口有没有被访问,可以配合 grep 做实时过滤:
bash复制sudo tcpdump -i eth0 -nn -A -l 'tcp port 8080' | grep -E 'GET|POST|HTTP/1'
这种用法在接口调试时非常方便,请求一进去终端立刻有输出。
5.2 抓 TCP 三次握手:判断连接失败在哪一环
客户端访问服务端建连失败,最常见的表现是卡住很久然后超时。此时抓 TCP 握手包是最直接的手段:
bash复制sudo tcpdump -i eth0 -nn 'tcp port 8080 and (tcp-syn or tcp-ack)' -c 20
正常建立连接的过程:
text复制客户端 → 服务端 SYN
服务端 → 客户端 SYN+ACK
客户端 → 服务端 ACK
如果只看到 SYN 没有 SYN+ACK,说明服务端没有响应,可能进程没监听、防火墙丢弃;如果 SYN+ACK 发出去了但客户端没回 ACK,问题可能在客户端或中间链路。用 tcpdump 分别抓两端,对比哪一侧没发出对应报文,就能快速定位是哪一段链路出了问题。
四次挥手同理,抓 FIN 和 ACK 的过程能判断是主动关闭还是被动关闭、状态卡在哪个阶段。
5.3 大流量场景:高性能抓包策略
流量稍大的环境直接 tcpdump 抓全量会丢包。丢包现象是抓包文件时间不连续、Wireshark 里看到 TCP 序列号跳跃,或者 tcpdump 退出时提示 dropped kernel。
几个实用策略:
- 先想办法缩小范围,过滤表达式越精确,性能压力越小。BPF 过滤在内核态完成,效率很高,但协议字段级过滤会稍慢一点。
- 用环形缓冲,只保留最近 N 分钟的数据。命令是
-C和-W组合已在前文讲过,这里不重复。 - 增大内核抓包缓冲区,
-B参数单位是 KB。默认通常是 2MB,大流量时可以调到 64MB:
bash复制sudo tcpdump -i eth0 -nn -B 4096 -s 0 -w /tmp/big.pcap
4096 表示 4MB,量级从几千到几万都试过。但注意 buffer 不是越大越好,过大会导致 tcpdump 来不及把数据写盘,用户态处理延迟反而丢包。
- 考虑用
gulp这类工具结合 tcpdump 和 PF_RING 或 AF_PACKET v3 抓包,但这属于进阶玩法,普通场景不需要。
5.4 抓 DHCP、ARP、ICMP 等基础协议
排查网络不通时,先抓 ICMP 看 ping 通不通:
bash复制sudo tcpdump -i eth0 -nn 'icmp'
排查 IP 地址冲突或网关问题时抓 ARP:
bash复制sudo tcpdump -i eth0 -nn 'arp'
排查 DHCP 获取不到 IP 的问题,抓 UDP 67/68 端口:
bash复制sudo tcpdump -i eth0 -nn 'udp port 67 or udp port 68'
这类协议报文少、频率低,抓起来没有性能压力。但它们的格式用终端看不太直观,尤其是 DHCP 的 Option 字段,最好还是保存 pcap 后用 Wireshark 查看。
5.5 与 Wireshark 联动:保存、传输、分析
日常流程基本固定:
bash复制# 第一步:服务器上抓包
sudo tcpdump -i eth0 -nn -s 0 -w /tmp/debug.pcap 'host 192.168.1.100'
抓一段时间后按 Ctrl+C 停止,然后把文件拷到本地:
bash复制scp user@server:/tmp/debug.pcap .
用 Wireshark 打开后,优先看这些内容:
- Statistics -> Conversations,查看通信双方的数据量分布
- Statistics -> Flow Graph,查看 TCP 连接时序
- Analyze -> Expert Info,Wireshark 会自动标记重传、乱序、重复 ACK 等异常
Wireshark 的图形化分析能力比 tcpdump 终端输出强太多,但它的基础数据来源于 tcpdump 保存的 pcap。两者搭配是生产环境问题排查的标准工作流。
6. 常见问题与排查技巧实录
6.1 抓包文件为空或抓不到任何包
现象:命令执行了,但 Ctrl+C 后文件大小为 0,或者终端一条输出都没有。
排查思路:
- 确认接口名是否写对,
tcpdump -D看一下。 - 确认流量路径是否正确。如果目标 IP 走的是其他网卡,你去 eth0 抓当然没有。此时用
-i any扫一遍。 - 确认 BPF 过滤条件是否写反。比如抓
src host 10.0.0.1但实际流量是 10.0.0.1 收的,方向反了自然为空。 - 确认网卡是否开启混杂模式。tcpdump 默认会自动开启混杂模式,但如果网卡驱动或者虚拟化环境不支持,可能只抓到本机收发流量。加
-p可以关闭混杂模式,但在虚拟化环境里有时反而能抓到更多流量,具体情况要看平台实现。
实际经验:有一次在虚拟机里抓包,明明有流量但 tcpdump 只抓到少量广播包。后来发现虚拟网卡没开混杂模式,在虚拟化平台的管理界面把"混杂模式"打开后才看到完整流量。
6.2 内核丢包:tcpdump 退出时提示 dropped kernel
现象:tcpdump 按 Ctrl+C 退出时,会有几行统计信息:
text复制5 packets captured
12 packets received by filter
6 packets dropped by kernel
这里的 packets dropped by kernel 表示内核抓包缓冲区满了,有报文被丢弃。抓大流量时几乎都会遇到,关键看丢弃比例。少量丢弃影响不大,如果大量丢失会导致分析结果失真。
处理办法:
- 先用
-B把内核缓冲区调大,比如-B 4096甚至-B 16384。 - 压缩过滤范围,只抓必要协议和端口。
- 抓包文件不要放在性能差的存储上,比如不要写到 NFS 挂载目录。
- 如果流量实在太大,考虑旁路抓包或者采样抓包,而不是全量抓。
6.3 过滤表达式语法写错
tcpdump 报错一般比较直接:
text复制tcpdump: syntax error
常见错误是括号没有闭合、引号没加导致 shell 解析歧义、关键字拼写错误。我的建议是写复杂表达式时先在简单场景测试,逐步加条件。比如先 tcp port 8080,确认有数据再往上加 host、src、dst。
关于 and 和关键字顺序的问题,记住 BPF 表达式其实是语法树,host、port、net 这些关键字后面必须跟具体值,单独一个 host 是没有意义的。另外 tcp port 80 和 tcp and port 80 看起来等效,但前者是简写,后者表示"TCP 且 端口 80",更严谨。
6.4 时间戳相关问题
有同学反馈:抓包文件里的时间比实际时间差了 8 个小时。这是因为 tcpdump 显示时间默认使用本地时区,但 pcap 文件里保存的时间其实是 UTC 时间戳。Wireshark 打开时会根据本机时区换算显示,如果抓包机器和查看机器时区不同,就会看到时间差。
处理办法:抓包时用 -tttt 显示带时区的完整时间,或者在 Wireshark 里设置显示时区。排查分布式系统问题、跨地域调用问题时,务必确认两端时间同步(NTP),否则时间戳对比没有意义。
6.5 安全验证与隐私注意
tcpdump 抓包会看到明文数据,包括 HTTP 请求里的 Cookie、Token、密码等敏感信息。内网调试还好,如果在生产环境抓包,务必注意:
- 抓包文件不要随意留存,分析完及时删除
- 涉及敏感数据的报文,用
-w保存时可以考虑只保留包头,比如-s 96只抓前 96 字节 - 不要在非必要场景下长期开启抓包进程
这个建议不是技术问题,而是操作习惯。我见过有人把带用户手机号的抓包文件传到网盘上,属于比较严重的数据泄露事件。
6.6 tcpdump 不存在或权限不足
有时在最小化系统里连 tcpdump 都没有,报 command not found。如果没 root 权限装不了,可以检查是否有 Wireshark 自带的 dumpcap:
bash复制which dumpcap
dumpcap 是 Wireshark 包里的命令行抓包工具,功能类似 tcpdump,还能直接输出 pcapng 格式文件,在某些受限环境下可以作为替代。
权限不足时报的错一般是:
text复制tcpdump: inet_pton: Permission denied
这种情况通常是用户没有访问原始套接字的权限,处理办法是 setcap 或者改用 root。
7. 几个实际排查案例复盘
7.1 案例一:TCP 重传导致接口缓慢
现象:生产接口偶发耗时 3 秒,日志里没有任何异常。
抓包结果:Wireshark Expert Info 显示大量 TCP Retransmission 和 Dup ACK,重传间隔在 1 秒左右。
进一步分析:重传的报文是同一个 TCP sequence,说明服务端确实没有收到 ACK。继续抓服务端和客户端两端对比,发现是中间防火墙老化 TCP 会话导致的丢包。
处理:调整防火墙会话超时时间,问题解决。
这个案例的启发:偶发性延迟不要只查代码,先看 TCP 层有没有异常。网络问题不一定是"完全不通",更多时候是"部分丢包"和"重传延迟"。
7.2 案例二:服务端能 ping 通但端口连接失败
现象:客户端能 ping 通服务器,但 TCP 8080 端口一直连接超时。
抓包结果:客户端只发出了 SYN,服务端没有回 SYN+ACK。
排查:先确认 8080 端口是否被监听,ss -lntp | grep 8080 没有输出。再确认防火墙,发现 iptables 有 DROP 规则,把 8080 端口的入站流量全部丢弃了。
处理:调整防火墙规则,连接恢复。
这个案例的启发:TCP 握手抓包能直接区分"服务端没收到"和"服务端收到了没回"。前者多半是网络路径或防火墙,后者是进程或系统问题。
7.3 案例三:离线环境交叉编译部署
现象:一台内网 ARM 架构服务器,没有软件源,需要部署抓包工具。
处理:在有网的 x86 机器上用交叉编译工具链编译静态版 tcpdump,二进制拷贝过去直接运行,加上 -Z 降权运行,放在 /usr/local/bin/tcpdump,后续配合 -w 保存抓包结果。
注意事项:交叉编译时前置的 libpcap 也必须用同一个交叉工具链编译静态库,否则链接阶段会报 undefined reference。
8. 经验小结:tcpdump 的使用心得
写了这么多,最后聊几句实际操作中的体会。
第一,tcpdump 是一个排查工具,不是调试工具。它的职责是告诉你"报文到底怎么了",而不是"代码哪里错了"。拿到抓包结果后要把现象翻译成网络层结论,再回到应用层去定位根因。
第二,抓包前一定要想清楚三个问题:抓哪个接口、过滤什么流量、保存还是直接看。这三个问题想不清楚,抓出来的东西大概率没法用。我见过有人抓了半小时全量流量,最后文件十几个 GB,Wireshark 打开卡半天——这是最常见的低效操作。
第三,tcpdump 的输出格式虽然古老,但它提供的信息密度比很多图形工具还高。学会用 -nn -v 这种组合,很多现场问题直接在终端就能判断,不一定非要传到本地用 Wireshark。
第四,不要忘了看退出时的统计信息。X packets captured、Y packets dropped by kernel 这些数据本身就是性能问题的诊断信息,很多人 Ctrl+C 之后就直接关终端,白白丢掉重要线索。
第五,如果只是排查特定应用问题,例如 HTTP 请求、App 接口、小程序,单独用 tcpdump 确实有点重,配 Charles 或 Fiddler 更高效。但网络层面的问题无论怎样绕不开 tcpdump。两者不是互斥关系,而是互补关系。
最后再分享一个小技巧:tcpdump 支持直接输入过滤表达式而不用加引号,但建议每次都加双引号或单引号。我自己踩过的坑是过滤表达式里带了括号和感叹号,没加引号直接被 shell 解析成历史命令扩展,差点误删文件。老实加引号,能避免大量莫名其妙的意外。
