TCP/IP(十三):抓包与网络诊断方法论

写在前面

上一篇把 NAT、DHCP 这些“地址怎么来、怎么转换”的机制讲完了,五层模型从下到上的协议也就齐了。但会背协议和“真的会排障”是两回事——线上一个“网站打不开”,到底是 DNS 挂了、网关不通、端口被占,还是应用自己炸了?

本篇是系列收官,目标是把前十二篇的原理落到工具上:先给一张网络诊断工具速查表,再重点拆 tcpdump 和 Wireshark,最后给一套按层排查的连通性方法论。结尾会把整个 TCP/IP 系列串一遍,并指向本站相关的实战文章。


一、工具速查表

先一张总表,每层对应的常用工具:

工具解决什么问题
网络层ping连通性 + RTT(基于 ICMP Echo)
网络层traceroute / mtr观察探测路径与逐跳响应,辅助定位异常区间(基于 TTL)
传输层ss / netstat连接状态、监听端口、TIME_WAIT 堆积
应用层dig / nslookup / hostDNS 解析是否正常
全层tcpdump抓包,看链路上真实在跑什么
全层Wireshark读 pcap、按协议分层解析、交互式过滤

下面挑最常用的逐个说清楚。

ping:连通性与 RTT

ping 发 ICMP Echo Request,对端回 ICMP Echo Reply,用来判断可达性往返延迟(RTT)

1
2
3
4
5
6
$ ping -c 4 8.8.8.8
PING 8.8.8.8 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=115 time=12.3 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=115 time=11.9 ms
...
4 packets transmitted, 4 received, 0% packet loss

读懂输出:ttl 是应答到达你时剩余的跳数;只有先假设对端使用了某个常见初始 TTL,才能粗略估计回程跳数,而且回程路由可能与去程不同。time 是 RTT,丢包率是重要指标。注意 ping 不通不等于网络断了——很多服务器 / 防火墙会禁掉 ICMP,HTTP 服务照样能访问。

traceroute / mtr:逐跳路径

ping 只能观察目标是否回应特定 ICMP 探测;traceroute 则通过各跳返回的 ICMP 响应观察一条可能的探测路径,帮助缩小异常范围,但不能单凭某一跳不回应就断定业务包在那里丢失。原理是利用 IP 头里的 TTL

  • 先发一个 TTL=1 的包,第一跳路由器收到后 TTL 减到 0,丢弃并回 ICMP Time Exceeded
  • 再发 TTL=2 的包,第二跳路由器回 Time Exceeded;
  • 如此递增,直到到达目的或超时。
1
2
3
4
5
6
$ traceroute -n 8.8.8.8         # Linux/macOS,-n 不反解域名(更快)
 1  192.168.1.1     1.2 ms      # 家用路由器
 2  10.0.0.1        5.3 ms      # 运营商接入
 3  * * *                       # 这一跳禁了 ICMP,不代表故障
 ...
 9  8.8.8.8       12.1 ms

平台差异要注意:Linux 的 traceroute 默认发 UDP 包到高端口(33434 起),Windows 的 tracert 默认发 ICMP Echo。有的路由器只对 ICMP 放行、对 UDP 高端口丢包,所以同一目标两边结果可能不同。

mtr(my traceroute)结合了持续探测与逐跳统计,实时显示各跳响应率和延迟,是常用的路径诊断工具。中间节点可能限制 ICMP 回包;若某一跳显示丢包而后续节点和最终目标正常,通常不能认定业务流量在该跳丢失:

1
$ mtr -n 8.8.8.8                # 持续刷新,Ctrl+C 退出看汇总

一个常见误读:中间某跳显示丢包,但后面几跳正常——这往往是该路由器对 ICMP 限速(control-plane policing),不是真丢包。看最终目的的丢包率才靠谱。

ss / netstat:连接状态与监听端口

netstat 是老牌工具,现代 Linux 发行版通常推荐使用 ss(socket statistics)。两者有一部分相似的短参数,但语法、过滤能力和输出并不完全兼容。排查“端口被谁占了”“连接建立没建立”“TIME_WAIT 是不是爆了”都可以用到它们。

1
2
3
4
ss -tlnp                # 监听中的 TCP 端口(-t tcp -l listen -n 不解析 -p 进程)
ss -tnp                 # 已建立的 TCP 连接
ss -s                   # 连接状态汇总(ESTABLISHED / TIME-WAIT / ... 数量)
ss -tan state time-wait # 只看 TIME-WAIT 状态的连接

经典场景:服务起不来报 Address already in use,用 ss -tlnp | grep :8080 看端口被哪个进程占着;短连接打多了,ss -s 里 TIME-WAIT 几万条,就是连接复用要做优化了(详见 TCP 那篇的状态机)。

