☰
tcpdump抓包实战指南:从基础命令到网络故障排查全解析
2026/10/3 4:28:15 网站建设 项目流程

你有没有遇到过这种排查场景:应用日志里全是 connect timeout,监控面板上 CPU 和内存都正常,服务端口 telnet 也能通,但你就是说不清请求到底卡在哪一段。这种时候打开 Wireshark 太重,让开发配合抓包又说不清格式,最后往往是一条 tcpdump 命令把包一抓,所有真相立刻摆在面前。tcpdump 是最经典、最可靠的命令行抓包工具,从 Linux 服务器到嵌入式设备,只要有网卡的地方基本都能用。这篇文章不讲虚的,直接从日常排障的角度,把 tcpdump 最常用的用法拆给你——怎么选网卡、怎么写过滤器、怎么存文件、怎么读输出,以及那些文档里不会明说的坑。

1. 抓包的基本姿势:先把这几条命令刻在脑子里

很多人第一次用 tcpdump 就直接tcpdump -i eth0,然后终端瞬间被刷屏,按 Ctrl+C 之后脑子一片空白。问题不在于工具复杂,而是没有在一开始想清楚三件事:在哪个接口抓、抓多少包、要不要存文件。

1.1 从“先抓起来”开始:-i、-n、-c 三条命根子

最少数量的参数组合是这一条:

tcpdump -i eth0 -nn -c 10

逐个拆开说。

-i eth0指定网卡接口。服务器上有多块网卡时,不指定接口它会在系统默认接口上抓,抓到的东西往往不是你想要的。拿不准到底有几块网卡,先执行ip link看一眼,或者干脆用-i any抓所有接口的流量。-i any在 2.0 以上的内核版本里很实用,特别是你只知道主机 IP、不知道流量从哪块网卡进来的时候,用any最不容易漏。

-n表示不做主机名解析,也就是不把 IP 反查成域名。-nn更进一步,连端口也不再解析成服务名。为什么强烈建议加双 n?因为排障时 DNS 一旦出问题,tcpdump 可能卡在 PTR 查询上半天不输出,而且你以为看到的http、https其实掩盖了真实端口号,443和8443都显示成https,这信息就废了。

-c 10的含义是抓到 10 个包后自动退出。这个参数不是你偷懒才用的,而是防止误操作导致 tcpdump 在线上机器上无限抓下去。刚开始熟悉命令时,建议每次都带一个合理的小数值。

1.2 抓了就必须存文件:-w、-r 的正确打开方式

新手最常见的第二个问题,是盯着屏幕看刷屏。等反应过来想回看某个包,早就滚过去了。而且要复盘一个复杂的交互过程,屏幕模式根本没法做。

所以我的习惯是:第一次抓包就带上-w写文件。

tcpdump -i eth0 -nn -s 0 -w /tmp/cap.pcap

这里有两个点需要说明一下。

第一,-w写出来的是原始包文件(pcap 格式),不是文本。你不要试图cat /tmp/cap.pcap去读它,要用-r参数回放:

tcpdump -nn -r /tmp/cap.pcap

第二,-s 0是抓取整个数据包,也就是 snaplen 不限制。tcpdump 默认的 snaplen 在部分老版本里是 96 字节或 262144 字节,对于只看包头够用,但排查 HTTP、远程调用这类需要看内容的场景,默认长度可能把 payload 截断。-s 0的含义是“抓到完整包”,代价是占用内存和磁盘更多,但排障时信息完整远比省那几百 MB 重要。

1.3 想直接看内容?-A、-X、-e 怎么选

不是每次都适合写文件后慢慢看。有些场景在终端里直接看内容更快。

-A把包的内容以 ASCII 码打印,很像strings命令的效果。排查明文协议(HTTP、Telnet、自定义文本协议)时特别好用,一条请求的 URL、Header、响应码全出来了。HTTPS 这种加密流量用-A就什么都看不见,别在这上面浪费时间。

-X同时打印十六进制和 ASCII,适合分析二进制协议、报文格式异常、或者一些不明协议的数据结构。如果你对某个自定义协议只要确认某几个字节的字段含义,用-X直接看偏移。

-e会打印数据链路层信息,也就是 MAC 地址和 VLAN。查 ARP、查 DHCP、查广播环路、确认报文到底从哪个交换机端口进来时,没有-e基本无从下手。

