2. TCP三次握手原理:SYN、SYN-ACK、ACK详解、握手状态机、握手失败与超时的关系

说到TCP连接超时,绕不开的就是三次握手。很多刚入行的朋友觉得握手就是“发三个包”,其实没那么简单。我见过不少线上故障,根因就是握手阶段出了问题。今天咱们把这层窗户纸捅破。

2.1 三次握手到底在干什么?

说白了,三次握手就是通信双方互相确认“我准备好了,你呢?”的过程。它要解决三个核心问题:

  • 确认对方在线——不是僵尸进程或网络不可达
  • 交换初始序列号——后续数据包的编号起点
  • 协商窗口大小等参数——双方能收多少数据

你想想看,如果只有两次握手,服务端怎么知道客户端收到了自己的SYN-ACK?万一丢了,服务端就得一直傻等。三次握手是经过实践检验的最小可靠方案。

2.2 三个报文,一个都不能少

步骤 报文 方向 核心作用
第1步 SYN 客户端 → 服务端 请求建立连接,携带初始序列号(ISN)
第2步 SYN-ACK 服务端 → 客户端 确认收到SYN,同时发送自己的SYN
第3步 ACK 客户端 → 服务端 确认收到SYN-ACK,连接建立完成

这里有个细节:SYN和ACK标志位是同时存在的。SYN-ACK报文里两个标志位都置1,表示“我收到了你的SYN,同时我也要发起连接”。

抓包要点:在Wireshark里过滤 tcp.flags.syn == 1 就能看到所有SYN包。但要注意,SYN-ACK包也会被匹配到,因为它的SYN位也是1。如果想只看纯SYN,用 tcp.flags.syn == 1 and tcp.flags.ack == 0。

2.3 握手状态机:从CLOSED到ESTABLISHED

TCP连接的状态变化,就像一部电梯的楼层按钮。每个状态都对应一个特定的阶段。我个人习惯在排查问题时,先看两端的状态,再决定抓包方向。

TCP三次握手状态机 CLOSED SYN_SENT ESTABLISHED LISTEN SYN_RCVD ESTABLISHED 主动打开 收到SYN-ACK 收到SYN 收到ACK SYN (seq=x) SYN-ACK (seq=y, ack=x+1) ACK (seq=x+1, ack=y+1) 关键点 客户端先发SYN → 服务端回复SYN-ACK → 客户端再回复ACK

这张图我建议你保存下来。每次排查连接超时,我都会在脑子里过一遍这个状态机,看两端分别卡在哪个状态。

2.4 握手失败与超时的关系

握手失败,说白了就是三个报文没凑齐。最常见的场景:

  • SYN丢了:客户端发了SYN,石沉大海。客户端会等一个RTO(初始重传超时),默认3秒,然后重传SYN。
  • SYN-ACK丢了:服务端回了SYN-ACK,客户端没收到。客户端继续重传SYN,服务端收到重复SYN后重新回复SYN-ACK。
  • ACK丢了:客户端发了ACK,服务端没收到。服务端会重传SYN-ACK,客户端收到后重新回复ACK。

注意:我曾经遇到过一个案例,客户端一直报连接超时,抓包发现客户端发了SYN,服务端也回了SYN-ACK,但客户端就是没反应。最后发现是客户端的防火墙把SYN-ACK当成“外部主动连接”给拦截了。所以抓包一定要在两端都抓,只看一端容易误判。

2.5 超时参数与重传策略

参数 默认值(Linux) 说明
tcp_syn_retries 6 SYN重传次数,超过后放弃连接
tcp_synack_retries 5 SYN-ACK重传次数,超过后放弃
tcp_retries1 3 尝试重传阈值,超过后通知上层
tcp_retries2 15 最终重传阈值,超过后断开连接

嗯,这里要注意:重传超时时间是指数退避的。第一次重传等3秒,第二次等6秒,第三次12秒……所以如果网络真的不通,客户端可能要等上几分钟才会报错。这就是为什么很多应用层超时设置得比TCP超时短——不能傻等。

实战技巧:在排查连接超时时,我习惯先看抓包里的 [SYN] 包有没有被重传。如果看到多个SYN包,说明客户端没收到SYN-ACK。这时候去服务端抓包,看服务端有没有收到SYN、有没有回复SYN-ACK。一对比,问题出在哪段链路就清楚了。

2.6 握手超时的典型抓包特征

在Wireshark里,握手超时通常表现为:

  • 只有SYN,没有SYN-ACK:客户端发了SYN,然后不断重传。这通常是网络不通或服务端没监听端口。
  • 有SYN和SYN-ACK,但没有最后的ACK:服务端在重传SYN-ACK。这通常是客户端的ACK丢了,或者客户端已经放弃连接。
  • 有完整的三个包,但紧接着就是RST:连接建立后立即被重置。这通常是应用层拒绝连接,比如防火墙策略或服务端连接数满。

你想想看,如果抓包看到的是第一种情况,你该查什么?先确认服务端端口是否监听,再检查中间防火墙有没有拦截。如果是第二种,重点查客户端为什么没回ACK——是不是客户端已经超时退出了?

好了,三次握手的原理就聊到这儿。记住这个状态机,后面讲抓包实战时,我们会反复用到它。