dig / nslookup / host:DNS

“能访问 IP、按域名访问失败”时,DNS 是优先检查项之一,但 Host / SNI、代理、虚拟主机和应用配置也可能造成相同表象。三个工具各有侧重:

1
2
3
4
5
6
7
dig www.example.com              # 最详细:显示完整的查询过程和 TTL
dig +short www.example.com       # 只要结果 IP,适合脚本
dig @8.8.8.8 www.example.com     # 指定用哪个 DNS 服务器查
dig MX example.com               # 查特定记录类型(MX/NS/TXT/A ...)

nslookup www.example.com         # 老牌,交互式,输出不如 dig 清晰
host www.example.com             # 最精简,只给一句话结论

排 DNS 时常用 dig @指定DNS 域名 对比。不同解析器结果不一致,说明问题与解析路径有关,可能是本地缓存或配置,也可能是 split-horizon、区域调度、DNS 劫持或不同解析器命中了不同 CDN 节点,需要结合权威记录和业务预期继续判断。


二、tcpdump:抓包与过滤语法

上面这些工具都是"问别人",tcpdump 是直接看链路上跑的原始字节。它是网络排障的终极武器,也是 Wireshark 抓包数据的命令行来源。

基本用法

1
2
3
4
5
6
tcpdump -i eth0                 # 监听 eth0 网卡(-i 指定接口)
tcpdump -i eth0 -n              # -n 不反解 IP 为域名(快、输出干净)
tcpdump -i eth0 -nn             # -nn 连端口也不反解
tcpdump -i eth0 -c 100          # 抓够 100 个包就停
tcpdump -i eth0 -w out.pcap     # 写入 pcap 文件(给 Wireshark 看)
tcpdump -i eth0 -w out.pcap -W 1 -G 60   # 每 60 秒轮转一个文件

习惯上排障时用 tcpdump -i any -nn 监听所有网卡、不解析名字,先看清流量再说。

过滤表达式(BPF 语法)

tcpdump 用的是 BPF(Berkeley Packet Filter)表达式,逻辑是"按条件筛选包"。这套语法也适用于 Wireshark 的抓包过滤器(capture filter,注意和显示过滤器 display filter 不是一回事)。核心原语:

原语含义示例
host源或目的是某 IPhost 8.8.8.8
src host / dst host只匹配源 / 目的src host 192.168.1.10
net匹配网段net 192.168.1.0/24
port源或目的是某端口port 80
src port / dst port只匹配源 / 目的端口dst port 443
tcp / udp / icmp按协议tcp and port 80
and / or / not逻辑组合host A and port 80

实战组合:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 抓与某主机的所有 HTTP 流量
tcpdump -i eth0 -nn host 93.184.216.34 and tcp port 80

# 抓某个客户端的所有 DNS 查询(UDP 53)
tcpdump -i eth0 -nn src host 192.168.1.10 and udp port 53

# 只抓 TCP SYN 包(连接建立的第一个包)——发现谁在发起连接
tcpdump -i eth0 -nn 'tcp[tcpflags] & tcp-syn != 0'

# 抓 SYN 但不带 ACK 的包(纯握手起始,排除 SYN-ACK)
tcpdump -i eth0 -nn 'tcp[tcpflags] & (tcp-syn|tcp-ack) == tcp-syn'

# 抓所有 RST(连接被重置)
tcpdump -i eth0 -nn 'tcp[tcpflags] & tcp-rst != 0'

TCP 标志位的写法要特别说明:tcp[tcpflags] 是 TCP 头里标志字段,后面跟位掩码 &。可用的标志常量是 tcp-fintcp-syntcp-rsttcp-pushtcp-acktcp-urg。想抓"某组合位都被置上",用 == (tcp-syn|tcp-ack);想抓"只要含某位",用 & tcp-syn != 0

语法细节参考官方 pcap-filter(7) man page。记住一点:tcpdump 的过滤表达式和 Wireshark 的显示过滤器是两套语法 。tcpdump 过滤(BPF)在抓包时就生效,决定哪些包写入 pcap;Wireshark 显示过滤是抓完之后再筛,语法完全不同(如 ip.addr == 8.8.8.8)。


三、Wireshark:分层解析利器

tcpdump 适合在服务器上快速看一眼,真正要"逐字段拆解协议",还得靠 Wireshark。典型工作流是:服务器上 tcpdump -w 存成 pcap → 下载到本地用 Wireshark 打开。

Wireshark 的核心能力是按协议分层展开一个包:点开一个帧,能看到以太网头、IP 头、TCP 头、HTTP 报文逐层嵌套,每一层的每个字段(TTL、序号、标志位、窗口大小……)都标好了含义——正好对应第一篇讲的封装。

