Skip to content

网络工具与分析方法 ​

#网络 · #tcpdump · #Wireshark · #netstat · #排查 · #工具

网络问题排查不是靠猜,而是靠工具链的熟练运用。本文覆盖从抓包、连接诊断、性能测量到系统调用的完整工具矩阵,以及实战排查方法论。


1. tcpdump — 抓包分析 ​

1.1 基础语法 ​

bash
tcpdump [选项] [过滤表达式]

# 最常用选项
tcpdump -i eth0          # 指定网卡
tcpdump -nn              # 不解析主机名和端口名(速度快)
tcpdump -v/-vv/-vvv      # 详细程度
tcpdump -w file.pcap     # 写入文件
tcpdump -r file.pcap     # 读取文件
tcpdump -c 100           # 抓 100 个包后停止
tcpdump -s 0             # 抓完整包(默认只抓 262144 字节)
tcpdump -A               # ASCII 显示包内容
tcpdump -X               # HEX + ASCII 显示
tcpdump -tttt            # 显示完整时间戳(微秒)

1.2 BPF 过滤器(Berkeley Packet Filter) ​

bash
# === 主机过滤 ===
tcpdump host 10.0.0.1
tcpdump src host 10.0.0.1
tcpdump dst host 10.0.0.1

# === 端口过滤 ===
tcpdump port 80
tcpdump portrange 8000-9000
tcpdump src port 443

# === 协议过滤 ===
tcpdump tcp
tcpdump udp
tcpdump icmp
tcpdump 'ip proto 6'      # 同 tcpdump tcp

# === 组合逻辑 ===
tcpdump 'host 10.0.0.1 and (port 80 or port 443)'
tcpdump 'src 10.0.0.0/24 and not port 22'

# === TCP 标志位过滤 ===
tcpdump 'tcp[tcpflags] & (tcp-syn) != 0'          # 所有 SYN 包
tcpdump 'tcp[tcpflags] == tcp-syn'                # 仅 SYN (不含 ACK)
tcpdump 'tcp[tcpflags] & (tcp-rst) != 0'          # RST 包
tcpdump 'tcp[tcpflags] & (tcp-syn|tcp-fin) != 0'  # SYN 或 FIN

1.3 输出格式逐字段解读 ​

14:23:01.123456 IP 192.168.1.5.54321 > 10.0.0.1.80: Flags [S], seq 123456789, win 65535, options [mss 1460,sackOK,TS val 123456 ecr 0,nop,wscale 7], length 0

解读:
  14:23:01.123456  → 时间戳(微秒)
  IP               → 网络层协议
  192.168.1.5.54321 → 源IP.源端口
  >                → 方向
  10.0.0.1.80      → 目的IP.目的端口
  Flags [S]        → TCP 标志位: S=SYN, .=ACK, F=FIN, R=RST, P=PSH
  seq 123456789    → 序列号
  win 65535        → 接收窗口大小
  options [...]    → TCP 选项
    mss 1460        → 最大分段大小
    sackOK          → 支持选择性确认
    TS val 123456   → 时间戳值
    wscale 7        → 窗口缩放因子 (×128)
  length 0         → TCP 数据长度(SYN 包无数据)

1.4 实战场景 ​

bash
# 场景1: 抓取 HTTP 请求和响应
tcpdump -i eth0 -A -s 0 'tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)'

# 场景2: 监控 DNS 查询
tcpdump -i eth0 -nn udp port 53

# 场景3: 抓取完整 TCP 连接(三次握手到四次挥手)
tcpdump -i eth0 -w conn.pcap 'host 10.0.0.1 and port 8080'
# 用 Wireshark 打开 conn.pcap

# 场景4: 按包大小过滤(大包通常有数据)
tcpdump -i eth0 'greater 100'

# 场景5: 限制每个包捕获的字节数(保护隐私,只抓头部)
tcpdump -i eth0 -s 96    # 只抓前 96 字节(足够 TCP/IP 头)

2. Wireshark — 图形化分析 ​

2.1 常用显示过滤器 ​

# 协议过滤
tcp / udp / http / dns / tls / icmp / arp

# IP 过滤
ip.addr == 10.0.0.1
ip.src == 192.168.1.0/24
!(ip.dst == 10.0.0.1)

# 端口过滤
tcp.port == 443
tcp.srcport == 8080
tcp.port in {80 443 8080}

# TCP 分析标志
tcp.flags.syn == 1 && tcp.flags.ack == 0    # 纯 SYN
tcp.flags.reset == 1                         # RST
tcp.analysis.retransmission                  # 重传
tcp.analysis.duplicate_ack                   # 重复 ACK
tcp.analysis.zero_window                     # 零窗口
tcp.analysis.fast_retransmission             # 快速重传

# HTTP
http.request.method == "POST"
http.response.code >= 400
http.host contains "example"

# TLS
tls.handshake.type == 1    # ClientHello
tls.handshake.type == 2    # ServerHello
tls.handshake.extensions_server_name  # SNI

2.2 关键功能 ​

功能路径用途
Follow TCP Stream右键 → Follow → TCP Stream看完整 TCP 对话(HTTP 请求/响应)
Follow TLS Stream右键 → Follow → TLS Stream解密后的 TLS 数据(需密钥)
Statistics → IO Graph菜单流量趋势图
Statistics → Flow Graph菜单TCP 流可视化
Statistics → Protocol Hierarchy菜单协议占比统计
Expert Info底部状态栏自动检测异常(重传/乱序/零窗口)
Time Column右键 → Time Display Format显示距上一个包的时间差