这三种参数都可以跟其他选项组合,比如tcpdump -nn -A -e -c 100一次得到链路层信息和内容。

2. 过滤器才是灵魂:在“一堆噪音”里锁定那根针

不加过滤器的 tcpdump 等于把整条马路上的车全拍下来,事后想找某一辆,只能靠肉眼。真正高效的用法是让 tcpdump 只抓你关心的那几股流量。

2.1 最常用的三类基元:host、net、port

tcpdump 的过滤器语法属于伯克利包过滤(BPF),它最基础的元素只有几类:主机、网段、端口。写法和自然语言非常接近。

只看某一台主机的流量:

tcpdump -nn host 10.0.0.7

只看一个网段:

tcpdump -nn net 192.168.1.0/24

只看某个端口的流量:

tcpdump -nn port 8080

这些原语前面还可以加方向修饰符src和dst。比如:

tcpdump -nn src host 10.0.0.7

只看这个 IP 作为源地址发出的包;dst port 443只看发往 443 的包。方向组合非常灵活,src or dst是默认语义,但明确写出来更容易让阅读命令的人一眼看懂意图。

还有一个容易被忽视的原语portrange。当服务端口是一段连续范围时(比如游戏服务器 8000-8010),不用逐个端口 or 来 or 去:

tcpdump -nn tcp portrange 8000-8010

2.2 逻辑组合:and、or、not

到这一步,你可能已经发现,单条原语解决不了复杂问题。比如“我要看主机 A 和主机 B 之间的 HTTP 流量”,或者“排除掉 ICMP”。tcpdump 的过滤器支持逻辑组合,写法就跟英文句子一样。

tcpdump -nn 'host 10.0.0.7 and tcp port 80' tcpdump -nn 'tcp and (port 80 or port 8080)' tcpdump -nn 'host 10.0.0.7 and not icmp'

这里有一个实战中非常常见的坑:tcpdump 过滤器必须整体作为一个参数传入,而 Linux 的 shell 会把空格拆成多个参数。所以只要你写的表达式包含空格,就必须用单引号把整段过滤器包起来。

为什么推荐单引号而不是双引号?因为 BPF 表达式里常用!表示非,而在 bash 里!有历史展开的特殊含义,双引号里它可能被 shell 拦截,单引号则是原样传递。

2.3 顺着包追协议:过滤协议类型与 TCP 标志位

除了 host、port 这些“地址维度”,tcpdump 还支持按协议类型过滤。最常见的:

tcpdump -nn tcp tcpdump -nn udp tcpdump -nn icmp tcpdump -nn arp

直接写tcp就表示只保留 TCP 协议。过滤器还支持更精确的协议头字段匹配,不过平时用得比较多的是 TCP 标志位。比如你想只抓 TCP 握手阶段的 SYN 包,判断有没有人撞库扫描端口,可以用:

tcpdump -nn 'tcp[13] & 0x02 != 0'

解释一下这个写法的含义。TCP 头部的第 13 个字节是控制位(flags),0x02对应的二进制是00000010,即 SYN 位。&是按位与,只要 SYN 位为 1 的包都会留下。想抓同时设置了 SYN 和 ACK 的包(也就是握手的第二步),写成:

tcpdump -nn 'tcp[13] & 0x12 == 0x12'

这类写法需要一点 TCP 头部结构的底子,不要求人人都会,但至少知道这是可以做到的,遇到海量扫描流量时能救命。

2.4 过滤器别废话:按需组合的排障范式

组合过滤器的时候一定要克制。抓包追求的是“最少噪音”,但也不建议写一条 200 个字符的表达式然后忘了它说的是什么。我通常这样组合:

  • 排查应用问题:host 业务IP and tcp port 端口,先锁机器再锁端口。
  • 排查网络问题:host 对端IP不加端口,把两个方向所有报文都收进来,观察重传、乱序、丢包。
  • 排查 DNS:port 53不用写 udp,因为 TCP 53 很少遇到,但写上也无妨。

过滤器写的越精确,后续分析越省力。多个条件用and串起来,括号改变优先级,这是核心逻辑。

3. 实测最常用的四个场景:对着抄就行

