文章

网络排错全景

网络排错全景

[!abstract] 定位 网络是最容易「谁都觉得自己没问题」的盲区。本手册覆盖 TCP 状态机排查、DNS 排查、抓包分析、以及从用户到服务的全链路排查方法论。


一、TCP 状态机 —— 面试最常考

stateDiagram-v2
    [*] --> CLOSED
    CLOSED --> SYN_SENT: 主动连接
    SYN_SENT --> ESTABLISHED: 收到 SYN+ACK
    CLOSED --> SYN_RECV: 被动连接
    SYN_RECV --> ESTABLISHED: 收到 ACK
    ESTABLISHED --> FIN_WAIT1: 主动关闭
    FIN_WAIT1 --> FIN_WAIT2: 收到 ACK
    FIN_WAIT2 --> TIME_WAIT: 收到 FIN
    TIME_WAIT --> CLOSED: 等待 2MSL
    ESTABLISHED --> CLOSE_WAIT: 收到 FIN(被动方)
    CLOSE_WAIT --> LAST_ACK: 应用调用 close()
    LAST_ACK --> CLOSED: 收到 ACK

各状态的面试考点

状态大量堆积说明什么排查命令
SYN_SENT连接发不出去 → 目标不可达/防火墙阻断telnet IP PORT 测试
SYN_RECVSYN Flood 攻击,或半连接队列满ss -ssynrecvsysctl net.ipv4.tcp_max_syn_backlog
TIME_WAIT大量短连接 → 用长连接 / 调内核参数ss -ant state time-wait | wc -l
CLOSE_WAIT应用层 bug! 收到 FIN 但应用没调 close() → 连接泄漏lsof -p PID 找泄漏源
ESTABLISHED正常,但数量超过 ulimit 会无法新建ss -s 看 TCP 总数

[!warning] CLOSE_WAIT 是应用层问题 面试被问到可以说:「CLOSE_WAIT 堆积一定是应用代码的问题——HTTP Client 或连接池没有正确关闭连接。这跟内核参数无关。」


二、抓包分析 —— tcpdump 速查

常用过滤

# 基础语法:tcpdump -i <网卡> <过滤条件> -w <文件>

# 抓指定端口
tcpdump -i eth0 port 8080

# 抓指定主机
tcpdump -i eth0 host 10.0.0.5

# 抓 HTTP 请求(只看 GET/POST)
tcpdump -i eth0 -A -s 0 'tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)'

# 抓 TCP SYN 包
tcpdump -i eth0 'tcp[tcpflags] & tcp-syn != 0'

# 抓 TCP RST 包
tcpdump -i eth0 'tcp[tcpflags] & tcp-rst != 0'

# 存文件(限制大小,自动轮转)
tcpdump -i eth0 -w /tmp/cap.pcap -C 100 -W 5
# -C 100: 每个文件 100MB,-W 5: 最多 5 个文件,循环覆盖

# 在 Wireshark 中打开 .pcap 分析

面试高频:三次握手在 tcpdump 里长什么样

# 三次握手
→ SYN      [S] seq=0
← SYN+ACK  [S.] seq=0 ack=1
→ ACK      [.] ack=1

# TCP 标志位速记:
# S = SYN, F = FIN, R = RST, P = PSH, . = ACK

三、全链路网络排查

[!tip] 面试金句 「从外往里查,逐层排除:DNS → 网络通不通 → 端口有没有监听 → 防火墙拦没拦 → 应用有没有响应。」

graph LR
    A[用户报障] --> B[DNS 解析对吗?]
    B -->|dig/nslookup| C[网络通吗?]
    C -->|ping/mtr/telnet| D[端口监听吗?]
    D -->|ss/netstat| E[防火墙拦了吗?]
    E -->|iptables/安全组| F[应用有响应吗?]
    F -->|curl| G[SSL 证书正常吗?]

逐层命令

# 1. DNS 解析
dig api.example.com                    # 查 A 记录
dig api.example.com @8.8.8.8          # 指定 DNS 服务器
nslookup api.example.com               # Windows 也可以用
# 注意:CNAME 链、TTL 值、返回了几个 IP

# 2. 网络通不通
ping -c 4 10.0.0.1                     # 基础连通性(ICMP,可能被禁)
mtr 10.0.0.1 -r -c 10                  # 看哪一跳丢包(mtr 是 ping + traceroute 的合体)
traceroute 10.0.0.1                    # 路由追踪

# 3. 端口通不通
telnet 10.0.0.1 8080                   # 最原始
nc -zv 10.0.0.1 8080                   # 更快,-z 不发送数据
timeout 3 bash -c 'cat < /dev/null > /dev/tcp/10.0.0.1/8080'  # 纯 bash 无依赖

# 4. 端口有没有监听
ss -tlnp | grep 8080                   # -t tcp, -l listening, -n 不解析, -p 进程名
# 注意:0.0.0.0:8080 监听所有网卡,127.0.0.1:8080 只监听本地

