凌晨两点半被电话叫醒,大概率不是什么好事。那天某业务系统的数据库连接大面积超时,应用日志干净得像什么都没发生过,ping 网关也通,但所有接口都在报 connection timed out。面对这种“网络不通”但又不是完全不通的诡异现场,我第一个打开的抓包工具就是 tcpdump。这篇内容想分享的,就是我用 tcpdump 定位“网络不通”和“延迟太高”这类问题的一套方法——先设计抓包方案,再按包特征对号入座,最后用两侧时间戳下结论。适合运维、后端开发、网络工程师,以及所有被网络故障折磨过的人。
tcpdump 这东西,会用的觉得它是神器,不会用的只会觉得它是天书。其实它的核心逻辑非常简单:把网卡上流过的报文抓出来,按时间顺序摆在你面前。网络问题最怕的是“看不到”,而 tcpdump 恰好就是那双眼睛。接下来我用两个实战案例,完整走一遍排查链路。
1. 抓包前必须想清楚的三个决策:抓哪里、抓什么、抓多久
很多人一上来就tcpdump -i eth0裸抓,抓到几 GB 文件然后傻眼。我不止一次看到同事对着几百 MB 的抓包文件无从下手。其实抓包这事,准备工作比抓包本身更重要。动手之前,先把三个问题想明白。
1.1 抓包点选在哪,决定了你能回答什么问题
抓包点的选择直接决定了你能证明什么、不能证明什么。举个例子:客户端连数据库超时,你只在客户端抓包,看到 SYN 发出去了,一直没人回应,你只能证明“客户端这边确实发了,也确实没收到确认”,但你说不清问题出在哪一段。
正确的做法是在两端同时抓包。这就像查一段路堵车,你不能只看一个路口的监控,得两头都看。客户端抓到“发了但没确认”,服务端如果完全没收到,那问题在中间链路,比如防火墙丢包、路由黑洞;服务端如果收到了也回了 SYN-ACK,但客户端没收到,那就是回程链路或服务端的问题。
实际操作中,如果没有条件在两端都抓,我的建议是先从客户端抓,同时用ss -nt看连接状态。SYN_SENT说明包发出去了还没确认,ESTABLISHED说明握手已经完成。先把范围缩小到“包根本到不了对端”还是“包到了但应用没响应”,再决定要不要去服务器那边抓一把。抓中间设备的包往往需要网络团队配合,所以优先把两端结论坐实,再去要中间设备的数据,效率高很多。
1.2 过滤表达式写不好,抓包就是灾难
过滤表达式是 tcpdump 最核心的部分,写好了事半功倍,写不好灾难现场。我常用的命令长这样:
tcpdump -i eth0 -nn -s 96 -tttt 'host 10.0.0.5 and tcp port 3306' -w /tmp/mysql_3306.cap拆开解释一下,这些都是踩坑换来的经验:
-i eth0:指定网卡。不确定是哪块网卡就先ip addr看一眼。内网流量一般走 eth0,管理口可能走别的网卡。-nn:不做域名反解、不显示服务名。这个参数很多人忽略,但实际排查时必须加。如果不加,tcpdump 会尝试反解 IP 对应的主机名,抓包过程中可能卡在 DNS 查询上,反而干扰了问题现场。-s 96:每个包只抓前 96 字节。TCP/IP 头部加起来大约是 40 字节(无选项时),96 字节足够覆盖 TCP 头加常用选项。payload 内容一般不是排查重点,你要看的是包的时序和标志位。-tttt:打印精确到微秒级的时间戳。排查延迟问题不看时间戳等于白抓,这一点后面会重点讲。- 过滤表达式:
host 10.0.0.5 and tcp port 3306只抓与指定 IP 的 3306 端口相关的 TCP 流量。
1.3 抓包文件落地姿势:磁盘、权限与冲刷
抓包文件要落地,就得考虑磁盘空间。很多人忽略这个问题,开着抓包脚本就去睡觉,第二天一看磁盘满了。一个经验值:-s 96时,每条记录大约 100 字节左右,100MB 的文件大概能装几十万到上百万个包。但这是理想情况,如果流量里有大量广播包或者抓包机本身处理不过来,实际占用会翻倍。
抓包建议用-c参数限制包数量,比如-c 5000,或者用 timeout 控制时间范围。我一般先-c 50小批量试探一下,确认过滤条件没写错、特征包能抓到,再重新跑一个-c 10000或者timeout 120 tcpdump ...抓两分钟。注意抓包机只是镜像流量的,抓包这个动作本身也会消耗 CPU,如果流量特别大,别在生产高峰期进行长时间全量抓包,容易把机器搞出额外的问题。
2. 网络不通的现场还原:SYN重传和ICMP报错才是关键证据
网络不通的现场,在抓包里长相其实是很有规律的。抛开那些花里胡哨的配置,真正能说明问题的就是两类包:TCP 的重传包和 ICMP 的报错包。下面把常见情况整理出来,直接对号入座。
2.1 三种“不通”的包长相,直接对号入座
第一种,SYN 发出去石沉大海,客户端过 1 秒、2 秒、4 秒地不断重发,这就是典型的 SYN 重传。重传间隔是在指数退避的,先 1 秒,再 2 秒,再 4 秒,再 8 秒。看到这个形态,说明 SYN 包既没有被确认,也没有收到 ICMP 报错,最可能的原因是:中间链路静默丢包、防火墙把包吞了,或者对端根本没有监听却又没发 RST。
第二种,收到了 ICMP 报文。这是网络设备在替对端“传话”。务必看 ICMP 的 type 和 code:
| ICMP 信息 | 含义 | 下一步动作 |
|---|---|---|
| type 3, code 1 | 目标主机不可达 | 检查路由表、ARP 表,大概率是路由黑洞 |
| type 3, code 3 | 目标端口不可达 | 对端服务没起来,或者防火墙策略拒绝 |
| type 3, code 13 | 管理禁止通信 | 基本可以判定是防火墙或安全设备拦截 |
| type 11 | TTL 超时 | 看是路由环路还是有设备多了跳数 |
第三种,收到 RST 包。RST 比 ICMP 有用得多,它说明网络层是通的,对端协议栈也已经收到了你的包,只不过它选择拒绝或重置。RST 出现的原因一般有两个:一是对端端口根本没监听,二是对端的安全组件主动 reset。前者去看服务状态,后者要查安全策略。
2.2 一次数据库连不上的完整排查链路:逐段排除法
去年的一个真实案例,某业务系统连数据库超时,客户端报错是常见的 connection timed out。我们做了三段式抓包排查:
第一段,客户端抓包。命令:
tcpdump -i eth0 -nn -c 200 'host 数据库IP and tcp port 3306'抓到的现象很明显:SYN 包重复出现,而且间隔是 1s、3s、7s,呈指数退避。这说明 TCP 三次握手的第一个包就没被确认。
第二段,数据库服务器抓包。由于服务器是核心设备,我们用交换机镜像口旁路抓包,不影响正常流量:
tcpdump -i mirror0 -nn -c 300 'host 客户端IP and tcp port 3306'结果:服务器侧一个包都没收到。到这里可以确认问题不在两端,而在这条路径中间的某个设备上。
第三段,在服务器前一级的网关接口抓包。这次抓到了客户端的 SYN,但它没被转发到服务器的接口上。后来和网络工程师确认,是安全设备对新网段的源地址转换策略漏配了一条,导致 SYN 被静默丢弃。补齐策略后,连接恢复。
这个案例最值得学习的一点是:每次抓包都有明确目标,每抓一段就排除一段,而不是漫无目的地瞎抓。另外注意一个坑:很多人习惯用 ping 通不通来推断 TCP 通不通,这是不对的。ICMP 和 TCP 走的路径不一定相同,防火墙策略也可能只放行了 ICMP 而没放行 TCP。ping 通了只能代表 ICMP 通,TCP 要单独验证。
3. 延迟太高的拆解手法:把端到端延迟拆成三段看
网络不通定位还算简单,延迟高反而更容易让人头大。因为“慢”是一个相对概念,你得先搞清楚到底是哪一段慢,才知道该去优化谁。端到端的延迟,可以从抓包里拆成三段来看。
3.1 关键时间差的读法:SYN到SYN-ACK、ClientHello到ServerHello
客户端发起 TCP 连接,到收到响应数据,中间可以拆成三个段:
- 第一段,客户端发出 SYN,到收到 SYN-ACK 的时间。这段就是网络往返时间 RTT,代表链路的基础延迟。
- 第二段,SYN-ACK 发出后,到应用层开始返回数据的时间。例如 HTTPS 场景,客户端发出 ClientHello,到收到 ServerHello 的时间。这一段主要反映服务端应用的处理速度和协议栈状态。
- 第三段,响应数据从服务端传输到客户端的时间。这段往往和第一段重叠,但涉及大文件传输、带宽瓶颈时,单独看传输速率很重要。
抓包命令不变,重点是把时间戳打开:
tcpdump -i eth0 -nn -tttt 'host 服务端IP and tcp port 443'然后手工看两个包之间的时间差。比如 SYN 的时间戳是 10:00:00.123456,SYN-ACK 的时间戳是 10:00:00.128912,那网络 RTT 大约是 5.5ms。如果这个值明显大于你预期的链路延迟,先查链路;如果这个值正常,但 ServerHello 等了大几百毫秒才出现,那就去查服务端应用。
3.2 重传、DUP ACK与乱序:链路质量的三面镜子
延迟高很多时候不是链路基础延迟高,而是丢包导致的重传。TCP 的重传是最直观的证据:同一个 seq 出现两次以上,就是重传。抓包时留意重传的时间间隔,指数退避间隔说明包丢失后等超时;间隔很短(几十毫秒)说明触发了快速重传,收到了多个重复 ACK。
DUP ACK 是链路乱序或丢包的信号。收到一个 ACK 重复三次,接收方就会快速重传。乱序则表现为 seq 号跳跃后又被补齐,中间丢包或链路做了多路径负载均衡。这些现象说明一个问题:链路质量不行,丢包多,TCP 的拥塞控制和重传机制在反复消耗时间和带宽。
有个经验供参考:长时间抓包统计重传率,如果重传包数占总包数超过 2%,基本可以认定链路质量有严重问题,需要查光模块光功率、误码率、交换机端口丢包统计。在服务器上执行ethtool -S eth0可以看网卡层的丢包计数,netstat -s可以看 TCP 层的重传统计,哪一层的计数异常,问题就往哪一层查。
3.3 一个“服务端慢”误判为“网络慢”的案例
某次业务升级后,第三方接口的调用延迟从 5ms 涨到 200ms,大家都以为是网络问题。客户端抓包一看,SYN 到 SYN-ACK 的时间大约 4ms,网络没问题。但 ClientHello 发出去之后,ServerHello 等了 180ms 才回来。
这个现象的关键在客户端抓包看不出来——因为客户端只能看到“对面回包慢”。我们到服务端也抓了一份包,对比时间戳发现:服务端收到 ClientHello 的时间是 t0,发出 ServerHello 的时间是 t0+180ms。也就是说,问题在服务端处理阶段,不在网络链路。后来查应用日志,发现是服务端线程池满了,业务线程全部阻塞在数据库连接等待上。优化连接池配置后,延迟回到 5ms。
这个案例说明,延迟高别急着骂网络,先看抓包里的时间差。如果 SYN 很快被确认,但业务首包迟迟不来,大概率是服务端处理慢;如果 SYN 确认本身就慢,那才是链路的问题。这个判断方法简单有效,能帮你省掉大量和网络团队扯皮的时间。
3.4 MTU导致的“不通不定时抽风”
还有一个容易被忽略的隐藏坑:MTU 不一致导致的大包不通、小包正常。现象很诡异——SSH 能连,但访问页面卡死;数据库能连,但大批量查询超时。抓包看到的现象是:TCP 连接建立正常,握手正常,但发到一定长度的数据后就静默无响应,没有重传,没有 ICMP 报错。
这多半是路径上某个设备的 MTU 小于本机网卡的 MTU,大包被丢弃,而且按常规行为应该回一个 ICMP “需要分片”的消息,但这个 ICMP 被防火墙屏蔽了。于是发送方一直等,等到 TCP 超时重传,再等,最后报错。
排查很简单,用 ping 做 MTU 探测:
ping -M do -s 1472 -c 3 目标IP ping -M do -s 1400 -c 3 目标IP如果 1472 丢包、1400 正常,说明路径 MTU 在 1400 到 1472 之间。配合 tcpdump 抓 ICMP 报错包:
tcpdump -i eth0 -nn 'icmp'如果看到 type 3, code 4(需要分片但设置了 DF 标记),基本可以锁定 MTU 问题。可以把本机网卡 MTU 调小做验证,但长期解法是找到中间设备的 MTU 配置,把链路打通。这个坑我见过不下三次,每次都是“时好时坏”的诡异现象,最后都指向 MTU。
4. 能直接抄的排查命令模板与实操收尾技巧
前面讲了很多原理,最后把常用的命令模板整理出来,遇到问题直接抄,同时分享两个我在实际排查中的收尾心得。
4.1 按场景抄命令:不通、慢、丢包、被拦截
| 排查场景 | 直接可用的命令 | 关键解读 |
|---|---|---|
| 网络不通(怀疑发出去了没人回) | tcpdump -i eth0 -nn -c 100 'host 目标IP and tcp[tcpflags] & tcp-syn != 0' -w /tmp/syn.cap | 只抓 SYN,看有没有重传,重传间隔是否指数退避 |
| 延迟高(初次连接慢) | tcpdump -i eth0 -nn -tttt 'host 目标IP and tcp port 443' -w /tmp/latency.cap | 抓完整握手过程,重点看 SYN 到 SYN-ACK 的间隔 |
| 怀疑链路丢包(大流量时慢) | tcpdump -i eth0 -nn -s 96 'host 目标IP' -w /tmp/loss.cap | 分析时筛选相同 seq 的重复出现次数,统计重传率 |
| 怀疑被防火墙拦截 | tcpdump -i eth0 -nn 'icmp and host 目标IP' | 看有没有 ICMP type 3 的报错,尤其关注 code 13 |
| 完全不知道哪的问题 | tcpdump -i eth0 -nn -s 96 -c 10000 -w /tmp/all.cap | 快速抓一个样本,用 Wireshark 打开,按三次握手和重传排序找问题 |
命令看着简单,但关键在于解读。比如最后一条全量抓包,不是让你看所有包,而是先按 TCP 会话分组,看哪些连接没完成握手、哪些连接有重传、哪些连接 RTT 异常。Wireshark 里有现成的统计功能,直接把 tcpdump 生成的 cap 文件导入,比用命令行看原始输出高效得多——尤其是处理大文件的时候。
4.2 配合使用的小工具:ss、ethtool、iptables计数
tcpdump 给出的是“现象”,要定位到“原因”,还得配合其他工具看状态。我常用的组合是:
ss -tinp:看当前连接的发送和接收队列,如果有连接堆积在Send-Q,说明要么对方接收慢,要么本地拥塞。ethtool -S eth0:看网卡统计,重点看rx_dropped、tx_dropped、rx_errors,这些是网卡层面丢包的铁证。iptables -L -nvx:看 DROP 计数,如果某个链的计数器不停上涨,基本就是策略拦截。检查完记得清空计数器:iptables -Z,再观察一段时间,看哪些链在涨。
这些工具输出的是实时状态,和 tcpdump 抓到的包特征放一起看,一个管“包在哪丢了”,一个管“设备状态怎么样”,互相印证,排查效率能提高不少。
4.3 最后分享两个实操心得
第一个心得,抓包前先把现场的元信息记录清楚。我习惯在抓包前用一句话记录:问题现象是什么、预期值是多少、实际值是多少、客户端和服务端各自抓包的起始时间、抓包文件的路径。排查结束后再对照时间戳,往往能发现之前忽略的细节。比如有一次延迟问题,我盯着 Wireshark 看了半天没头绪,后来发现客户端和服务端抓包时间差了 30 秒,导致两边对比时间戳完全对不上,白折腾了半小时。
第二个心得,抓包不要裸奔,先小批量试探。我每次抓包都先-c 50小跑一下,确认特征包已经抓到,再跑正式的大任务。如果小批量跑下来一个包都没有,先检查过滤器写没写对、网卡选没选对,别直接开着大任务挂半天,回头发现抓了个寂寞。tcpdump 的-c参数就是这个用途,限定包数自动退出,既能控制文件大小,也不会把机器打挂。
网络排查说白了就是一步一步缩小范围:先看两端能不能通,再看中间哪段出了问题,最后落在具体设备上。过程中少点玄学猜测,多点抓包证据,问题大都能在半小时内收敛。下次再遇到“网络不通”或者“延迟太高”,别急着拍脑袋,先把 tcpdump 的抓包方案定好,按这套思路走一遍,罪魁祸首基本跑不掉。