讲完语法,我们来点实际能上手的。下面四个场景基本覆盖了日常排障的 70% 需求,每一条我都给了可复制的命令和判读方法。

3.1 三次握手排障:先分清“包没发出去”还是“包没回来”

某个 Web 服务一直连接超时,应用日志什么线索都没有。一台台机器 Telnet 过去都说通,但实际请求就是失败。这时直接在客户端抓握手包:

tcpdump -nn -i eth0 -c 20 host 10.0.0.5 and tcp port 80

正常的三次握手输出长这样:

09:22:01.123456 IP 192.168.1.10.54321 > 10.0.0.5.80: Flags [S], seq 1000, win 64240, length 0 09:22:01.789012 IP 10.0.0.5.80 > 192.168.1.10.54321: Flags [S.], seq 2000, ack 1001, win 29200, length 0 09:22:01.789102 IP 192.168.1.10.54321 > 10.0.0.5.80: Flags [.], ack 2001, win 64240, length 0

判读起来并不难:

  • 只有第一条[S]而且不断重发,后面啥也没有,说明本机发出的包没有到达对端,或者对端根本没有理你。最常见的原因是防火墙 drop 掉了 SYN,或者对端服务没在监听。
  • 看到第二条[S.]但发了很多次相同的[S],说明客户端发出的 SYN-ACK 之后没有得到确认,问题很可能在客户端的网络路径上。
  • 三次握手完整但应用一直超时,那问题不在链路层,往应用层继续查。

有一点要特别注意:在客户端抓包和在服务器端抓包结论可能完全相反。客户端上看不到 SYN-ACK,服务器上却能看到 SYN 进来,说明落包点在两者之间的网络设备上。所以“这种问题先两边同时抓一把包”是惯例,别嫌麻烦。

3.2 接口请求发不出去:-A 把 HTTP 原形打出来

有一次排查一个奇怪问题:调用第三方接口返回 302,但浏览器访问明明是 200。我懒得去翻各种日志,直接在应用服务器上抓了一把 HTTP 流量:

tcpdump -nn -A -s 0 -i any tcp port 80 and host 10.10.0.8

-A会直接打印 HTTP 明文内容,你能看到请求方法、URL、Host、User-Agent,也能看到响应行和响应头。那种说“我请求的是 A 接口,返回的却是错误信息”的问题,一把包就能看清楚是不是走了别的域名、带了奇怪的 Header,或者被网关改写。

需要注意:如果服务是 HTTPS,-A抓下来全是密文,跳过这个步骤,加密流量的排查思路完全不同,这里不展开。

3.3 DNS 解析慢:在 53 端口抓两端

用户反馈页面打开要十几秒,其他机器一切正常。经验告诉我,先怀疑 DNS。在本地抓 DNS 查询:

tcpdump -nn -i any port 53 -v

重点关注两点。第一是查询发出后多长时间能收到响应,时间戳之间的差就是解析延迟;第二是响应码是什么,No such name明确表示域名根本解析不出来,Server failure说明上游递归服务器有问题。

如果你看到客户端反复发给同一个 DNS 服务器的相同请求,每次间隔刚好是 5 秒、10 秒这种固定周期,那基本可以断定是 DNS 超时重试。这时候再去检查 DNS 服务器的上游连通性,方向就对了。

3.4 TCP 重传与乱序:网络抖动的证据链

网络“慢”是一个很模糊的词。到底是丢包重传、乱序,还是带宽瓶颈,别看监控面板,抓包看看最有说服力。

tcpdump -nn -i eth0 host 10.0.0.8

把包存成文件,然后用 Wireshark 打开,重点看两样东西:

  • 有没有大量的 TCP Retransmission:这代表对端没收到或者 ACK 丢了,链路丢包率大概率超标。
  • 有没有 Dup ACK / Out-of-order:这代表报文乱序到达,常见于多路径负载均衡或拥塞。

这里要先泼一盆冷水:tcpdump 本身的终端输出不好直接判断重传,因为重传的判定需要结合 seq 和 ack 的前后关系,人工盯屏幕效率很低。正确姿势是-w存 pcap,事后用 Wireshark 的 Expert Info 自动标出来。所以我在这类排查中从来不用屏幕打印模式,上面的命令只是抓包阶段,分析阶段还在后面。