2.3 解密 TLS 流量 ​

bash
# 方法1: 设置环境变量 (Chrome/Firefox)
export SSLKEYLOGFILE=/tmp/sslkeys.log
# Wireshark → Preferences → TLS → (Pre)-Master-Secret log filename

# 方法2: 使用 RSA 私钥 (仅 RSA 密钥交换, TLS 1.2 以下)
# Wireshark → Preferences → RSA Keys → 加载 .key 文件

# 方法3: 抓取前配置代理 (mitmproxy)
mitmproxy --mode transparent

3. ss / netstat — 连接状态诊断 ​

3.1 ss 命令(替代 netstat,更快) ​

bash
# 查看所有 TCP 连接
ss -tanp

# 按状态过滤
ss -tan state established    # 已建立连接
ss -tan state time-wait      # TIME_WAIT 连接数
ss -tan state close-wait     # CLOSE_WAIT 排查
ss -tan state listen         # 监听端口
ss -tan state syn-recv       # 半连接队列(可能被 SYN Flood)
ss -tan state syn-sent       # 正在建立连接

# 统计各状态数量
ss -tan | awk '{print $1}' | sort | uniq -c | sort -rn

# 查看进程打开的文件描述符(连接)
ss -tanp | grep <pid>

# 查看 listen 队列溢出
ss -lnt | grep -E 'Recv-Q|LISTEN'
# Recv-Q > 0 → backlog 不够,新连接排队中!

# 按目标 IP 分组(找热点)
ss -tan | awk '{print $5}' | awk -F: '{print $1}' | sort | uniq -c | sort -rn

3.2 连接状态的含义解读 ​

mermaid
flowchart LR
    subgraph Normal["正常状态"]
        EST["ESTABLISHED ✅"]
        LISTEN["LISTEN ✅"]
    end

    subgraph Warning["需关注"]
        TW["TIME_WAIT ⚠️ 过多→短连接"]
        SR["SYN_RECV ⚠️ 过多→SYN Flood"]
    end

    subgraph Danger["危险信号"]
        CW["CLOSE_WAIT ❌ 100%应用bug"]
        LA["LAST_ACK ❌ 对端未响应"]
        FS["FIN_WAIT2 ❌ 对端不发FIN"]
    end

3.3 netstat 补充用法 ​

bash
# 统计各状态 (netstat 兼容写法)
netstat -an | awk '/^tcp/ {print $6}' | sort | uniq -c | sort -rn

# 查看网络接口统计(错误、丢包)
netstat -i
netstat -s    # 各协议统计(重传、超时、reset等)

# 查看路由表
netstat -rn

3.4 TCP 状态 + fd 耗尽排查 Runbook ​

当服务出现 too many open files、502/504、连接超时、连接被 reset 时,优先判断是不是连接状态异常或 fd 耗尽。

第一步:看全局状态分布

bash
# TCP 状态计数
ss -tan | awk 'NR>1 {print $1}' | sort | uniq -c | sort -rn

# socket 总览
ss -s

# 全机 socket 使用情况
cat /proc/net/sockstat

第二步:看进程 fd 是否逼近上限

bash
pid=<pid>

# 当前打开 fd 数
ls /proc/$pid/fd | wc -l

# 进程 fd 上限
cat /proc/$pid/limits | grep 'open files'

# fd 类型分布
ls -l /proc/$pid/fd | awk '{print $NF}' | sed 's/\[.*\]/[socket]/' | sort | uniq -c | sort -rn | head

第三步:定位异常连接属于哪条链路

bash
# CLOSE_WAIT 连接及进程
ss -tanp state close-wait

# 按本地地址/对端地址聚合
ss -tanp state close-wait | awk 'NR>1 {print $4, $5}' | sort | uniq -c | sort -rn | head -20

# TIME_WAIT 按对端聚合,判断是否连接固定 upstream 太频繁
ss -tan state time-wait | awk 'NR>1 {print $5}' | sort | uniq -c | sort -rn | head -20

第四步:结合链路判断根因

状态出现位置高概率根因下一步
大量 CLOSE_WAIT服务本地端口client 已关闭,服务未 close查 handler 是否退出、响应体是否关闭
大量 CLOSE_WAIT本地临时端口 → MySQL/Redis/HTTP 后端下游已关闭,代理/客户端未 close查连接池、超时、错误分支
大量 TIME_WAIT本地临时端口 → 固定 upstream短连接过多,主动关闭方是本机开启 keepalive/连接池,扩大端口范围只是止血
大量 FIN_WAIT2本机主动关闭后等待对端 FIN对端应用迟迟不关闭查对端应用、tcp_fin_timeout 只能缓解
大量 SYN_RECVlisten 端口SYN Flood 或 backlog 不够查 ss -lnt、SYN Cookie、防火墙
Recv-Q 持续增长listen 或 established应用 accept/read 不及时查 CPU、线程池、事件循环阻塞

第五步:确认应用是否真的没有 close

bash
# 追踪 close/shutdown/read/write/connect/accept
strace -f -e trace=close,shutdown,read,write,connect,accept -p $pid

