网络排查与编程常用命令速查,涵盖 ping/traceroute/dig/curl/tcpdump/Wireshark/netstat/ss/iptables,以及常见网络问题(超时/丢包/DNS 慢)的排查思路。
网络系列:TCP-IP 协议栈 · HTTP 协议 · HTTPS 与 TLS · 相关:Shell 常用命令速查
⚠️ 约定:
<>表示必填占位符,[]表示可选项。命令示例均在 Linux/macOS 下验证。
目录
| 章节 | 说明 |
|---|---|
| 连通性探测 | ping、traceroute/tracert |
| DNS 解析 | dig、nslookup、host |
| HTTP 调试 | curl、wget |
| 抓包分析 | tcpdump、Wireshark |
| 连接状态查看 | netstat、ss |
| 防火墙基础 | iptables |
| 常见网络问题排查思路 | 超时/丢包/DNS 慢 |
连通性探测
ping
测试目标主机是否可达,以及往返时延(RTT)。
# 基础用法
ping <host>
# 指定发包次数
ping -c 4 8.8.8.8
# 指定包大小(字节)
ping -s 1400 <host>
# 指定发包间隔(秒)
ping -i 0.2 <host>
# 不解析域名(只显示 IP)
ping -n <host>
输出解读:
PING 8.8.8.8: 56 data bytes
64 bytes from 8.8.8.8: icmp_seq=1 ttl=117 time=4.32 ms
...
--- 8.8.8.8 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3004ms
rtt min/avg/max/mdev = 4.1/4.3/4.6/0.2 ms
| 字段 | 含义 |
|---|---|
| ttl | 剩余跳数,可估算网络路径长度 |
| time | 单次 RTT |
| packet loss | 丢包率,>1% 需关注 |
| mdev | RTT 抖动,越小越稳定 |
traceroute / tracert
追踪数据包经过的每一跳路由,定位网络瓶颈。
# Linux/macOS
traceroute <host>
traceroute -n <host> # 不解析域名,更快
traceroute -T <host> # 使用 TCP(穿透防火墙)
traceroute -p 443 <host> # 指定端口
# Windows
tracert <host>
tracert -d <host> # 不解析域名
输出解读:
1 192.168.1.1 1.234 ms 1.123 ms 1.098 ms ← 网关
2 10.0.0.1 5.432 ms 5.211 ms 5.198 ms
3 * * * ← 该跳不响应 ICMP(不一定是问题)
4 8.8.8.8 15.234 ms 14.987 ms 15.001 ms
* * * 表示该跳的路由器不回应,但后续跳如果正常,说明网络通畅。
DNS 解析
dig
最强大的 DNS 查询工具。
# 查询 A 记录(默认)
dig example.com
# 查询指定记录类型
dig example.com A
dig example.com AAAA # IPv6
dig example.com MX # 邮件服务器
dig example.com CNAME
dig example.com TXT
dig example.com NS # 名称服务器
# 指定 DNS 服务器
dig @8.8.8.8 example.com
# 精简输出(只看结果)
dig +short example.com
# 追踪完整解析过程
dig +trace example.com
# 反向解析(IP → 域名)
dig -x 8.8.8.8
输出关键字段:
;; ANSWER SECTION:
example.com. 300 IN A 93.184.216.34
↑TTL ↑类型 ↑IP
nslookup
交互式 DNS 查询(Windows/Linux 均可用)。
nslookup example.com
nslookup example.com 8.8.8.8 # 指定 DNS 服务器
nslookup -type=MX example.com # 查询 MX 记录
host
简洁的 DNS 查询工具。
host example.com
host -t MX example.com
host 8.8.8.8 # 反向解析
HTTP 调试
curl
功能最全面的 HTTP 命令行工具。
# GET 请求
curl https://example.com
# 显示响应头
curl -I https://example.com # 只看头部(HEAD 请求)
curl -i https://example.com # 头部 + body
# POST 请求
curl -X POST -d 'key=value' https://example.com/api
curl -X POST -H 'Content-Type: application/json' \
-d '{"key":"value"}' https://example.com/api
# 自定义请求头
curl -H 'Authorization: Bearer <token>' https://example.com/api
# 跟随重定向
curl -L https://example.com
# 保存响应到文件
curl -o output.html https://example.com
curl -O https://example.com/file.zip # 使用远程文件名
# 显示详细连接信息(调试 TLS、重定向等)
curl -v https://example.com
# 显示计时信息
curl -w "\nTime: %{time_total}s\n" -o /dev/null -s https://example.com
# 忽略 TLS 证书验证(仅调试用)
curl -k https://self-signed.example.com
# 设置超时
curl --connect-timeout 5 --max-time 30 https://example.com
# 通过代理
curl -x http://proxy:8080 https://example.com
curl 计时模板(排查慢请求必备):
curl -w "
DNS: %{time_namelookup}s
Connect: %{time_connect}s
TLS Handshake:%{time_appconnect}s
TTFB: %{time_starttransfer}s
Total: %{time_total}s
" -o /dev/null -s https://example.com
wget
适合下载文件。
wget https://example.com/file.zip
wget -O output.zip https://example.com/file.zip
wget -c https://example.com/file.zip # 断点续传
wget -r -np https://example.com/docs/ # 递归下载目录
抓包分析
tcpdump
命令行抓包,适合服务器环境。
# 基础用法:抓指定网卡的包
tcpdump -i eth0
# 抓取指定主机的包
tcpdump -i any host 192.168.1.1
# 抓取指定端口的包
tcpdump -i any port 80
tcpdump -i any port 443
# 抓取 HTTP 流量(不加密)
tcpdump -i any -A port 80
# 组合过滤
tcpdump -i any host 192.168.1.1 and port 443
# 保存到文件(供 Wireshark 分析)
tcpdump -i eth0 -w capture.pcap
# 从文件读取
tcpdump -r capture.pcap
# 显示 IP 和端口(不解析域名,更快)
tcpdump -n -i any port 80
# 常用参数
# -n 不解析 IP/端口为域名/服务名
# -A 以 ASCII 显示包内容
# -X 以十六进制+ASCII 显示
# -v/-vv 更详细的输出
# -c <N> 只抓 N 个包
# -s <N> 每包抓取字节数(0 = 全部)
常用过滤表达式:
# TCP SYN 包(新连接)
tcpdump 'tcp[tcpflags] & tcp-syn != 0'
# TCP RST 包(异常断开)
tcpdump 'tcp[tcpflags] & tcp-rst != 0'
# 排除 SSH 流量(避免干扰)
tcpdump -i eth0 not port 22
# 抓取 DNS 查询
tcpdump -i any port 53
Wireshark 分析要点
Wireshark 是图形化抓包工具,适合详细分析。
常用显示过滤器:
# HTTP 流量
http
# 指定 IP
ip.addr == 192.168.1.1
ip.src == 192.168.1.1
ip.dst == 192.168.1.1
# 指定端口
tcp.port == 443
tcp.port == 80
# TCP 握手
tcp.flags.syn == 1
# TCP 重传
tcp.analysis.retransmission
# HTTP 状态码
http.response.code == 404
http.response.code >= 500
# TLS 握手
tls.handshake.type == 1 # ClientHello
tls.handshake.type == 2 # ServerHello
分析 TCP 问题的常用视图:
Statistics → TCP Stream Graphs → Time-Sequence— 查看序号变化Statistics → TCP Stream Graphs → Throughput— 查看吞吐量Analyze → Expert Information— 自动标记问题(重传/乱序等)
连接状态查看
netstat(传统工具)
# 查看所有监听端口
netstat -tlnp # TCP 监听,显示进程
netstat -ulnp # UDP 监听
# 查看所有连接
netstat -anp
# 统计各状态连接数
netstat -ant | awk '{print $6}' | sort | uniq -c | sort -rn
# 查看某个端口的连接
netstat -anp | grep :8080
ss(现代替代工具,更快)
# 查看所有 TCP 连接
ss -t
# 查看监听端口
ss -tlnp # TCP 监听
ss -ulnp # UDP 监听
# 查看所有连接(含状态)
ss -antp
# 过滤指定状态
ss -t state ESTABLISHED
ss -t state TIME-WAIT
ss -t state CLOSE-WAIT
# 过滤指定端口
ss -t sport = :8080
ss -t dport = :443
# 统计 TIME_WAIT 数量
ss -t state TIME-WAIT | wc -l
TCP 连接状态速查:
| 状态 | 说明 | 常见问题 |
|---|---|---|
| ESTABLISHED | 连接正常 | — |
| TIME_WAIT | 主动关闭方等待 2MSL | 大量 TIME_WAIT 说明短连接过多 |
| CLOSE_WAIT | 被动关闭方收到 FIN 后 | 大量 CLOSE_WAIT 说明应用未及时关闭连接 |
| SYN_SENT | 客户端发出 SYN 等待响应 | 连接超时,目标不可达 |
| SYN_RCVD | 服务端收到 SYN | 大量可能是 SYN Flood 攻击 |
防火墙基础
iptables
Linux 内核防火墙,规则链:INPUT(入)、OUTPUT(出)、FORWARD(转发)。
# 查看规则
iptables -L -n -v
iptables -L INPUT -n --line-numbers
# 允许端口
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
# 拒绝端口
iptables -A INPUT -p tcp --dport 8080 -j DROP
# 允许特定 IP
iptables -A INPUT -s 192.168.1.0/24 -j ACCEPT
# 删除规则(按行号)
iptables -D INPUT 3
# 清空所有规则
iptables -F
# 保存规则(CentOS/RHEL)
service iptables save
# Ubuntu
iptables-save > /etc/iptables/rules.v4
常见网络问题排查思路
连接超时
排查步骤:
1. ping <目标IP>
→ 丢包/超时:网络层不通,检查路由、防火墙
→ 正常:继续下一步
2. telnet <目标IP> <端口> 或 nc -zv <目标IP> <端口>
→ 连接拒绝(Connection refused):端口未监听,检查服务是否启动
→ 连接超时:端口被防火墙拦截
3. traceroute <目标IP>
→ 找到在哪一跳开始丢包/超时
4. 检查服务端 netstat/ss
→ 确认端口是否在监听
→ 是否有大量 SYN_RCVD(可能是 SYN Flood)
丢包
排查步骤:
1. ping -c 100 <目标IP>
→ 统计丢包率和 RTT 抖动(mdev)
2. mtr <目标IP>(实时版 traceroute+ping)
→ 找到丢包发生在哪一跳
3. tcpdump 抓包
→ 看 TCP 重传次数(tcp.analysis.retransmission)
4. 检查网卡错误
→ ifconfig eth0 或 ip -s link show eth0
→ 看 errors/dropped/overruns 字段
DNS 解析慢
排查步骤:
1. 测量 DNS 解析时间
→ time dig example.com
→ curl 的 time_namelookup 字段
2. 对比不同 DNS 服务器
→ dig @8.8.8.8 example.com
→ dig @1.1.1.1 example.com
→ dig @<本地DNS> example.com
3. 检查 /etc/resolv.conf
→ nameserver 配置是否合理
→ 是否有多个 nameserver(按顺序尝试,第一个超时才用第二个)
4. 检查 /etc/hosts
→ 是否有错误的本地解析覆盖
5. 使用 nscd 或 systemd-resolved 本地 DNS 缓存
快速参考卡
| 场景 | 命令 |
|---|---|
| 检查端口是否开放 | nc -zv <host> <port> |
| 查看当前监听端口 | ss -tlnp |
| 查看 ESTABLISHED 连接数 | ss -t state ESTABLISHED | wc -l |
| 抓 443 端口流量 | tcpdump -i any port 443 -w /tmp/cap.pcap |
| 查 DNS 解析结果 | dig +short example.com |
| 测试 HTTP 接口 | curl -v -X POST -H 'Content-Type: application/json' -d '{}' https://api/endpoint |
| 查看网卡流量 | iftop -i eth0 或 nload eth0 |
| 查看路由表 | ip route show 或 route -n |
| 测试 TLS 证书 | openssl s_client -connect example.com:443 |
| 检查端口占用 | lsof -i :8080 |
参考资料
- 《趣谈网络协议》— 刘超,极客时间,第 7、18、19 讲
man tcpdump、man ss、man iptables- Wireshark 官方文档
Socket 编程实战
本节内容来自极客时间《网络编程实战》(盛延敏)和《网络排查案例课》(胜辉)。
TCP Socket 完整编程流程
TCP 连接本质是四元组 (clientIP:clientPort, serverIP:serverPort) 唯一确定的一条双向通道。
服务端流程:
// 1. 创建监听 socket
int listenfd = socket(AF_INET, SOCK_STREAM, 0);
// 2. 绑定地址和端口
struct sockaddr_in servaddr;
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(12345);
bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
// 3. 开始监听(backlog 为半连接队列 + 全连接队列上限)
listen(listenfd, 1024);
// 4. 接受连接(阻塞直到有客户端连入)
int connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen);
// 5. 读写数据
read(connfd, buf, sizeof(buf));
write(connfd, response, len);
// 6. 关闭连接(触发 FIN,进入四次挥手)
close(connfd);
客户端流程:
// 1. 创建 socket
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
// 2. 发起连接(触发三次握手)
struct sockaddr_in servaddr;
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(12345);
inet_pton(AF_INET, "192.168.1.1", &servaddr.sin_addr);
connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
// 3. 发送数据
send(sockfd, data, len, 0);
// 4. 接收数据
recv(sockfd, buf, sizeof(buf), 0);
// 5. 关闭
close(sockfd);
关键原则:
| 函数 | 阻塞行为 | 注意事项 |
|---|---|---|
write/send |
等待数据全部拷贝到发送缓冲区后返回 | 返回成功 ≠ 对端已收到 |
read/recv |
接收缓冲区有数据就立即返回,不等满 | 返回 0 表示对端发送了 FIN |
accept |
阻塞直到有完成三次握手的连接 | 建议设为非阻塞,避免 RST 后卡死 |
connect |
阻塞直到三次握手完成或超时 | 非阻塞时立即返回 EINPROGRESS |
非阻塞 Socket 与 epoll 事件驱动
阻塞 vs 非阻塞:
| 模式 | read 无数据时 | write 缓冲区满时 |
|---|---|---|
| 阻塞 | 挂起等待 | 挂起等待 |
| 非阻塞 | 立即返回 EAGAIN/EWOULDBLOCK | 写入尽可能多,返回实际写入字节数 |
设置非阻塞:
int flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
epoll 三步走:
// 1. 创建 epoll 实例
int efd = epoll_create1(0);
// 2. 注册感兴趣的事件(边缘触发 ET)
struct epoll_event ev;
ev.events = EPOLLIN | EPOLLET; // 可读 + 边缘触发
ev.data.fd = connfd;
epoll_ctl(efd, EPOLL_CTL_ADD, connfd, &ev);
// 3. 等待事件(-1 表示永不超时)
struct epoll_event events[128];
int n = epoll_wait(efd, events, 128, -1);
for (int i = 0; i < n; i++) {
if (events[i].data.fd == listenfd) {
// 新连接到来
int fd = accept(listenfd, ...);
make_nonblocking(fd);
ev.data.fd = fd;
epoll_ctl(efd, EPOLL_CTL_ADD, fd, &ev);
} else {
// 已连接 socket 可读
while ((n = read(events[i].data.fd, buf, sizeof(buf))) > 0) { ... }
if (errno == EAGAIN) { /* 数据读完,继续等待 */ }
}
}
ET vs LT 对比:
| 触发模式 | 含义 | 要求 |
|---|---|---|
| LT(条件触发,默认) | 只要缓冲区有数据就持续通知 | 每次处理部分数据即可 |
| ET(边缘触发) | 仅在状态变化时通知一次 | 必须一次性读完所有数据,否则不再通知 |
ET 模式效率更高,但必须搭配非阻塞 I/O,否则读完后 read 会阻塞。
select / poll / epoll 对比:
| 特性 | select | poll | epoll |
|---|---|---|---|
| fd 数量限制 | 1024(FD_SETSIZE) | 无限制 | 无限制 |
| 时间复杂度 | O(n) 遍历所有 fd | O(n) | O(就绪事件数) |
| 内核态/用户态拷贝 | 每次调用都拷贝 | 每次调用都拷贝 | 只注册时拷贝一次 |
| 适用场景 | 小并发、跨平台 | 小并发 | 高并发(C10K+) |
粘包/拆包问题与解决方案
TCP 是字节流协议,没有天然的消息边界。发送 "hello" 和 "world" 两次,接收端可能一次收到 "helloworld"(粘包),也可能分两次收到 "hel" 和 "loworld"(拆包)。
三种解决方案:
| 方案 | 原理 | 示例 |
|---|---|---|
| 定长消息 | 每条消息固定 N 字节 | 简单但浪费带宽 |
| 分隔符 | 用特殊字符(如 \r\n)标记消息结束 |
HTTP header 用 \r\n\r\n |
| 长度前缀(推荐) | 消息头部包含消息体长度字段 | [4字节长度][消息体] |
长度前缀实现(发送端):
struct Message {
uint32_t length; // 消息体字节数(网络字节序)
uint32_t type; // 消息类型
char body[0]; // 消息体(变长)
};
// 发送时:
message.length = htonl(body_len); // 主机序 → 网络序
send(sockfd, &message, sizeof(uint32_t)*2 + body_len, 0);
长度前缀实现(接收端):
// 先读 4 字节长度
uint32_t msg_len;
readn(fd, &msg_len, 4);
msg_len = ntohl(msg_len); // 网络序 → 主机序
// 再读消息体
char buf[msg_len];
readn(fd, buf, msg_len); // readn 循环读直到读满
字节序转换函数速查:
htons(x) // host → network,16位(端口号用这个)
htonl(x) // host → network,32位(IP地址/长度字段用这个)
ntohs(x) // network → host,16位
ntohl(x) // network → host,32位
网络排查案例:TIME_WAIT 过多
现象:ss -t state TIME-WAIT | wc -l 数量很大(数千甚至数万)。
原因:TIME_WAIT 是主动关闭方在发出最后一个 ACK 后等待 2MSL(约 60 秒)的状态,目的是确保对端收到 ACK。大量 TIME_WAIT 说明服务端在主动关闭大量短连接(如 HTTP 短连接场景)。
排查与处理:
# 查看 TIME_WAIT 数量
ss -t state TIME-WAIT | wc -l
# 查看哪些端口的 TIME_WAIT 最多
ss -t state TIME-WAIT | awk '{print $4}' | cut -d: -f2 | sort | uniq -c | sort -rn | head
# 内核参数调优(谨慎修改)
# 开启 TIME_WAIT 快速回收(仅在 NAT 环境外使用)
sysctl net.ipv4.tcp_tw_reuse=1 # 允许复用 TIME_WAIT 连接(客户端侧)
sysctl net.ipv4.tcp_fin_timeout=30 # 缩短 FIN_WAIT2 超时
根本解法:使用长连接(HTTP Keep-Alive、连接池),减少频繁建立/断开连接。
网络排查案例:连接超时
排查步骤:
1. ping <目标IP>
→ 丢包/超时:网络层不通,检查路由、防火墙
→ 正常:继续下一步
2. nc -zv <目标IP> <端口>
→ Connection refused:端口未监听,检查服务是否启动
→ 超时无响应:端口被防火墙拦截
3. traceroute -T -p <端口> <目标IP>
→ 找到在哪一跳开始丢包(TCP traceroute 可穿透部分防火墙)
4. 两侧同时抓包对比(关键!)
→ tcpdump -i any host <对端IP> -w /tmp/cap.pcap
→ 用 TCP 裸序列号定位两端的同一个 TCP 流
→ 对比报文到达时序,判断丢包位置
网络排查案例:DNS 解析慢
排查步骤:
1. 测量 DNS 解析耗时
time dig example.com
curl -w "DNS: %{time_namelookup}s\n" -o /dev/null -s https://example.com
2. 对比不同 DNS 服务器响应时间
dig @8.8.8.8 example.com
dig @1.1.1.1 example.com
dig @<本机DNS> example.com
3. 检查 /etc/resolv.conf
→ nameserver 配置的 DNS 服务器是否可达
→ options timeout:2 attempts:2 可缩短超时
4. 检查 ndots 配置(容器环境常见问题)
→ ndots:5(K8s 默认)会导致短域名先尝试 5 次带集群后缀的查询
→ 对外部域名加 "." 结尾可跳过搜索域:dig example.com.
网络排查案例:TCP 重传与 Nginx 499
TCP 重传类型速查:
| 类型 | 触发条件 | 特征 |
|---|---|---|
| 超时重传 | RTO 内未收到 ACK | 时间间隔呈指数增长(200ms→400ms→800ms...) |
| 快速重传 | 收到 3 个重复 ACK(DupAck) | 无需等待 RTO,立即重传 |
| Spurious 重传 | 已被确认的包再次发送 | 影响较小,一般忽略 |
Nginx 499 根因:客户端在服务端返回响应前主动关闭了连接(发送 FIN)。常见原因:
- 客户端设置了较短的超时(如 5 秒)
- 某个 TCP 包在网络中丢失,导致请求无法在超时内完成
- 排查方法:在服务端抓包,通过客户端 IP + 时间戳 + URL 定位对应 TCP 流,分析 FIN 出现时机
参考资料
- 《网络编程实战》— 盛延敏,极客时间
- 《网络排查案例课》— 胜辉,极客时间
评论 (0)
发表评论