4. 输出结果里的暗语:时间戳、TTL、Seq 这些数字到底在说什么

很多人会 tcpdump 基本命令,但输出的一长串东西看不懂,只能抓到包以后截图发到群里等别人帮忙看。要真正用好 tcpdump,得学会解析输出。

4.1 第一眼先看时间戳:-tttt 和 -ttttt

默认情况下 tcpdump 的时间戳格式是“时分秒.微秒”,不带日期。如果抓到凌晨的包,你根本不知道是哪天抓的。加-tttt会带上日期:

tcpdump -tttt -nn -r cap.pcap

输出类似:

2025-01-12 09:22:01.123456 IP ...

还有一个更实用的参数-ttttt,它显示的是“从抓包开始到现在经过了多少秒”。排查延迟漂移、确认两次请求的间隔,用相对时间好算多了。

tcpdump -ttttt -nn -r cap.pcap

4.2 拆一条 IP 包:ttl、id、flags 的含义

来看一行比较典型的输出:

IP 10.0.0.2.47192 > 10.0.0.5.80: ttl 64 id 54681 length 52

ttl 64是 IP 包的生存时间。Linux 默认发出包的 TTL 是 64,Windows 是 128,经过一个路由器减 1。如果你看到对端发来的包 TTL 是 58,说明中间经过了 6 跳。TTL 还有一个用处:当你同时看到同一主机的包 TTL 有时是 64 有时是 60,说明可能有两个源,比如负载均衡后端节点在响应时 TTL 不一样,或者经过了不同路径。

id 54681是 IP 分片标识。如果同一时刻有多个包 id 相同但总长度超了 MTU,说明发生了分片。length 52表示本次 IP 报文的长度。还有常见的DF标识,表示“不要分片”,加了之后大包只能丢弃,这就是某些场景下 ping 不通但端口能通的原因之一。

4.3 拆一条 TCP 包:seq、ack、win、flags 的配合

TCP 输出的可读性比 IP 层高:

IP 192.168.1.10.54321 > 10.0.0.5.80: Flags [P.], seq 1001:1049, ack 2001, win 64240, length 48

这里的Flags [P.]是组合标志。P表示 PSH(推送数据),.表示 ACK;[S]是 SYN,[S.]是 SYN+ACK,[F]是 FIN,[R]是 RST。记住一个规律:握手包是S,正常传输包绝大多数是[.],断开是[F.]。

seq 1001:1049是 TCP 报文段的序号范围,表示这个包携带了从绝对序号 1001 到 1048 的 48 字节数据。ack 2001是对端期望收到的下一个字节序号,同时也是确认号。用 Wireshark 时看相对序列号能看得更直白,但在 tcpdump 里看到的是绝对序号,所以不要指望“seq 都是 1”这种教科书式数据。

win 64240是接收窗口大小,表示本端还能接收多少字节。这是排障的关键指标:如果对端持续发来win 0的包,那就是接收端窗口关闭,说明对方应用层读数据太慢,数据积压在缓冲区。很多“客户端卡死”的问题,抓包时能看到一堆 Zero Window 包。

4.4 别被 UDP 迷惑:没有握手并不代表没有问题

UDP 输出简单得多:

IP 192.168.1.10.55443 > 10.0.0.9.53: 44846+ A? example.com. (31) IP 10.0.0.9.53 > 192.168.1.10.55443: 44846 1/0/0 A 93.184.216.34 (47)

第一行末尾的(31)是 UDP 包总长度,问号前面A?表示 DNS 查询类型 A 记录,44846+是 DNS 事务 ID。第二行1/0/0是国家段:答案数/权威记录数/附加记录数,后面的A 93.184.216.34就是解析结果。这种判断方式在手工验证 DNS 响应时非常顺手。

UDP 没有握手和确认机制,应用层自己处理可靠性,所以看到 UDP 包数量不少、丢失却没有任何迹象时,说明丢包问题在应用层以上,不要再挂网络设备的锅了。

5. 抓包踩坑实录:为什么我抓到了包,却还是一头雾水

tcpdump 用顺手了,接下来才是难点:它能抓包,但抓到的包不一定是你想的那样。这几个坑我不止一次见人踩过,包括我自己。

5.1 网卡 offload:让 CPU 省事却让抓包者很痛苦