# 找 socket inode,再反查 fd
ss -tanp state close-wait
ls -l /proc/$pid/fd | grep socket

# Go 服务额外看 goroutine 是否卡在 net.(*netFD).Read/Write 或数据库驱动
curl http://127.0.0.1:6060/debug/pprof/goroutine?debug=2

第六步:止血与根治

类型动作注意
止血重启进程释放 fd只能恢复服务,不能消除根因
止血临时调大 ulimit -n只是在泄漏时延后崩溃
止血降低入口流量 / 熔断下游防止代理继续堆连接
根治所有错误路径补 close()特别是超时、取消、部分读写失败
根治设置 connect/read/write/request timeout不允许无限等待下游
根治启用连接池和 keepalive降低 TIME_WAIT 和建连开销
根治增加 fd、TCP 状态、连接池等待、下游 P99 监控提前发现趋势

4. curl — HTTP 调试利器 ​

4.1 基础调试 ​

bash
# 详细输出(含 TLS 握手)
curl -v https://example.com

# 只看响应头
curl -I https://example.com

# 输出时间明细
curl -w "\n\
dns_resolve:  %{time_namelookup}s\n\
tcp_connect:  %{time_connect}s\n\
tls_handshake:%{time_appconnect}s\n\
first_byte:   %{time_starttransfer}s\n\
total:        %{time_total}s\n" \
  -o /dev/null -s https://example.com

# 指定 DNS 解析(绕过 DNS)
curl --resolve example.com:443:1.2.3.4 https://example.com

# 指定网卡/IP
curl --interface eth0 https://example.com

4.2 高级用法 ​

bash
# 查看 TLS 握手详情
curl -v --tlsv1.3 https://example.com 2>&1 | grep -E 'TLS|cert|subject|issuer'

# 指定密码套件
curl --ciphers ECDHE-RSA-AES128-GCM-SHA256 https://example.com

# 测试 HTTP/2
curl --http2 -I https://example.com

# POST JSON + Header
curl -X POST https://api.example.com/users \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer token123" \
  -d '{"name":"Alice","age":30}'

# 跟踪重定向
curl -L https://example.com

# 下载并保存
curl -O https://example.com/file.tar.gz

5. nc (netcat) — 网络瑞士军刀 ​

bash
# 端口连通性测试
nc -zv 10.0.0.1 80       # TCP 扫描
nc -zvu 10.0.0.1 53       # UDP 扫描

# 构建简单 TCP 服务端
nc -l 8080

# 构建 TCP 客户端
echo "hello" | nc 10.0.0.1 8080

# 构建 HTTP 请求
printf "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n" | nc example.com 80

# 端口转发
mkfifo /tmp/fifo
nc -l 8080 < /tmp/fifo | nc 10.0.0.1 80 > /tmp/fifo

# 传输文件
# 接收方: nc -l 9999 > received_file
# 发送方: nc 10.0.0.1 9999 < file_to_send

6. 网络性能测试 ​

6.1 iperf3 — 带宽测试 ​

bash
# 服务端
iperf3 -s

# 客户端 (TCP 测试)
iperf3 -c <server_ip> -t 30        # 30 秒测试
iperf3 -c <server_ip> -P 10        # 10 个并行流
iperf3 -c <server_ip> -R           # 反向测试(下载)

# UDP 测试
iperf3 -c <server_ip> -u -b 100M   # 100 Mbps UDP

6.2 wrk / ab — HTTP 压测 ​

bash
# wrk (推荐,更快)
wrk -t4 -c100 -d30s https://example.com
# -t4   : 4 线程
# -c100 : 100 并发连接
# -d30s : 持续 30 秒

# ab (Apache Bench)
ab -n 10000 -c 100 https://example.com/
# -n 10000 : 总请求数
# -c 100   : 并发数

6.3 mtr — 综合路由诊断 ​

bash
# My Traceroute (ping + traceroute 结合)
mtr 8.8.8.8
# 显示每一跳的丢包率、延迟、抖动

7. DNS 诊断 ​

bash
# dig 完整查询过程
dig +trace example.com

# 指定 DNS 服务器
dig @8.8.8.8 example.com

# 查询特定记录类型
dig example.com A
dig example.com AAAA
dig example.com MX
dig example.com TXT

# 反向解析
dig -x 8.8.8.8

# 简短输出
dig +short example.com

# nslookup (兼容 Windows)
nslookup example.com
nslookup example.com 8.8.8.8

8. 系统层面分析 ​

8.1 strace — 追踪网络系统调用 ​

bash
# 追踪所有网络相关系统调用
strace -e trace=network -p <pid>

# 追踪 socket 调用
strace -e trace=socket,connect,bind,listen,accept,close -p <pid>

# 追踪并统计耗时
strace -c -e trace=network -p <pid>   # Ctrl+C 后输出统计

# 追踪连接建立
strace -e trace=connect,accept -p <pid>

8.2 /proc 文件系统 ​

bash
# TCP 连接信息
cat /proc/net/tcp      # IPv4 TCP
cat /proc/net/tcp6     # IPv6 TCP
cat /proc/net/udp      # UDP

# socket 统计
cat /proc/net/sockstat

# 网卡统计(丢包、错误)
cat /proc/net/dev
ip -s link show eth0