显示过滤器(Display Filter)

Wireshark 的显示过滤器是一套独立语法,比 BPF 强大得多:

过滤表达式含义
ip.addr == 8.8.8.8源或目的是 8.8.8.8
tcp.port == 443TCP 端口 443
tcp.flags.syn == 1SYN 标志位置 1
tcp.flags.reset == 1RST 包
dns所有 DNS 报文
http.request.method == "GET"HTTP GET 请求
tls.handshake.type == 1TLS ClientHello

还能按协议右键某个字段 → “作为过滤器应用”,组合条件非常方便。

常用排障视角

  • TCP 流跟踪:右键一个 TCP 包 → Follow → TCP Stream,把一个连接的请求 / 响应整理成可读的对话,看 HTTP 在裸 TCP 上是怎么一来一回的。
  • 统计 → 会话(Conversations):列出所有连接的端点、包数、字节数,快速发现"谁在异常大量通信"。
  • 统计 → IO 图表:按时间画吞吐量曲线,定位性能毛刺。

Wireshark 配合前十二篇的协议知识,基本是"协议的活体解剖"——你能在里面亲眼看到三次握手、TLS 握手、HTTP 请求头、DNS 查询响应。


四、分层连通性排障方法论

工具会用了,关键是怎么有章法地用 。网络问题的典型困境是:现象一样(“连不上”),原因五花八门。乱试一通效率极低,正确做法是沿协议栈自底向上逐层排除,在哪一层断了就停在那查。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
应用层    DNS 解析对吗?HTTP/TLS 正常吗?应用本身有没有报错?
   ▲         │  dig / curl -v / 应用日志
   │         ▼
传输层    端口通吗?连接能建立吗?TIME_WAIT 堆积了吗?
   ▲         │  ss / telnet host port / nc -zv
   │         ▼
网络层    网关能 ping 通吗?路由对吗?哪一跳丢包?
   ▲         │  ping / ip route / traceroute / mtr
   │         ▼
链路层    ARP 学到吗?MAC 对得上吗?网卡有没有丢包错包?
   ▲         │  arp -a / ip -s link
   │         ▼
物理层    网线插好了吗?网卡灯亮吗?交换机端口正常吗?
             眼看 / ethtool

每一层的判断准则:这一层通了,才往上查;这一层不通,先修这一层 。原因很简单——下面断了,上面必然全断;下面不通时去调应用,纯属浪费时间。

每层的关键检查项

物理层:看网卡灯、ethtool eth0 看链路是否 up、有没有大量物理层错误。云服务器里这层基本是虚拟的,可跳过。

链路层ip neigh(旧 arp -n)看 ARP 表,目标 IP 的 MAC 学到了吗?ip -s link 看网卡有没有 RX/TX 丢包、错误帧。VLAN、二层隔离的问题也在这层。

网络层

  • ping 网关 通吗?不通说明本机到网关就有问题(链路层或本机配置)。
  • ping 8.8.8.8 有回应吗?有回应只说明这条 ICMP 路径此刻可达,不代表所有出口协议和目标都正常;无回应时可换目标并结合 traceroute / mtr 观察异常从哪一段开始持续出现。
  • ip route 看默认路由在不在、对不对。

传输层

  • ss -tlnp 确认服务真的在监听那个端口、那个地址(监听 127.0.0.1:8080 是连不上外部访问的)。
  • nc -zv host porttelnet host port 测端口可达性。
  • ss -s 看连接状态,TIME_WAIT / CLOSE_WAIT 堆积是常见病。

应用层

  • dig 验证 DNS。
  • curl -v 看完整的 HTTP / TLS 交互,分清是 DNS、连接、TLS 还是应用层返回错误。
  • 看应用自身日志——很多"网络问题"最后是应用抛的异常。

五、实战:网站打不开怎么按层排查

把方法论套到一个真实场景。假设用户反馈 https://api.example.com 打不开,按层走一遍:

1. 物理层 / 链路层(先确认本机网络没断)

1
2
3
$ ip -s link show eth0     # 网卡 up,无错包
$ ping 192.168.1.1         # ping 网关
64 bytes from 192.168.1.1: time=0.5 ms   # 通

本机到网关正常,底层没问题。

2. 网络层(确认出网正常)

1
2
3
$ ping 8.8.8.8
64 bytes from 8.8.8.8: time=12 ms        # 出网正常
$ ping 1.1.1.1                            # 换个目标再确认

能 ping 通某个公网 IP,说明到该目标的 ICMP 往返路径可达,但不能据此断言整个网络层或所有出口策略都正常。如果这步异常,可更换探测目标,并用 mtr -n 8.8.8.8 观察异常是否从某一跳开始延续到最终目标。

