连接超时原理:TCP三次握手与超时重传
说到连接超时,咱们得先搞清楚一个基础问题——TCP连接是怎么建起来的。说白了,就是那个经典的三次握手。我干了这么多年网络排障,发现很多人对三次握手停留在背口诀的阶段,真到抓包分析时就懵了。今天咱们把它彻底掰开揉碎。
三次握手:连接建立的基石
TCP是面向连接的协议,通信前必须先建立连接。这个建立过程就是三次握手。你想想看,就像两个人打电话,得先确认对方在听,对吧?
核心要点:三次握手的本质是双方确认彼此的收发能力都正常。
具体流程是这样的:
- 客户端发送SYN包——客户端说:“我要跟你建立连接,这是我的初始序列号x。”
- 服务端回复SYN-ACK包——服务端说:“收到你的请求,这是我的初始序列号y,我确认收到了你的x。”
- 客户端发送ACK包——客户端说:“我确认收到了你的y,连接建立完成。”
嗯,这里要注意:第三步的ACK包其实可以携带数据。但在实际抓包中,大部分场景第三步就是个纯ACK,不带数据。
SYN包:连接请求的发起者
SYN包是三次握手的第一个包。它的标志位SYN=1,序列号是一个随机生成的初始值(ISN)。
我记得有一次排查一个跨机房连接超时的问题,抓包发现客户端一直在发SYN,但服务端根本没回应。后来发现是中间防火墙把SYN包给丢了。这种问题,光看应用层日志根本看不出来,必须抓包。
实战技巧:抓包时看到大量重传的SYN包,但没有任何SYN-ACK回应,大概率是中间网络设备(防火墙、路由器)把包丢了,或者服务端根本没在监听这个端口。
SYN-ACK包:服务端的回应
服务端收到SYN后,如果端口在监听,就会回复SYN-ACK包。这个包同时设置了SYN和ACK标志位,ACK号是客户端的ISN+1,表示“我收到了你的SYN”。
这里有个坑——SYN-ACK包丢了怎么办?客户端收不到SYN-ACK,就会认为SYN包丢了,然后触发超时重传。我曾经遇到过一个案例,服务端CPU负载过高,导致SYN-ACK包在协议栈里排队太久,客户端那边已经超时重传了。抓包一看,客户端发了3个SYN,服务端回了3个SYN-ACK,但客户端只认第一个,后面的都当重复包丢弃了。
避坑指南:我曾经被一个假象坑过——抓包看到服务端确实回了SYN-ACK,但客户端就是没收到。后来发现是服务端的SYN-ACK包被出口路由器的ACL给拦截了。所以抓包一定要在两端都抓,才能定位问题到底出在哪一侧。
ACK包:握手的收尾
客户端收到SYN-ACK后,发送ACK包确认。这个包的ACK号是服务端的ISN+1。至此,连接建立完成,双方可以开始传输数据了。
你可能会问:为什么是三次,不是两次?我个人的理解是:两次握手无法防止已失效的连接请求到达服务端。举个例子,客户端发了个SYN,网络延迟导致这个包很久才到服务端,但客户端已经超时重传了。如果只有两次握手,服务端收到这个延迟的SYN就会建立连接,白白浪费资源。三次握手通过客户端的最终ACK来确认,就能避免这个问题。
超时重传机制:TCP的可靠性保障
TCP是可靠传输协议,怎么保证可靠?其中一个关键机制就是超时重传。发送方发出一个包后,会启动一个定时器。如果在规定时间内没收到确认,就重新发送这个包。
这个“规定时间”不是固定的。TCP会根据网络状况动态调整——这就是RTO(Retransmission Timeout)。RTO的计算基于RTT(Round-Trip Time),也就是一个包从发出去到收到确认的时间。
| 参数 | 说明 | 默认值(Linux) |
|---|---|---|
| RTO_min | 最小超时时间 | 200ms |
| RTO_max | 最大超时时间 | 120s |
| 初始RTO | 第一次发送SYN时的超时时间 | 1s(实际由内核决定) |
| 重传次数 | SYN包最大重传次数 | 6次(约127s后放弃) |
我个人习惯在排查连接超时问题时,先看抓包里的重传间隔。如果重传间隔是1s、2s、4s、8s这样指数增长,说明是正常的TCP超时重传。如果重传间隔固定不变,那可能是应用层自己实现的超时逻辑,问题可能出在代码里。
连接超时的几种典型场景
结合三次握手和超时重传,连接超时通常出现在以下几个环节:
- SYN包发不出去——客户端路由不可达,或者防火墙拦截了出站SYN。抓包特征:客户端一直在重传SYN,但抓不到任何回应。
- SYN-ACK包回不来——服务端回了SYN-ACK,但被中间设备拦截了。抓包特征:客户端重传SYN,服务端也在重传SYN-ACK,两边都在重传。
- ACK包丢失——客户端发了ACK,但服务端没收到。抓包特征:服务端重传SYN-ACK,客户端回复ACK,反复循环。
- 服务端端口未监听——服务端收到SYN后直接回RST包。抓包特征:客户端发SYN,服务端立即回RST,连接立即失败。
关键判断:抓包时看到RST包,说明服务端是活着的,但端口没开或者应用没在监听。看到重传的SYN但没有SYN-ACK,说明服务端可能挂了或者网络不通。看到重传的SYN-ACK,说明客户端可能没收到或者收到了但ACK丢了。
知识体系总览
下面这张图把三次握手和超时重传的核心逻辑串起来了。我建议你把它存下来,以后排查连接超时问题时对照着看。
这张图把三次握手的三个步骤和超时重传的机制放在一起了。你排查连接超时问题时,对照这个流程,看抓包卡在哪一步,问题就定位到哪一步。
我的习惯:拿到一个连接超时的抓包,我第一件事就是看有没有SYN包。没有SYN?那客户端根本没发起连接。有SYN但没SYN-ACK?看重传间隔,判断是网络问题还是服务端问题。有SYN-ACK但没最后的ACK?看看是不是客户端收到SYN-ACK后丢了或者被防火墙拦截了。三步走完还没建立连接?那可能是应用层的问题了。