8.3 sysctl 内核参数 ​

bash
# TCP 相关内核参数
sysctl -a | grep net.ipv4.tcp

# 常用调优
sysctl net.ipv4.tcp_fin_timeout          # TIME_WAIT 时长
sysctl net.ipv4.tcp_tw_reuse             # TIME_WAIT 重用
sysctl net.ipv4.tcp_max_syn_backlog      # SYN 队列大小
sysctl net.core.somaxconn                # accept 队列大小
sysctl net.ipv4.tcp_slow_start_after_idle # 空闲后重新慢启动
sysctl net.ipv4.tcp_congestion_control    # 当前拥塞控制算法

9. 实战排查方法论 ​

mermaid
flowchart TD
    PROBLEM["网络出问题了"] --> Q1{"什么症状?"}

    Q1 -->|"连不上"| C1["1. ping/telnet 端口"]
    C1 -->|"ping 不通"| C11["网线/交换机/IP/防火墙"]
    C1 -->|"ping 通, port 不通"| C12["服务未启动/防火墙DROP"]

    Q1 -->|"连接慢"| C2["2. curl -w 计时分析"]
    C2 -->|"DNS 慢"| C21["dig 检查 / DNS服务器"]
    C2 -->|"TCP 慢"| C22["traceroute + mtr 检查路由"]
    C2 -->|"TLS 慢"| C23["检查证书链 / OCSP 超时"]

    Q1 -->|"连接断开/重置"| C3["3. ss 查状态"]
    C3 -->|"大量 CLOSE_WAIT"| C31["应用 bug: 未 close()"]
    C3 -->|"大量 TIME_WAIT"| C32["短连接太多, 需复用"]
    C3 -->|"SYN_RECV"| C33["SYN Flood / backlog 小"]

    Q1 -->|"传输慢/卡"| C4["4. tcpdump 抓包 + Wireshark"]
    C4 -->|"大量重传"| C41["丢包/拥塞/带宽不足"]
    C4 -->|"零窗口"| C42["接收方处理不过来"]
    C4 -->|"延迟高"| C43["TCP 延迟 ACK / Nagle"]

    C1 & C2 & C3 & C4 --> FINAL["定位根因 → 修复"]

9.1 常见问题速查表 ​

症状首选工具诊断命令可能原因
连接拒绝telnet / ncnc -zv host port端口未监听/防火墙
连接超时tcpdump抓 SYN 是否有响应防火墙丢包/路由不通
TLS 证书错误openssl s_clientopenssl s_client -connect证书过期/域名不匹配
HTTP 502/504curl -v看响应头和耗时上游服务挂了/超时
内存泄漏(连接)ss -tanpCLOSE_WAIT 堆积代码未 close
端口不够用ss -tanTIME_WAIT 堆积短连接过多
DNS 解析失败digdig +traceDNS 劫持/缓存污染

9.2 client → proxy → mysql 故障定位模板 ​

mermaid
flowchart TD
    A["用户报错: 超时/502/连接失败"] --> B["看 proxy 状态: ss -tanp"]
    B --> C{"异常状态是什么?"}
    C -->|"CLOSE_WAIT 指向 mysql:3306"| D["MySQL 已关连接<br/>Proxy 未 close 下游 fd"]
    C -->|"TIME_WAIT 指向 mysql:3306"| E["Proxy 到 MySQL 短连接过多<br/>连接池/keepalive 不足"]
    C -->|"ESTABLISHED 很多且 Recv-Q/Send-Q 高"| F["读写堵塞<br/>下游慢或上游不读"]
    C -->|"listen Recv-Q 高"| G["accept 不及时<br/>事件循环/线程池被卡住"]

    D --> H["查代码 close 路径、连接池归还、goroutine dump"]
    E --> I["查连接池配置、端口范围、QPS、MySQL max_connections"]
    F --> J["tcpdump/Wireshark 看 zero window、重传、RTT"]
    G --> K["查 CPU、GC、锁、线程池、backlog"]
观测项命令解释
Proxy fd 数`ls /proc/$pid/fdwc -l`
CLOSE_WAIT 对端ss -tanp state close-wait判断是 client 侧还是 mysql 侧
MySQL 连接数show processlist / Threads_connected下游是否也被打满
连接池等待应用指标是否大量请求等连接
Go goroutinepprof/goroutine?debug=2是否卡在 database/sql、netFD.Read/Write
抓包tcpdump -i eth0 -nn host <mysql_ip> and port 3306看 FIN/RST/重传/零窗口

判断口诀:

text
CLOSE_WAIT:对端已经关,我方没关 → 查应用 close。
TIME_WAIT:我方主动关太多 → 查短连接和连接复用。
FIN_WAIT2:我方想关,对方不关 → 查对端应用。
SYN_RECV:连接还没建完 → 查 backlog、SYN Flood、防火墙。
Recv-Q 高:内核有数据,应用不读 → 查事件循环/线程池/GC/锁。
Send-Q 高:应用写了,对端不收 → 查对端处理能力/网络拥塞。

10. 按协议层分工具验证知识点 ​

每一层的知识点都可以用对应的系统工具来"眼见为实"。以下按 OSI 层次从底到顶,给出工具 + 验证命令 + 预期输出解读。

bash
# === ethtool — 网卡物理层诊断 ===

