←

TCP 三次握手:两次为什么会建错连接

握手要同时完成两件事:双方都确认对方能收发,以及用序列号把后续字节的顺序定下来。

客户端先发 SYN,序列号选 x,进入 SYN-SENT。服务端回 SYN+ACK,确认号是 x+1,自己的序列号是 y,进入 SYN-RCVD。客户端再回 ACK,确认号 y+1,序列号 x+1,进入 ESTABLISHED。服务端收到后也进入 ESTABLISHED。

两次不够,是因为网络里可能有一条已经迟到的旧 SYN。客户端第一次发出去之后没有回应,于是重发,第二次成功,连接已经在用。这时旧的 SYN 才到达服务端。如果服务端收到 SYN 就认为连接建立,它会多出一个客户端并不知道的连接。两边对「现在有几条连接」的判断不一致,后面的数据会写进错误的那条。第三次是客户端确认:这次的序号是我现在要的。

客户端发出 SYN 之后消失,服务端的 SYN+ACK 没有回应。不能立刻连续重试。Linux 默认再试 5 次,间隔是 1、2、4、8、16 秒,第五次发出去还要再等 32 秒才知道结果,加起来大约 63 秒才放弃半开连接。

这 63 秒就是 SYN Flood 的成本。服务端在这段时间里为每个没有完成的握手保留状态。缓解是少重试几次、加大 SYN 队列;队列满了就拒绝新的握手,而不是继续堆。