10个排查网络问题的命令行工具:每个配真实输出,建议收藏
前言
线上出问题的时候,最怕的不是问题本身,而是不知道用什么工具去看。
ping不通?是网络不通还是端口没开?DNS没解析?是本地缓存还是权威DNS的问题?连接超时?是防火墙拦了还是目标服务没响应?
这些问题,一个命令就能定位。但你得知道用哪个命令、怎么看输出。
今天这篇,10个工具,从最常见的到冷门但救命的,每个都配真实输出和使用场景。不讲man page上那些参数罗列,只讲你排查问题时真正会敲的命令。
工具1:ping
场景: 第一步,确认目标是否可达。
bash
ping -c 4 10.0.1.60
python
PING 10.0.1.60 (10.0.1.60) 56(84) bytes of data.
64 bytes from 10.0.1.60: icmp_seq=1 ttl=64 time=0.312 ms
64 bytes from 10.0.1.60: icmp_seq=2 ttl=64 time=0.287 ms
64 bytes from 10.0.1.60: icmp_seq=3 ttl=64 time=0.295 ms
64 bytes from 10.0.1.60: icmp_seq=4 ttl=64 time=0.301 ms
--- 10.0.1.60 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3004ms
rtt min/avg/max/mdev = 0.287/0.298/0.312/0.012 ms
重点看什么
| 字段 | 含义 | 异常信号 |
|---|---|---|
packet loss |
丢包率 | > 0% 就有问题 |
time |
单次往返延迟 | 同机房应 < 1ms,同城 < 5ms |
ttl |
生存跳数 | 突然变化说明路由路径变了 |
mdev |
延迟抖动 | 值大说明网络不稳定 |
高频用法
bash
# 用指定大小的包测试(排查MTU问题)
ping -s 1472 -c 4 目标IP
# 持续ping,观察网络波动(Ctrl+C停止)
ping 目标IP
# 指定源地址(多网卡场景)
ping -I eth1 目标IP
ping不通不代表网络不通
这是最常见的误解。ping不通的可能原因:
- 目标机器禁了ICMP(iptables -j DROP)
- 中间防火墙拦截了ICMP
- 目标服务正常,只是不响应ping
ping只能证明"ICMP可达",不能证明"服务可用"。 ping通了不代表端口开着,ping不通不代表服务挂了。
工具2:telnet / nc(端口连通性)
场景: ping通了,但服务连不上。用telnet或nc测试端口级别的连通性。
bash
telnet 10.0.1.60 3306
sql
Trying 10.0.1.60...
Connected to 10.0.1.60.
Escape character is '^]'.
看到 Connected to 就说明端口通。如果卡在 Trying... 最后超时,说明端口不通。
bash
# nc更轻量,脚本中更常用
nc -zv 10.0.1.60 3306
css
Connection to 10.0.1.60 3306 port [tcp/mysql] succeeded!
bash
# 测试不通的情况
nc -zv 10.0.1.60 3307 -w 3
yaml
nc: connect to 10.0.1.60 port 3307 (tcp) failed: Connection refused
输出含义
| 结果 | 含义 | 排查方向 |
|---|---|---|
succeeded |
端口通,服务在监听 | 问题在应用层 |
Connection refused |
端口不通,目标主动拒绝 | 服务没启动,或iptables REJECT |
| 超时无输出 | 包被丢弃 | 防火墙DROP,或网络不通 |
Connection refused 和 超时的区别非常重要:
- refused = 到达了目标机器,目标说"这个端口没人监听"。说明网络通,问题在目标服务。
- 超时 = 包根本没到达目标机器,或者到了但被防火墙默默丢掉。说明网络层有问题。
bash
# 批量测试多个端口
nc -zv 10.0.1.60 80 443 3306 6379
# 扫描一段端口
nc -zv 10.0.1.60 8000-8010
工具3:traceroute / mtr(路由追踪)
场景: 访问慢,想知道数据包经过了哪些节点、在哪一跳出问题。
bash
traceroute -n 10.0.1.60
arduino
traceroute to 10.0.1.60, 30 hops max, 60 byte packets
1 10.0.0.1 0.5 ms 0.4 ms 0.3 ms
2 172.16.0.1 1.2 ms 1.1 ms 1.3 ms
3 * * *
4 192.168.1.1 3.5 ms 3.2 ms 3.8 ms
5 10.0.1.60 5.1 ms 4.9 ms 5.0 ms
第3跳全是 * * *,说明那一跳的设备不回ICMP(很常见,不代表有问题)。
traceroute的问题: 只跑一次,结果可能不准。
mtr:traceroute的升级版
bash
mtr -n -c 100 10.0.1.60
erlang
Host Loss% Snt Last Avg Best Wrst StDev
1. 10.0.0.1 0.0% 100 0.4 0.5 0.3 0.8 0.1
2. 172.16.0.1 0.0% 100 1.2 1.3 1.0 2.1 0.2
3. 192.168.1.1 5.0% 100 3.5 3.8 3.0 8.2 1.1
4. 10.0.1.60 0.0% 100 5.1 5.0 4.8 5.5 0.1
重点看什么
| 字段 | 含义 |
|---|---|
Loss% |
该跳的丢包率 |
Avg |
平均延迟 |
Wrst |
最大延迟(看尖刺) |
StDev |
延迟标准差(越大越不稳定) |
关键判断: 如果某一跳Loss%突然升高,且后续所有跳都保持这个丢包率,那问题就在那一跳。如果某一跳Loss%高但后续跳恢复正常,那大概率是那一跳设备限速ICMP,不影响实际流量。
bash
# TCP模式(绕过ICMP限制,更接近真实流量路径)
mtr -T -P 80 -n -c 50 目标IP
# 指定源地址
mtr -a 10.0.1.50 -n -c 50 目标IP
工具4:dig / nslookup(DNS排查)
场景: 域名访问异常,怀疑DNS解析有问题。
bash
dig api.example.com
css
;; ANSWER SECTION:
api.example.com. 300 IN A 5.6.7.8
;; Query time: 23 msec
;; SERVER: 223.5.5.5#53(223.5.5.5)
高频用法
bash
# 直接查权威DNS(绕过本地缓存)
dig api.example.com @ns1.dnspod.net +short
# 5.6.7.8
# 查不同DNS服务器的结果对比
dig api.example.com @114.114.114.114 +short # 国内公共DNS
dig api.example.com @223.5.5.5 +short # 阿里DNS
dig api.example.com @8.8.8.8 +short # Google DNS
dig api.example.com @1.1.1.1 +short # Cloudflare DNS
# 只看结果,不看额外信息
dig +short api.example.com
# 查看完整解析链路(从根DNS开始追踪)
dig +trace api.example.com
+trace 输出(非常有用)
erlang
. 518400 IN NS a.root-servers.net.
. 518400 IN NS b.root-servers.net.
...
com. 172800 IN NS a.gtld-servers.net.
...
example.com. 86400 IN NS ns1.dnspod.net.
...
api.example.com. 300 IN A 5.6.7.8
从根→顶级域→权威DNS逐级追踪,能看到每一级返回了什么。如果中间某一级返回了错误的结果,就能定位问题在哪个DNS服务商。
bash
# 查反向解析(IP → 域名)
dig -x 5.6.7.8 +short
# 查所有记录类型
dig example.com ANY +noall +answer
# 查TTL
dig example.com +noall +answer +ttlid
DNS排查速查表
| 现象 | 排查命令 | 可能原因 |
|---|---|---|
| 解析到错误IP | dig @权威DNS 对比 dig @LocalDNS |
LocalDNS缓存未刷新或被劫持 |
| 解析慢 | dig 看Query time |
DNS服务器响应慢或网络延迟 |
| 部分地区解析错误 | 从不同DNS查询对比 | DNS服务商配置不一致 |
| 间歇性解析失败 | dig +trace 逐级排查 |
某级DNS服务器不稳定 |
工具5:ss(替代netstat)
场景: 查看当前服务器的连接状态、端口监听情况。
bash
# 查看所有监听端口
ss -tlnp
ruby
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=1234,fd=3))
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=5678,fd=6))
LISTEN 0 128 127.0.0.1:3306 0.0.0.0:* users:(("mysqld",pid=9012,fd=23))
重点看什么
| 字段 | 含义 | 异常信号 |
|---|---|---|
LISTEN 的地址 |
0.0.0.0 = 全网监听,127.0.0.1 = 仅本地 |
数据库只该监听127.0.0.1 |
Recv-Q |
接收队列积压 | LISTEN状态下 > 0 说明有连接等待accept |
Send-Q |
发送队列积压 | ESTABLISHED状态下积压说明发送缓冲区满 |
bash
# 按状态统计连接数
ss -ant | awk '{print $1}' | sort | uniq -c | sort -rn
yaml
5000 ESTABLISHED
2000 TIME_WAIT
500 CLOSE_WAIT
50 SYN_SENT
bash
# 查看特定端口的连接
ss -tnp | grep :3306
# 查看特定状态的连接
ss -ant state time-wait
ss -ant state close-wait
# 看连接的详细信息(包含timer)
ss -ant -o state established '( dport = :3306 or sport = :3306 )'
bash
# 查看socket统计摘要
ss -s
yaml
Total: 8500
TCP: 7550 (estab 5000, closed 2000, orphaned 50, timewait 2000)
Transport Total IP IPv6
RAW 2 1 1
UDP 10 8 2
TCP 5550 5500 50
...
ss比netstat快10倍以上,连接数多的服务器上netstat可能卡住,ss秒出结果。
工具6:tcpdump(抓包神器)
场景: 以上工具都定位不了问题时,抓包看实际网络交互。
bash
# 抓指定端口的包
tcpdump -i eth0 port 8080 -nn
ini
14:23:01.123456 IP 10.0.1.50.45678 > 10.0.1.60.8080: Flags [S], seq 1000000
14:23:01.123789 IP 10.0.1.60.8080 > 10.0.1.50.45678: Flags [S.], seq 2000000, ack 1000001
14:23:01.124012 IP 10.0.1.50.45678 > 10.0.1.60.8080: Flags [.], ack 2000001
14:23:01.125678 IP 10.0.1.50.45678 > 10.0.1.60.8080: Flags [P.], seq 1000001:1000200, ack 2000001
14:23:01.126001 IP 10.0.1.60.8080 > 10.0.1.50.45678: Flags [.], ack 1000200
高频用法
bash
# 抓指定主机之间的包
tcpdump -i eth0 host 10.0.1.50 and host 10.0.1.60 -nn
# 只抓SYN包(看谁在建连接)
tcpdump -i eth0 'tcp[tcpflags] & tcp-syn != 0' -nn
# 只看TCP握手(排除数据包)
tcpdump -i eth0 'tcp[tcpflags] & (tcp-syn|tcp-fin) != 0' -nn
# 抓DNS查询
tcpdump -i eth0 port 53 -nn
# 保存到文件,用Wireshark分析
tcpdump -i eth0 port 80 -nn -w /tmp/capture.pcap
# 限制抓包数量
tcpdump -i eth0 port 80 -nn -c 100
# 读取pcap文件
tcpdump -nn -r /tmp/capture.pcap
实战:排查连接超时
bash
tcpdump -i eth0 host 10.0.1.60 and port 3306 -nn
场景A:看到SYN重传
ini
14:23:01.000 IP 10.0.1.50.45678 > 10.0.1.60.3306: Flags [S]
14:23:02.000 IP 10.0.1.50.45678 > 10.0.1.60.3306: Flags [S] ← 1秒后重传
14:23:04.000 IP 10.0.1.50.45678 > 10.0.1.60.3306: Flags [S] ← 2秒后重传
只有SYN出去,没有SYN+ACK回来。说明包被中间设备丢掉了(防火墙拦截)或目标端口没开。
场景B:看到RST
ini
14:23:01.000 IP 10.0.1.50.45678 > 10.0.1.60.3306: Flags [S]
14:23:01.001 IP 10.0.1.60.3306 > 10.0.1.50.45678: Flags [R.] ← RST
目标端口收到SYN后直接回RST。说明目标机器上3306端口没有进程在监听。
场景C:看到完整握手但数据传输后超时
makefile
14:23:01.000 SYN
14:23:01.001 SYN+ACK
14:23:01.002 ACK
14:23:01.003 发送请求数据
14:23:06.003 发送请求数据(5秒后重传)
14:23:16.003 发送请求数据(10秒后重传)
握手正常,但请求数据发出去后没有响应。问题在应用层------服务端收到请求但没处理,或者处理了但响应没发出来。
工具7:curl(HTTP层诊断)
场景: 网络通、端口通,但HTTP请求有问题。
bash
# 看完整的请求和响应头
curl -v http://api.example.com/health
markdown
* Trying 5.6.7.8:80...
* Connected to api.example.com (5.6.7.8) port 80
> GET /health HTTP/1.1
> Host: api.example.com
>
< HTTP/1.1 200 OK
< Content-Type: application/json
< Content-Length: 15
<
{"status":"ok"}
高频用法
bash
# 只看响应头(不下载body)
curl -I http://api.example.com
# 看每个阶段耗时(排查慢在哪)
curl -o /dev/null -s -w "
DNS解析: %{time_namelookup}s
TCP连接: %{time_connect}s
TLS握手: %{time_appconnect}s
首字节时间: %{time_starttransfer}s
总耗时: %{time_total}s
" http://api.example.com
makefile
DNS解析: 0.023s
TCP连接: 0.045s
TLS握手: 0.120s
首字节时间: 0.350s
总耗时: 0.380s
这个输出极其有用。一眼就能看出慢在哪个阶段:
| 阶段 | 耗时大说明 |
|---|---|
| DNS解析慢 | DNS服务器有问题 |
| TCP连接慢 | 网络延迟或服务端backlog满 |
| TLS握手慢 | 证书问题或CPU瓶颈 |
| 首字节时间慢 | 服务端处理慢(查应用日志) |
| 总耗时-首字节时间差大 | 响应体大,带宽瓶颈 |
bash
# 指定DNS解析(跳过本地DNS)
curl --resolve api.example.com:80:5.6.7.8 http://api.example.com
# 模拟HTTPS但忽略证书错误(测试环境用)
curl -k https://api.example.com
# 指定源IP(多网卡场景)
curl --interface eth1 http://api.example.com
# 跟随重定向
curl -L http://api.example.com
工具8:ip route / ip rule(路由排查)
场景: 服务器多网卡、多出口,流量走了错误的路径。
bash
# 查看路由表
ip route show
scss
default via 10.0.0.1 dev eth0
10.0.0.0/24 dev eth0 proto kernel scope link src 10.0.0.50
10.0.1.0/24 dev eth1 proto kernel scope link src 10.0.1.50
172.16.0.0/16 via 10.0.0.1 dev eth0
重点看什么
bash
# 查看到特定目的地走哪条路由
ip route get 10.0.1.60
css
10.0.1.60 dev eth1 src 10.0.1.50
bash
ip route get 8.8.8.8
css
8.8.8.8 via 10.0.0.1 dev eth0 src 10.0.0.50
场景: 你期望访问10.0.1.60走eth1,但实际走了eth0,导致从错误的源IP发出,被对端防火墙拒绝。
bash
# 查看策略路由规则
ip rule show
sql
0: from all lookup local
32764: from 10.0.1.50 lookup 100
32765: from 10.0.0.50 lookup 200
32766: from all lookup main
32767: from all lookup default
bash
# 查看特定路由表
ip route show table 100
sql
default via 10.0.1.1 dev eth1
10.0.1.0/24 dev eth1 scope link
多网卡环境下,策略路由决定了"从哪个IP进来的流量,从哪个IP回去"。如果配错了,会出现请求能到但回包走错路径的问题,表现为连接建立后立刻断开或超时。
工具9:ethtool(网卡底层状态)
场景: 网络时好时坏,怀疑是物理层或驱动层的问题。
bash
ethtool eth0
yaml
Settings for eth0:
Speed: 1000Mb/s
Duplex: Full
Auto-negotiation: on
Link detected: yes
Statistics:
rx_packets: 12345678
tx_packets: 9876543
rx_errors: 0
tx_errors: 0
rx_dropped: 150
tx_dropped: 0
rx_crc_errors: 0
rx_fifo_errors: 0
重点看什么
| 字段 | 含义 | 异常信号 |
|---|---|---|
Speed |
协商速率 | 本该千兆但显示百兆/十兆 |
Duplex |
双工模式 | 显示Half就是半双工,性能大降 |
Link detected |
物理链路 | no = 网线没插或交换机端口没开 |
rx_errors |
接收错误包数 | 持续增长说明物理链路有问题 |
rx_dropped |
接收丢包 | 内核缓冲区满或ring buffer太小 |
rx_crc_errors |
CRC校验错误 | 网线质量差或光模块故障 |
bash
# 查看ring buffer大小
ethtool -g eth0
yaml
Pre-set maximums:
RX: 4096
TX: 4096
Current hardware settings:
RX: 256
TX: 256
当前RX只有256,最大支持4096。高流量场景下可能不够用:
bash
# 调大ring buffer
ethtool -G eth0 rx 2048 tx 2048
bash
# 查看网卡驱动和固件版本(排查驱动bug)
ethtool -i eth0
makefile
driver: virtio_net
version: 1.0.0
firmware-version:
虚拟机用的virtio_net,物理机通常是ixgbe、bnxt_en、mlx5_core等。如果遇到奇怪的网络问题,搜一下对应驱动的已知bug。
工具10:strace(系统调用追踪)
场景: 以上工具都查不出问题,进程行为异常但不知道卡在哪。strace能看到进程的每一个系统调用。
bash
# 追踪一个进程的所有网络相关系统调用
strace -f -e trace=network -p 12345
scss
[pid 12345] socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) = 8
[pid 12345] connect(8, {sa_family=AF_INET, sin_port=htons(3306), sin_addr=inet_addr("10.0.1.60")}, 16) = 0
[pid 12345] sendto(8, "...query...", 42, MSG_NOSIGNAL, NULL, 0) = 42
[pid 12345] recvfrom(8, <unfinished ...>
卡在 recvfrom,说明请求发出去了但在等对端回复。问题在对端。
高频用法
bash
# 追踪文件和网络操作
strace -f -e trace=file,network -p 12345
# 带时间戳
strace -f -T -e trace=network -p 12345
scss
[pid 12345] connect(8, {...sin_port=htons(3306)...}, 16) = 0 <0.003125>
[pid 12345] sendto(8, "...", 42, 0, NULL, 0) = 42 <0.000089>
[pid 12345] recvfrom(8, "...", 16384, 0, NULL, NULL) = 1024 <5.234567>
<5.234567> 就是这个系统调用的耗时。recvfrom花了5.2秒才返回,说明对端处理了5秒才回数据。
bash
# 追踪连接建立失败的原因
strace -f -e trace=connect,socket -p 12345 2>&1 | grep -i "3306"
scss
connect(8, {sa_family=AF_INET, sin_port=htons(3306), sin_addr=inet_addr("10.0.1.60")}, 16) = -1 ECONNREFUSED
ECONNREFUSED------目标端口拒绝连接。非常明确。
bash
# 追踪DNS解析
strace -f -e trace=network -p 12345 2>&1 | grep "53"
能看到进程是否发了DNS查询、查询发给了哪个DNS服务器、有没有收到回复。
strace是最后的手段。 当你用尽了所有网络工具都定位不了问题时,strace能告诉你进程在系统调用层面到底在干什么。
排查流程速查
遇到网络问题时,按这个顺序来:
arduino
第1步:ping 目标IP
├── 不通 → 检查路由 ip route get、检查防火墙
└── 通 → 第2步
第2步:nc -zv 目标IP 端口
├── Connection refused → 服务没启动或端口错了
├── 超时 → 防火墙拦截,用tcpdump确认
└── succeeded → 第3步
第3步:curl -v(HTTP服务)或对应客户端测试
├── 连接建立但无响应 → 抓包看数据交互
├── 响应慢 → curl -w 看耗时分布
└── 响应正常但内容错误 → 应用层问题
第4步:tcpdump抓包
├── SYN重传无回复 → 包被丢弃,查防火墙和路由
├── 有RST → 端口或服务异常
└── 握手正常但数据异常 → 应用层bug
第5步:strace追踪进程
└── 定位具体卡在哪个系统调用
速查对照表
| 场景 | 首选工具 | 备选 |
|---|---|---|
| 目标是否可达 | ping | mtr |
| 端口是否开放 | nc -zv | telnet |
| 路由路径 | mtr | traceroute |
| DNS解析 | dig | nslookup |
| 连接状态 | ss | netstat |
| 抓包分析 | tcpdump | Wireshark |
| HTTP诊断 | curl -w | wget |
| 路由表 | ip route | route |
| 网卡状态 | ethtool | ip link |
| 进程网络行为 | strace | ltrace |
这篇偏实操,建议收藏,遇到问题时直接翻出来对照用。下一篇写 一次跨网延迟飙到300ms的排查全过程,把前面几篇的知识串起来做一次完整的故障复盘。