# 5. 防火墙
iptables -L -n -v                      # 看规则
# 临时关防火墙测试(生产别干)
# iptables -P INPUT ACCEPT

# 6. 应用有没有响应
curl -v http://10.0.0.1:8080/health    # -v 看请求详情(DNS 解析、TCP 连接、SSL 握手、HTTP 响应)
curl -w '\n%{time_namelookup}\n%{time_connect}\n%{time_total}\n' -o /dev/null -s http://example.com
# time_namelookup: DNS 耗时
# time_connect: TCP 连接耗时
# time_total: 总耗时

# 7. SSL 证书
openssl s_client -connect example.com:443 -servername example.com | openssl x509 -noout -dates

四、TIME_WAIT 深度解析

[!important] 必考,且必考 NAT 下的陷阱

为什么需要 TIME_WAIT?

两个原因:

  1. 保证最后一个 ACK 能被对端收到。 如果对端没收到 ACK 会重发 FIN,TIME_WAIT 状态能处理这个重传。
  2. 防止旧连接的包窜到新连接。 等待 2MSL 确保这个连接的所有包都在网络中消失。

内核参数精讲

# ✅ 可以开的
net.ipv4.tcp_tw_reuse = 1             # 允许 TIME_WAIT socket 被复用(仅客户端)

# ❌ 绝对不能开的(NAT 环境)
net.ipv4.tcp_tw_recycle = 0           # Linux 4.12 已移除!依赖时间戳,NAT 下必丢包

# ✅ 推荐的
net.ipv4.ip_local_port_range = 10240 65535  # 扩大可用端口范围
net.ipv4.tcp_max_tw_buckets = 262144        # 允许的 TIME_WAIT 总数
net.ipv4.tcp_fin_timeout = 15               # FIN_WAIT2 超时(秒)

# 生效
sysctl -p

[!warning] tcp_tw_recycle 的血泪史 这个参数在 2015 年前后被大量博客推荐作为「性能优化」,但实际上:

  • 它会检查每个包的时间戳是否递增
  • 在 NAT 环境下,不同客户端的时间戳不同步
  • 导致正常 SYN 包被当作「旧连接重传」丢弃
  • 表现就是:随机用户连不上,查了半天一切正常,最终发现是 NAT + tcp_tw_recycle 的锅

面试提到这个 = 你有实战经验。

根治:用长连接

# Nginx upstream keepalive(见 [[Nginx 排错调优速查]])
upstream backend {
    server 10.0.0.1:8080;
    keepalive 32;
}
# Python requests 使用 Session(连接复用)
session = requests.Session()
session.get('http://api.example.com/data')
session.get('http://api.example.com/other')  # 复用 TCP 连接

五、常见网络故障速查

场景 1:间歇性超时

# 可能原因:DNS 慢、丢包、负载不均
mtr <IP> -r -c 100                   # 看丢包率
tcpdump -i eth0 host <IP> -w cap.pcap # 抓包看重传
# 在 Wireshark 中:Statistics → TCP Stream Graphs → Time-Sequence

场景 2:部分用户连不上

# 可能原因:DNS 污染、CDN 节点异常、NAT 下的 tcp_tw_recycle
dig api.example.com @114.114.114.114  # 换 DNS 对比
curl -v --resolve api.example.com:443:<IP> https://api.example.com  # 绕过 DNS 直接测

场景 3:连接数打满

ss -s                                # TCP 总体统计
ss -ant | awk '{print $6}' | sort | uniq -c | sort -rn  # 各状态数量
netstat -ant | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn | head  # 哪个 IP 连接最多

六、面试话术模板

[!quote] 被问「网络故障怎么排查」

「我从外往里查,七层逐层排除。」

「第一层 DNS:dig 确认域名解析是否正确,TTL 是否合理,有没有 CNAME 串了好几层。」

「第二层网络连通性:pingmtr 看是否可达,traceroute 看走哪条路由。」

「第三层端口:telnetnc 测端口通不通。如果端口不通,上服务器 ss -tlnp 确认是否在监听,注意 0.0.0.0127.0.0.1 的区别。」

「第四层防火墙:iptables -L -n 和服务器的安全组/云防火墙。经常是安全组改了没通知就出问题。」

「第五层应用:curl -v 发请求看完整的握手和响应过程,包括 SSL 证书有效期。」

「如果有间歇性问题,上 tcpdump 抓包,看 TCP 重传、RST 包、握手失败的具体原因。抓完在 Wireshark 里分析是最快的。」

「TIME_WAIT 堆积的话,优先看能不能改长连接,而不是无脑调内核参数——尤其是 tcp_tw_recycle 在 NAT 环境下会导致丢包,生产绝对不能开。」

#sre #网络 #tcp #面试