# 查看网卡速率、双工模式、自协商状态
ethtool eth0
# Speed: 25000Mb/s   ← 25Gbps
# Duplex: Full       ← 全双工
# Auto-negotiation: on
# Link detected: yes ← 物理链路正常

# 查看网卡统计(丢包、CRC 错误、碰撞)
ethtool -S eth0 | grep -E 'rx_errors|tx_errors|rx_dropped|tx_dropped|crc'
# rx_errors: 0
# rx_crc_errors: 0    ← CRC 错误 > 0 说明网线/光模块有问题
# rx_dropped: 12      ← 内核来不及处理(ring buffer 满)

# 查看/修改 ring buffer 大小(解决高流量丢包)
ethtool -g eth0          # 查看当前和最大值
ethtool -G eth0 rx 4096  # 调大接收 ring buffer

# 查看网卡 offload 功能(TSO/GRO/GSO)
ethtool -k eth0 | grep -E 'tcp-segmentation|generic-receive|generic-segmentation'
# tcp-segmentation-offload: on  ← TSO 开启,大包由网卡分段

# 关闭 GRO(排查抓包看不到小包的问题)
ethtool -K eth0 gro off

# === arping — ARP 层诊断 ===

# 检测 IP 冲突(同一 IP 是否有两个 MAC 响应)
arping -D -I eth0 192.168.1.100
# 如果收到响应 → IP 冲突!

# 查看 ARP 缓存
ip neigh show
# 192.168.1.1 dev eth0 lladdr aa:bb:cc:dd:ee:ff REACHABLE

# 清除 ARP 缓存(排查 ARP 缓存污染)
ip neigh flush dev eth0

# === bridge — 二层交换/VLAN 诊断 ===

# 查看 Linux bridge 的 MAC 转发表
bridge fdb show
# aa:bb:cc:dd:ee:ff dev veth0 master br0

# 查看 VLAN 过滤
bridge vlan show
验证知识点工具命令预期输出/判断
网卡是否全双工ethtool eth0Duplex: Full
物理链路是否正常ethtool eth0Link detected: yes
是否有 CRC 错误(网线问题)ethtool -S eth0rx_crc_errors > 0
Ring buffer 是否溢出ethtool -S eth0rx_missed_errors > 0
IP 地址冲突arping -D收到响应 = 冲突
ARP 欺骗ip neigh show同一 IP 对应多个 MAC
TSO/GRO 是否影响抓包ethtool -k eth0gro: on 时抓包看到大包

10.2 IP 层(L3)— traceroute / ip route / iptables / nftables ​

bash
# === traceroute — 逐跳路由诊断 ===

# TCP traceroute(穿透防火墙,比 ICMP 更可靠)
traceroute -T -p 80 example.com

# UDP traceroute(默认)
traceroute example.com

# 指定源 IP(多网卡环境)
traceroute -s 10.0.0.5 example.com

# === ip route — 路由表诊断 ===

# 查看完整路由表
ip route show table all

# 查看某个目标走哪条路由
ip route get 8.8.8.8
# 8.8.8.8 via 10.0.0.1 dev eth0 src 10.0.0.5 uid 0

# 查看策略路由规则
ip rule show

# === iptables — 包过滤/NAT 诊断 ===

# 查看所有规则(含计数器)
iptables -L -n -v --line-numbers
iptables -t nat -L -n -v

# 实时监控被 DROP 的包
iptables -I INPUT -j LOG --log-prefix "IPT-DROP: " --log-level 4
dmesg -w | grep "IPT-DROP"

# 查看 conntrack 表(NAT 连接跟踪)
conntrack -L -n | head -20
conntrack -C    # 当前连接跟踪数
cat /proc/sys/net/netfilter/nf_conntrack_max  # 最大值

# conntrack 表满的症状: 新连接被丢弃
# 解决: sysctl -w net.netfilter.nf_conntrack_max=262144

# === nftables — 新一代包过滤 ===
nft list ruleset
nft list table inet filter
nft monitor trace   # 实时追踪包经过的规则链
bash
# 验证: MTU 和分片
# 发送不允许分片的大包,测试 Path MTU
ping -M do -s 1472 example.com
# 如果返回 "Frag needed" → MTU < 1500

# 查看接口 MTU
ip link show eth0 | grep mtu

# 验证: IP 分片重组
tcpdump -i eth0 'ip[6:2] & 0x3fff != 0'  # 抓所有分片包
验证知识点工具命令预期输出/判断
路由是否正确ip route get <dst>看 via/dev/src
哪一跳丢包mtr <dst>某跳 Loss% > 0
防火墙是否拦截iptables -L -n -v看 DROP 规则计数
conntrack 表是否满conntrack -C vs nf_conntrack_max接近上限 = 新连接丢弃
MTU 是否匹配ping -M do -s 1472Frag needed = MTU 不够
PMTUD 是否工作tcpdump icmp看 ICMP type 3 code 4

10.3 TCP 层(L4)— ss / tcpdump / sysctl / /proc/net ​

bash
# === 验证三次握手 ===
# 抓 SYN → SYN+ACK → ACK
tcpdump -i eth0 -nn 'tcp[tcpflags] & (tcp-syn) != 0' -c 10

