TCP 实战与排错 — 抓包、观测与故障定位
TCP Wireshark 抓包、ss/netstat 状态、TIME_WAIT 堆积/重传高/建连失败排查、与 UDP 对比 / 传输层 / RFC 9293
这篇你能学到
- 用 Wireshark 过滤式快速锁定握手失败、重传、重复 ACK、RST 和零窗口这几类关键现象。
- 用
ss/netstat从系统侧看连接状态分布、每连接 RTT 与 cwnd,以及几个常用内核调优项。 - 六类高频故障(SYN 无响应、TIME_WAIT 堆积、重传率高、吞吐上不去、RST 频发、建连慢)的结论先行式排查,外加对比表和面试速查。
1. 抓包观察
下面这些过滤式的作用:从一堆 TCP 流量里,分别把"握手首包"“重传"“重复 ACK"“异常复位"“对端收不动了"这几类现象单独挑出来——排障基本就是围绕这五件事展开。
| 过滤式 | 作用 |
|---|---|
tcp / ip.proto == 6 | 所有 TCP |
tcp.flags.syn == 1 && tcp.flags.ack == 0 | 只看 SYN(握手首包) |
tcp.analysis.retransmission | 重传段 |
tcp.analysis.duplicate_ack | 重复 ACK(可能触发快速重传) |
tcp.flags.reset == 1 | RST(异常断开) |
tcp.analysis.zero_window | 接收窗口为 0(对端繁忙) |
这三条命令分别干什么:第一条带绝对时间戳和绝对序号抓 443 端口流量,适合看时序;第二条只抓 SYN,用来确认握手请求到底有没有发出/到达;第三条从本机视角快速列出当前 TCP 连接及其所属进程。
Wireshark 中 Follow → TCP Stream 可重组字节流;IO Graph 看吞吐与重传率。
2. 常用命令 / 观测
这几条命令分别干什么:第一条统计各 TCP 状态各有多少连接(一眼看出是不是 TIME_WAIT 或 CLOSE_WAIT 堆积);第二条看每条连接的 RTT、cwnd、重传等内部指标;第三条看系统级累计重传数;后两条是临时的内核调优——分别开启客户端 TIME_WAIT 复用和放大接收缓冲上限。
3. 常见故障与排错
现象:SYN 无响应
先记住结论: 先别怀疑 TCP 本身——要么对端根本没在监听,要么中间被防火墙静默丢弃,要么后端已经过载。
排查/解决:ss -lnt 查 LISTEN;抓包看 SYN 是否到达、是否回 SYN+ACK。
现象:大量 TIME_WAIT
先记住结论: 这通常不是故障,而是"你是主动关闭方 + 短连接高并发"的必然结果。
排查/解决:连接复用/长连接;tcp_tw_reuse(客户端);tcp_max_tw_buckets。
现象:重传率高
先记住结论: 重传是结果不是原因,真正的病根在丢包、链路拥塞或缓冲膨胀上。
排查/解决:tcp.analysis.retransmission 定位;查 RTT、丢包;换 BBR。
现象:吞吐上不去
先记住结论: 带宽没跑满,多半是窗口被卡住了——rwnd/cwnd 受限,或者窗口缩放选项压根没开。
排查/解决:看 Window Scale 选项;增大缓冲;BBR。
现象:RST 频发
先记住结论: RST 是"对方明确拒绝或已不认这条连接”,先找出是谁发的。
排查/解决:抓 RST 来源;查服务状态与 backlog。
现象:连接建立慢(SYN 队列满)
先记住结论: 请求到了但排不上队——半连接/全连接队列已满。
排查/解决:调大半/全连接队列。
| 现象 | 原因 | 排查/解决 |
|---|---|---|
| SYN 无响应 | 对端未监听 / 防火墙 DROP / 后端过载 | ss -lnt 查 LISTEN;抓包看 SYN 是否到达、是否回 SYN+ACK |
| 大量 TIME_WAIT | 短连接高并发(主动关闭方) | 连接复用/长连接;tcp_tw_reuse(客户端);tcp_max_tw_buckets |
| 重传率高 | 网络丢包 / 链路拥塞 / 缓冲膨胀 | tcp.analysis.retransmission 定位;查 RTT、丢包;换 BBR |
| 吞吐上不去 | rwnd/cwnd 受限、窗口缩放未开 | 看 Window Scale 选项;增大缓冲;BBR |
| RST 频发 | 端口无服务、半开连接、应用崩溃 | 抓 RST 来源;查服务状态与 backlog |
| 连接建立慢(SYN 队列满) | tcp_max_syn_backlog 不足 | 调大半/全连接队列 |
4. 与其他协议对比
| 维度 | TCP | UDP |
|---|---|---|
| 连接 | 面向连接 | 无连接 |
| 可靠 | 是(重传/确认) | 否 |
| 消息边界 | 无(字节流) | 有(数据报) |
| 拥塞/流控 | 有(cwnd/rwnd) | 无 |
| 头部 | 20+ 字节 | 8 字节 |
| 适用 | 文件/网页/SSH | 视频/游戏/DNS/QUIC |
5. 速查表 / 常见面试题
| 项目 | 值 |
|---|---|
| RFC / IP 协议号 | RFC 9293 / 6 |
| MSS(典型) | 1460 |
| 握手 / 挥手 | 3 次 / 4 次 |
| TIME_WAIT | 2MSL(Linux 60s) |
| 重传触发 | RTO 超时 / 3 个重复 ACK |
| 窗口 | rwnd(流控)+ cwnd(拥塞) |
- Q:为什么握手 3 次、挥手 4 次? 握手 SYN+ACK 可合并;挥手时被动方收到 FIN 后可能还有数据要发,故 ACK 与 FIN 分开。
- Q:TIME_WAIT 意义? 确保最后 ACK 到达对端、让网络中残留旧报文失效,避免新连接误收。
- Q:TCP 为什么会粘包? TCP 是字节流无消息边界,“粘包"是应用层未分帧,需在应用层加长度前缀/分隔符。
- Q:快速重传 vs 超时重传? 收 3 个重复 ACK 即重发(进快速恢复);RTO 超时则回慢启动,后者代价大。
- Q:CUBIC 与 BBR 区别? CUBIC 基于丢包做拥塞信号(Reno 演进);BBR 基于带宽-时延积建模,抗缓冲膨胀。