如果你在 10G 网卡上抓包,经常会发现一个诡异现象:明明 MTU 是 1500,抓到的大包却有 9000 字节,或者所有 TCP 校验和显示为[bad](实际上是[offload])。这不是你的抓包姿势错了,而是网卡硬件卸载特性在“捣乱”。

现代网卡为了减轻 CPU 负担,会把数据包的分段、合并、校验和计算交给硬件来做。其中和抓包最相关的是 GRO(Generic Receive Offload)和 TSO(TCP Segmentation Offload)。GRO 会把多个小包合并成大包再交给协议栈,tcpdump 抓包时看到的就是合并后的大包;TSO 相反,应用层给一个超大包,tcpdump 在发送路径上抓到的还没被网卡切分,所以也超长。

解决方法是临时关掉相关卸载特性,抓完再恢复:

ethtool -K eth0 gro off gso off tso off

如果还不行,把 lro、rx、tx 也一起关掉:

ethtool -K eth0 lro off rx off tx off

先执行ethtool -k eth0看一下当前哪些是 on 的再动手,别把性能调没了。抓完包记得恢复原来的状态,毕竟这些卸载特性是为了性能存在的。

5.2 抓包文件越来越大的富贵病:滚动保存姿势

线上流量大的机器,一条-w /tmp/cap.pcap的命令挂几分钟就可能写出几个 G 的文件。磁盘写满的严重程度比连接问题高得多。

推荐用滚动保存模式。按文件大小滚动:

tcpdump -i eth0 -nn -w /tmp/cap.pcap -C 100

这里的-C 100表示每个文件写到 100MB 就自动切换成 cap.pcap1、cap.pcap2 继续写。注意-C的单位在不同系统中是兆字节,实测时留意一下。

按时间滚动则用-G:

tcpdump -i eth0 -nn -w /tmp/cap_%Y%m%d_%H%M%S.pcap -G 60

-G 60表示每 60 秒切换到新文件,文件名里的%Y%m%d_%H%M%S会格式化为当前时间。生产环境建议先-C控制大小,不要只靠时间间隔,因为流量忽大忽小时时间轮转要么拆得太碎要么一个巨大文件撑到最后。

5.3 普通用户抓不到包:权限问题的常规解法

tcpdump 抓包本质上需要使用 AF_PACKET 套接字读取网络数据,需要CAP_NET_RAW这类能力。普通用户直接执行会报:

tcpdump: You don't have permission to capture on that device

常规操作是加 sudo。但这里有个隐藏坑:sudo tcpdump -w /tmp/cap.pcap生成的文件 owner 是 root,普通用户后面想读会提示权限不足。两个解法:

  • 提前创建文件并给足权限:touch /tmp/cap.pcap && chmod 666 /tmp/cap.pcap && sudo tcpdump -w /tmp/cap.pcap。
  • 或者设置 tcpdump 二进制文件的能力位:
sudo setcap 'cap_net_raw,cap_net_admin+eip' $(which tcpdump)

设置之后普通用户直接运行 tcpdump 就可以抓包,不再需要 sudo。注意这是修改系统能力配置,在严格的生产环境要确认安全策略允许。

5.4 混杂模式不是必须的:-p 参数的大作用

默认情况下,tcpdump 会把网卡切换到混杂模式(promiscuous mode),这样才能收到目的地址不是本机的包。但如果你只想分析本机收发流量,混杂模式没有必要,而且它会让网卡收到更多无关广播和探测报文,造成噪音和性能损耗。

关闭混杂模式加-p:

tcpdump -p -i eth0 -nn host 10.0.0.7

抓本机业务流量时-p能明显减少无关包的数量,这个细节很多人不知道。

5.5 容器环境抓包:注意看不到的虚拟网卡

现在很多服务跑在容器里,直接在宿主机上用 tcpdump 抓 eth0,看到的流量可能都没有源端口映射,因为容器网络命名空间是隔离的。常见的正确姿势是docker exec进入容器再抓:

docker exec -it <container_id> tcpdump -nn -i eth0 -w /tmp/cap.pcap

容器镜像不一定装了 tcpdump,没装就先apt或yum装上。还有,容器的 eth0 是 veth 设备,抓包时可以看到对端是对应 veth 对,别被名字吓到。如果用的是 host 网络模式,那直接在宿主机抓即可。

