TCP 流分析82 条

TCP 三次握手判读

抓包判读建连:客户端 SYN(seq=x) → 服务端 SYN-ACK(seq=y, ack=x+1) → 客户端 ACK(ack=y+1);三步齐全才算建立成功。

三次握手在做什么

TCP 建连要走三步:客户端发 SYN,带上自己的初始序列号与 MSS、窗口缩放、SACK 许可等选项;服务端回 SYN-ACK,带上自己的初始序列号并确认对方;客户端再回一个 ACK 确认对方。三步走完连接进入已建立状态,之后才能传数据。抓包判读的价值在于把「连不上」拆成三种完全不同的结论:请求没发出、发出了没人应答、对方回了本地没收到。这三者对应的责任方分别是本机配置、路径与中间设备、以及回程链路。

基本信息

判读依据序列号与确认号的对应关系,以及标志位组合
tcpdump 过滤tcp[tcpflags] = tcp-syn(纯 SYN)、tcp[tcpflags] = tcp-syn|tcp-ack(SYN-ACK)
Wireshark 过滤tcp.flags.syn == 1 && tcp.flags.ack == 0
时延基线SYN 到 SYN-ACK 的间隔约为半个往返时延
协议依据RFC 9293(TCP)

典型使用场景

  • 判断服务是否可达:SYN 发出后有没有收到 SYN-ACK
  • 判断慢在哪一段:看 SYN 到 SYN-ACK 的间隔,以及是否出现重复 SYN
  • 判断责任方:静默无响应多为路径丢弃,立刻回 RST 说明端口没有进程监听
  • 判断队列压力:服务端收到 SYN 却不回,可能是半连接队列溢出
  • 验证策略改动:对比变更前后的握手结果,确认放行是否真的生效

抓取与判读

抓取与判读
tcpdump -i eth0 -nn 'tcp[tcpflags] = tcp-syn' -c 20        # 只看纯 SYN
tcpdump -i eth0 -nn 'tcp[tcpflags] = tcp-syn|tcp-ack'      # 只看 SYN-ACK
tcpdump -i eth0 -nn -ttt 'tcp port 443' -c 30              # 带相对时间看间隔
ss -lnt | grep :443                                        # 服务端核对是否在监听
sysctl net.core.somaxconn net.ipv4.tcp_max_syn_backlog     # 半连接队列相关参数

三步逐包读法

报文序列号关系如何判读
SYNseq=x,携带 MSS 与窗口选项建连请求,方向由客户端指向服务端口
SYN-ACKseq=y,ack=x+1服务端同意建连;确认号不等于 SYN 序列号加一即为异常
ACKseq=x+1,ack=y+1客户端确认,连接进入已建立状态
重复 SYN序列号与首个 SYN 相同,间隔递增未收到 SYN-ACK 的重传,问题在上行路径或服务端
SYN 后紧跟 RST无对应确认关系端口未监听或被策略明确拒绝
只有 SYN 无任何回包无路径静默丢弃,查中间设备与会话表
SYN 与 SYN-ACK 的间隔约等于半个往返时延,是判断「建连慢」的基线。这个间隔很小而整体依然慢,说明耗时在后续的数据传输或应用处理阶段,不要继续在建连上找原因。
用抓包下结论前先说明抓包点:在客户端侧抓只能证明「这里没收到 SYN-ACK」,不能证明服务端没有发出,关键问题必须两端同时抓取对照。

命令行核对

命令行核对
ss -s                       # 看 socket 总量与各状态计数
netstat -s | grep -i -A2 listen   # 观察因队列满而丢弃的 SYN 计数
tcpdump -i eth0 -nn -c 10 'tcp[tcpflags] & tcp-syn != 0' | grep -c 'S\.'   # 统计回包形式

常见结论与依据

  • 只有 SYN、没有 SYN-ACK、也没有 RST:请求被路径上某台设备丢弃,或服务端队列已满而静默丢弃
  • SYN 与 SYN-ACK 都有,但客户端重传 SYN:说明回包在回程路径上丢失,重点查反向链路
  • 立刻收到 RST:报文到达了主机,但没有进程监听该端口,或被策略配置为拒绝
  • 握手正常但仍然无法通信:问题在 TLS 或应用层,继续看后续数据报文
  • 握手时延接近秒级:通常是初始重传超时造成,说明第一个 SYN 或 SYN-ACK 被丢弃
  • 客户端与抓包点在同一台机器时,源端口相同而序列号递增的重传说明本地已经重试过多次
不要把「没有看到 SYN-ACK」直接写成结论。单向抓包点的缺失只能说明本地没收到,回包可能在回程路径上被丢弃,也可能根本没发出,两种情况的责任方完全不同。

易错点

  • 抓包点不覆盖回程路径时会把「服务端未回」当结论,必须两端同时抓才能定性
  • 半连接队列溢出时服务端静默丢弃 SYN,现象与防火墙丢包完全一样,要到服务端看统计计数
  • 用 tcp[tcpflags] & tcp-syn != 0 会把 SYN-ACK 一并抓进来,判断建连请求要用等于号写法
  • 负载均衡健康检查会周期性产生握手样本,统计前先按目的端口与频率过滤业务流量
  • 中间设备做了连接代理或优化时,两端看到的序列号与选项不一致,不能按端到端三次握手模板比对

数据来源与参考

  • Wireshark User's Guide(TCP 建连)
  • RFC 9293(TCP)
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