# === 验证四次挥手 ===
# 抓 FIN → ACK → FIN → ACK
tcpdump -i eth0 -nn 'tcp[tcpflags] & (tcp-fin) != 0'

# === 验证 TIME_WAIT ===
ss -tan state time-wait | wc -l
# 查看 TIME_WAIT 超时时间(Linux 固定 60s,不可调)
cat /proc/sys/net/ipv4/tcp_fin_timeout  # 注意: 这是 FIN_WAIT2 的超时

# === 验证拥塞控制算法 ===
# 查看当前算法
sysctl net.ipv4.tcp_congestion_control
# cubic / bbr / reno

# 切换到 BBR
sysctl -w net.ipv4.tcp_congestion_control=bbr

# 验证 BBR 是否生效(需要 fq 队列调度)
tc qdisc show dev eth0
# qdisc fq 8001: root ... ← BBR 需要 fq

# === 验证滑动窗口 ===
# 实时观察窗口大小变化
ss -ti dst 10.0.0.1
# 输出: cwnd:10 ssthresh:7 rtt:1.5/0.5 ... rcv_space:14600

# === 验证 TCP Keep-Alive ===
sysctl net.ipv4.tcp_keepalive_time      # 空闲多久发探测 (默认 7200s)
sysctl net.ipv4.tcp_keepalive_intvl     # 探测间隔 (默认 75s)
sysctl net.ipv4.tcp_keepalive_probes    # 探测次数 (默认 9)

# === 验证 backlog 队列 ===
ss -lnt | head
# Recv-Q Send-Q  Local Address:Port
#   0      128    0.0.0.0:80        ← Send-Q = backlog 大小
#   5      128    0.0.0.0:80        ← Recv-Q > 0 = 有连接等待 accept

# 查看 SYN 队列溢出统计
netstat -s | grep -i "SYNs to LISTEN"
# 或
nstat -az TcpExtListenOverflows TcpExtListenDrops

# === 验证重传 ===
# 实时统计重传率
nstat -az TcpRetransSegs TcpOutSegs
# 重传率 = TcpRetransSegs / TcpOutSegs

# 查看某连接的重传次数
ss -ti dst 10.0.0.1 | grep retrans
# retrans:0/3 ← 当前/累计重传次数

# === 验证 Nagle 算法 ===
# 抓包看小包是否被合并
tcpdump -i eth0 -nn 'dst port 80 and (ip[2:2] < 100)'
# 如果看到大量 < 100 字节的包 → Nagle 被关闭 (TCP_NODELAY)
验证知识点工具命令预期输出/判断
三次握手是否完成tcpdump 抓 SYN/SYN+ACK/ACK三个包都有 = 正常
TIME_WAIT 数量ss -tan state time-wait | wc -l> 10000 需关注
拥塞窗口大小ss -ti dst <ip>cwnd 值
当前拥塞算法sysctl net.ipv4.tcp_congestion_controlcubic/bbr
backlog 是否溢出nstat TcpExtListenOverflows> 0 = 溢出
重传率nstat TcpRetransSegs持续增长 = 丢包
零窗口tcpdump 看 win 0接收方处理不过来

10.4 UDP 层 — nc / tcpdump / ss ​

bash
# === 验证 UDP 连通性 ===
# 服务端
nc -ul 9999

# 客户端
echo "hello" | nc -u <server> 9999

# === 抓 UDP 包 ===
tcpdump -i eth0 -nn udp port 53
# 看 DNS 查询和响应

# === 查看 UDP 统计 ===
cat /proc/net/snmp | grep Udp
# Udp: InDatagrams NoPorts InErrors OutDatagrams RcvbufErrors SndbufErrors
# InErrors > 0 → 校验和错误或缓冲区溢出

# UDP 缓冲区溢出统计
nstat -az UdpRcvbufErrors UdpSndbufErrors

# === 调整 UDP 缓冲区 ===
sysctl net.core.rmem_max          # 最大接收缓冲区
sysctl net.core.rmem_default      # 默认接收缓冲区
sysctl -w net.core.rmem_max=26214400  # 调到 25MB

# === QUIC (UDP 443) 诊断 ===
# 抓 QUIC 初始握手包
tcpdump -i eth0 -nn 'udp port 443 and udp[0] & 0x80 != 0'
# 0x80 = Long Header (Initial/Handshake 包)

# 用 qlog 分析 QUIC 连接
# Chrome: chrome://flags → Enable QUIC → 设置 QUIC_LOG
# 或用 quiche 的 qlog 输出
验证知识点工具命令预期输出/判断
UDP 是否可达nc -uv <host> <port>无响应不代表不通(无连接)
UDP 丢包nstat UdpRcvbufErrors> 0 = 缓冲区溢出丢包
DNS 查询过程tcpdump udp port 53看 query/response
QUIC 是否走 UDPtcpdump udp port 443有包 = QUIC 生效

10.5 HTTP 层 — curl / httpie / h2c / grpcurl / wscat ​

bash
# === HTTP/1.1 完整诊断 ===

# 查看完整请求/响应头
curl -v http://example.com 2>&1 | grep -E '^[<>*]'

# 分段计时(DNS/TCP/TLS/首字节/总时间)
curl -w "\
  dns:        %{time_namelookup}s\n\
  tcp:        %{time_connect}s\n\
  tls:        %{time_appconnect}s\n\
  first_byte: %{time_starttransfer}s\n\
  total:      %{time_total}s\n\
  size:       %{size_download} bytes\n\
  speed:      %{speed_download} B/s\n" \
  -o /dev/null -s https://example.com