5.6 抓包对性能的影响:别在大流量机器上裸跑

大流量环境下 tcpdump 本身是消耗 CPU 和磁盘 IO 的,尤其注意,tcpdump 进程在接收端如果跟不上包速率,内核缓冲区溢出会丢包。tcpdump 退出时打印的统计信息里就有:

1555 packets captured 1560 packets received by filter 0 packets dropped by kernel

packets dropped by kernel这一行如果长期不为 0,说明抓手速度跟不上流量速度。解法是加大内核抓包缓冲区,用-B指定缓冲区大小(单位 KB):

tcpdump -i eth0 -B 4096 -nn -w /tmp/cap.pcap

缓冲区不是越大越好,太大会占用很多内存,一般 4096 到 8192 就够用了。

6. 把 tcpdump 和 Wireshark 组合成自己的排障流水线

写到最后,我想认真聊一下工具搭配的问题。tcpdump 是现场第一工具,但不是唯一工具。真正高效的排障流程,往往是 tcpdump 负责采集,Wireshark 或 tshark 负责分析,各干各擅长的事。

6.1 为什么现场只抓包,不现场分析

线上环境没有图形界面,而且服务正在抖动时你不可能慢慢打开 Wireshark。正确做法是:第一时间用最短的命令抓到足够的包,存成 pcap 文件,然后带走或者 scp 到本地分析。现场的分析只需要回答两个问题:包里有没有问题特征?问题涉及哪个 IP、哪个端口?剩下的重传统计、时序分析、协议解码,全部丢给带 GUI 的工具。

6.2 Wireshark 里必看的三个视图

打开 pcap 文件后,大多数人只会盯着第一条红色标注的包看,这样效率不高。我建议按顺序看三个地方:

  • Expert Info(分析 -> 专家信息):Wireshark 会自动标出 TCP 重传、重复 ACK、乱序、窗口更新等异常事件。它按严重程度分级,错误级别先行。
  • TCP 流图(Statistics -> TCP Stream Graph -> Time-Sequence Graph 或 Throughput):能看到连续的序列号走势。折线出现明显的回退,就是重传;斜率陡峭但断断续续,考虑限速和拥塞。
  • Conversations(统计 -> 会话):直接列出所有会话的字节数、包数、持续时间,快速定位到底是谁在大量通信。

如果线下没有图形环境,用 tshark 命令行做统计也够用:

tshark -r /tmp/cap.pcap -q -z io,stat,10

输出的是 10 秒一个区间的流量统计,一眼就能看出流量高峰出现在哪段时间。

6.3 工具选择的边界:什么时候不推荐 tcpdump

tcpdump 适合的场合是:轻量、快速、无 GUI、嵌入式设备、只确认基础连通性。但遇到这些情况,用它效率反而低:

  • 需要深度解码复杂协议(如 SMB、TLS 握手细节),直接上 Wireshark。
  • 需要按时间窗口做协议统计、重传率计算,用 tshark 的-z统计功能。
  • 需要在大量 pcap 里搜索特定 payload 内容,可以 tcpdump-A加 grep 管道,但注意管道下游处理太慢会导致上游抓包缓冲区溢出,最稳妥的是先存文件再过滤。

还有个小技巧是,tcpdump 抓包时过滤器和后期过滤是可以分开的。现场抓包时过滤器尽量放宽,宁可多抓一点,也不要太严格的把关键包过滤掉。比如你只知道业务端口是 8080,但抓包后发现它还要和一个数据库交互,漏了 3306 就得重新抓。抓满再删,比没抓到再补,成本低得多。

最后再分享一个我个人的习惯:我把一条“万能排障命令”存在了笔记里,遇到问题先跑这一条:

sudo tcpdump -i any -nn -s 0 -w /tmp/$(date +%Y%m%d_%H%M%S).pcap

它抓所有接口、不解析主机名、抓完整包、按时间命名文件,只用管 Ctrl+C 结束抓包。等 pcap 文件拿到手,再根据时间、主机、端口慢慢用-r加过滤条件去剥。这个习惯帮我省了很多事——因为线上问题千奇百怪,你永远不知道下一次需要回放的是哪一段流量,而只要包在,问题就永远有得查。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询