3. 应用层 - DNS(域名能解析吗)

1
2
3
4
$ dig api.example.com +short
()                                     # 解析不出来!
$ dig @8.8.8.8 api.example.com +short
1.2.3.4                                  # 换 DNS 能解析

定位到 DNS 路径差异后,再检查本机、递归解析器和权威记录。使用 systemd-resolved 的新系统可执行 resolvectl flush-caches;DNS 配置通常由 NetworkManager、systemd-resolved 或 DHCP 管理,不要在未确认管理方式前直接修改可能被自动覆盖的 /etc/resolv.conf

4. 传输层(端口可达吗,假设 DNS 修好了拿到 IP)

1
2
$ nc -zv 1.2.3.4 443
Connection to 1.2.3.4 443 port [tcp/https] succeeded!   # 端口通

端口通说明 TCP 层没问题。如果这里超时,多半是对端没监听、被防火墙拦或本机出口被限。

5. 应用层 - HTTP/TLS(服务正常响应吗)

1
2
3
4
5
6
$ curl -v https://api.example.com
*   Trying 1.2.3.4:443...
* Connected
* TLS handshake                       # 卡在这里?
> GET / HTTP/2
< HTTP/2 500                          # 服务端报错

curl -v 会展示连接、TLS 握手和 HTTP 请求响应等阶段。失败阶段能帮助确定优先排查方向,但跨层依赖和中间设备会让同一根因表现为不同阶段失败,不能把它当成严格的一一映射。这里若是 TLS 失败,应继续检查证书、时间、SNI 和协议协商;若收到 HTTP 500,说明请求已经到达某个 HTTP 服务,重点转向应用及其后端依赖。

整个过程对应的方法论就是:从下到上逐层排除,用工具验证每一层的假设,定位到具体层后再深入 。比起一上来就重启服务、瞎改配置,这套方法省时得多。


小结

  • 网络诊断要按层排查:物理 → 链路(ARP/MAC)→ 网络(ping/路由/traceroute)→ 传输(端口/TIME_WAIT)→ 应用(DNS/HTTP/TLS),一层通了才往上查。
  • ping 观察 ICMP 可达性与 RTT,traceroute / mtr 观察探测路径和异常区间,ss / netstat 看连接与端口,dig 查 DNS。
  • tcpdump 是抓包终极武器,过滤用 BPF 语法(host/port/tcp[tcpflags] & tcp-syn),存成 pcap 交给 Wireshark。
  • Wireshark 按协议分层展开,配合系列前十二篇的知识能看懂每个字段;显示过滤器是独立语法,别和 BPF 混。

系列总结

十三篇,从"为什么要分层"到"怎么抓包排障",TCP/IP 的主干终于串完了。回顾整个系列,可以浓缩成一条主线:数据在发送端被逐层封装,在接收端被逐层解封装,每一层只解决自己的问题

主题一句话要点
1分层模型OSI 七层是参考、TCP/IP 四层贴实际、五层是教学折中
2–3链路层以太网帧 + MAC 地址,ARP 把 IP 翻译成 MAC,交换机按 MAC 转发
4–6网络层IP 编址与子网划分,路由怎么选路,ICMP 反馈通断
7–9传输层TCP 三次握手 / 四次挥手 / 状态机,可靠性与流控拥塞控制,UDP 轻量无连接
10–11应用层DNS 分层解析与缓存,HTTP 语义、连接复用与 TLS
12地址机制NAT/NAPT 改写地址省公网 IP,DHCP 自动分配 IP,CGNAT 让家网失去公网 IP
13诊断方法论工具速查 + 按层排障

把这些拼起来,一个 HTTP 请求的全貌就是:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
应用层:构造 HTTP 报文
   │ DNS 解析域名 → 拿到 IP(应用层,但贯穿)
   │ HttpClient / epoll 在这层调度 I/O
传输层:套 TCP 头,三次握手建连接,按序可靠传输
网络层:套 IP 头,路由逐跳转发
链路层:套帧头尾,ARP 找到下一跳 MAC,交换机转发
物理层:比特流出网线
   │  出口 NAT 改写源 IP:端口 → 公网
   │  对端反向解封装,层层上交到应用
对端应用收到报文,返回响应走反向全程

每一步都对应系列里某一篇的原理。理解了这条链,再回头看平时写的后端代码,很多"玄学问题"就有了着落。

网络是后端、云原生、分布式共同的地基,本站还有几篇从应用侧把网络用起来的实战文章,正好衔接:

TCP/IP 系列到此完结。原理是死的,排障是活的——希望你下次遇到网络问题时,能从容地掏出这套分层方法论,而不是只会重启服务。


参考:

Licensed under CC BY-NC-SA 4.0