# 验证 Keep-Alive 是否生效
curl -v http://example.com http://example.com 2>&1 | grep -E 'Re-using|Connected'
# "Re-using existing connection" = Keep-Alive 生效

# 验证 HTTP 压缩
curl -H "Accept-Encoding: gzip" -I http://example.com
# Content-Encoding: gzip ← 压缩生效

# 验证 HTTP 缓存
curl -I http://example.com
# Cache-Control: max-age=3600
# ETag: "abc123"
curl -H "If-None-Match: \"abc123\"" -I http://example.com
# HTTP/1.1 304 Not Modified ← 缓存命中

# === HTTP/2 诊断 ===

# 验证是否支持 HTTP/2
curl --http2 -I https://example.com 2>&1 | grep -i 'HTTP/2'

# h2c — HTTP/2 明文调试工具
# 安装: go install github.com/nicholasgasior/h2c@latest
h2c connect example.com:443
h2c get /api/users

# nghttp — HTTP/2 专用客户端
nghttp -v https://example.com
# 可以看到 SETTINGS/HEADERS/DATA/WINDOW_UPDATE 帧

# 验证 HTTP/2 多路复用(同一连接并发请求)
nghttp -a -n https://example.com
# 看 stream_id: 1, 3, 5, 7... 多个流并行

# === gRPC 诊断 ===

# grpcurl — gRPC 的 curl
# 列出服务
grpcurl -plaintext localhost:50051 list

# 调用方法
grpcurl -plaintext -d '{"name":"world"}' localhost:50051 helloworld.Greeter/SayHello

# 查看方法签名
grpcurl -plaintext localhost:50051 describe helloworld.Greeter

# grpc-health-probe — 健康检查
grpc-health-probe -addr=localhost:50051

# === WebSocket 诊断 ===

# wscat — WebSocket 交互式客户端
# 安装: npm install -g wscat
wscat -c ws://localhost:8080/ws
> {"type":"ping"}
< {"type":"pong"}

# websocat — Rust 实现,更强大
websocat ws://localhost:8080/ws
# 支持管道: echo '{"msg":"hi"}' | websocat ws://localhost:8080/ws

# 用 curl 测试 WebSocket 升级握手
curl -v -H "Upgrade: websocket" \
  -H "Connection: Upgrade" \
  -H "Sec-WebSocket-Key: dGhlIHNhbXBsZQ==" \
  -H "Sec-WebSocket-Version: 13" \
  http://localhost:8080/ws
# 期望: 101 Switching Protocols
验证知识点工具命令预期输出/判断
HTTP Keep-Alivecurl -v 连续请求"Re-using existing connection"
HTTP/2 支持curl --http2 -IHTTP/2 200
HTTP/2 多路复用nghttp -v多个 stream_id 并行
gRPC 连通性grpcurl list列出服务
WebSocket 握手curl -v + Upgrade 头101 Switching Protocols
响应压缩curl -H "Accept-Encoding: gzip"Content-Encoding: gzip
缓存协商curl -H "If-None-Match"304 Not Modified

10.6 TLS/HTTPS 层 — openssl / ssldump / testssl.sh ​

bash
# === openssl s_client — TLS 握手完整诊断 ===

# 基础连接测试(看证书链、协议版本、密码套件)
openssl s_client -connect example.com:443 -servername example.com </dev/null

# 关键输出解读:
# ---
# Certificate chain
#  0 s:CN = example.com          ← 服务器证书
#    i:C = US, O = Let's Encrypt  ← 签发者
#  1 s:C = US, O = Let's Encrypt  ← 中间证书
#    i:O = Digital Signature Trust ← 根 CA
# ---
# SSL-Session:
#   Protocol  : TLSv1.3           ← 协议版本
#   Cipher    : TLS_AES_256_GCM_SHA384  ← 密码套件
#   Session-ID: ...
#   Verify return code: 0 (ok)    ← 证书验证通过

# 强制 TLS 1.3
openssl s_client -connect example.com:443 -tls1_3

# 强制 TLS 1.2(测试降级)
openssl s_client -connect example.com:443 -tls1_2

# 查看证书详情(有效期、SAN、签名算法)
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
  | openssl x509 -text -noout | grep -A2 'Validity\|Subject Alternative'

# 检查证书过期时间
openssl s_client -connect example.com:443 </dev/null 2>/dev/null \
  | openssl x509 -noout -dates
# notBefore=Jan  1 00:00:00 2024 GMT
# notAfter=Dec 31 23:59:59 2024 GMT

# 验证证书链完整性
openssl verify -CAfile ca-bundle.crt server.crt

# 测试 OCSP Stapling
openssl s_client -connect example.com:443 -status </dev/null | grep -A5 "OCSP Response"

# 测试 SNI(同一 IP 多证书)
openssl s_client -connect 1.2.3.4:443 -servername site-a.com </dev/null | grep "subject"
openssl s_client -connect 1.2.3.4:443 -servername site-b.com </dev/null | grep "subject"
# 不同 servername 返回不同证书 = SNI 正常

# === testssl.sh — 一键全面扫描 ===
# 安装: git clone https://github.com/drwetter/testssl.sh
./testssl.sh example.com
# 输出: 协议支持、密码套件强度、漏洞检测(Heartbleed/POODLE/BEAST)、证书信息

# === ssldump — TLS 流量解析 ===
ssldump -i eth0 -k server.key port 443
# 解密并显示 TLS 握手和应用数据(需要私钥,仅 RSA 密钥交换)

# === 测量 TLS 握手耗时 ===
openssl s_time -connect example.com:443 -new -num 100
# 100 次新建连接的平均耗时

# 对比 session 复用
openssl s_time -connect example.com:443 -reuse -num 100
# 复用 session 的耗时(应该明显更快)
验证知识点工具命令预期输出/判断
TLS 版本openssl s_client -tls1_3Protocol: TLSv1.3
证书链是否完整openssl s_clientdepth=0,1,2 且 Verify=0
证书是否过期openssl x509 -datesnotAfter 在未来
SNI 是否正确openssl s_client -servername返回对应域名证书
OCSP Staplingopenssl s_client -statusOCSP Response Status: successful
Session 复用openssl s_time -reuse耗时明显低于 -new
密码套件安全性testssl.sh无 WEAK/VULNERABLE 标记

10.7 lsof — 文件描述符与网络连接 ​

bash
# === 查看进程的所有网络连接 ===
lsof -i -a -p <pid>
# COMMAND  PID USER   FD   TYPE  DEVICE SIZE/OFF NODE NAME
# server  1234 root   3u  IPv4  12345      0t0  TCP *:8080 (LISTEN)
# server  1234 root   5u  IPv4  12346      0t0  TCP 10.0.0.1:8080->10.0.0.2:54321 (ESTABLISHED)

# === 查看谁在监听某端口 ===
lsof -i :8080
lsof -i TCP:8080 -s TCP:LISTEN

# === 按状态过滤 ===
lsof -i -s TCP:CLOSE_WAIT    # 所有 CLOSE_WAIT 连接
lsof -i -s TCP:ESTABLISHED   # 所有已建立连接

# === 查看进程打开的 socket 文件 ===
lsof -p <pid> | grep -E 'IPv4|IPv6|socket|pipe'

# === 查看某个远端 IP 的所有连接 ===
lsof -i @10.0.0.1

# === 查看 Unix Domain Socket ===
lsof -U

# === fd 泄漏排查 ===
# 1. 看总数
lsof -p <pid> | wc -l

# 2. 按类型分组
lsof -p <pid> | awk '{print $5}' | sort | uniq -c | sort -rn
# 输出:
#  1024 IPv4    ← 大量网络连接
#    50 REG     ← 普通文件
#    10 unix    ← Unix socket

# 3. 对比 fd 上限
cat /proc/<pid>/limits | grep "open files"
# Max open files  65536  65536  files

# === lsof vs ss 的选择 ===
# lsof: 看进程维度(这个进程打开了什么)
# ss:   看连接维度(这个端口/状态有哪些连接)
# 两者结合使用效果最佳

10.8 综合: 一个请求的全链路工具验证 ​

text
用户请求: curl https://api.example.com/users

每一步对应的验证工具:

┌─────────────────────────────────────────────────────────────────┐
│ 步骤                    │ 工具                │ 验证什么          │
├─────────────────────────┼─────────────────────┼───────────────────┤
│ 1. DNS 解析             │ dig / tcpdump :53   │ 解析耗时、结果    │
│ 2. TCP 三次握手         │ tcpdump / ss        │ SYN-RTT、backlog  │
│ 3. TLS 握手             │ openssl s_client    │ 版本、证书、耗时  │
│ 4. HTTP 请求发送        │ curl -v / tcpdump   │ 请求头、压缩      │
│ 5. 服务端处理           │ strace / pprof      │ 系统调用、耗时    │
│ 6. HTTP 响应接收        │ curl -w / tcpdump   │ 首字节、总时间    │
│ 7. TCP 连接复用/关闭    │ ss / tcpdump        │ Keep-Alive/FIN    │
│ 8. 网卡收发统计         │ ethtool -S          │ 丢包、错误        │
└─────────────────────────────────────────────────────────────────┘
bash
# 一键全链路诊断脚本
TARGET="api.example.com"
PORT=443

echo "=== 1. DNS ==="
dig +short $TARGET
dig $TARGET | grep "Query time"

echo "=== 2. TCP + TLS + HTTP 分段计时 ==="
curl -w "\
  dns:        %{time_namelookup}s\n\
  tcp:        %{time_connect}s\n\
  tls:        %{time_appconnect}s\n\
  first_byte: %{time_starttransfer}s\n\
  total:      %{time_total}s\n\
  http_code:  %{http_code}\n\
  remote_ip:  %{remote_ip}\n" \
  -o /dev/null -s "https://$TARGET/"

echo "=== 3. TLS 证书 ==="
openssl s_client -connect $TARGET:$PORT -servername $TARGET </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -dates -issuer

echo "=== 4. 路由 ==="
traceroute -T -p $PORT -m 10 $TARGET

echo "=== 5. 本机连接状态 ==="
ss -tan | grep $(dig +short $TARGET | head -1) | head -5

参考 ​

批注模式

💬 文章评论

暂无评论,来说点什么吧 👇

编程学习笔记