TCP为什么要三次握手
TCP为什么要三次握手
两次握手的致命缺陷在于服务端资源分配过早,缺乏最终确认机制。
服务端在收到第一次握手(SYN)并发出 SYN+ACK 后,就立刻分配完整连接资源并进入 ESTABLISHED 状态。它没有 SYN_RCVD 这个中间状态来做缓冲。
这就导致:无论收到的是序列号不同的历史报文,还是序列号相同的超时重传报文,服务端都会盲目地分配一份完整资源。等到后续发现不对劲,只能靠发送 RST 来强制重置连接,此时刚刚分配的资源已经浪费掉了。
如果攻击者伪造大量历史 SYN,服务端就会被这种无效连接瞬间榨干资源,这就是 SYN Flood 攻击的底层逻辑。
所以,三次握手不可替代的价值,就是用 SYN_RCVD 半连接状态把服务端资源分配推迟到第三次握手之后。确认对了才分配资源,确认不对(发 RST)直接在半连接队列里删掉,从头到尾不产生完整资源开销。
一、收到历史报文

可以看见,建立了一次历史会话把资源分配了才发现不对,就 RST 重连,浪费了一次完整资源。
二、超时重发

主要原因在于握手报文的丢失,有两种情况:
第一种情况:客户端发送的 SYN 报文丢失
这种情况下会超时重发 SYN 报文。由于此时服务端尚未分配任何资源,所以这种超时重传不会造成资源浪费。
第二种情况:服务端回复的 SYN+ACK 报文丢失
这首先意味着服务端已经接收到了客户端的 SYN,并分配了完整资源建立了连接。而 SYN+ACK 丢失后,客户端会超时重发 SYN 报文(ACK 报文丢失不会由客户端重传,需要等待对方重传之前的报文)。
这种情况下,当服务端再次收到这个 SYN 报文时,又会分配一份完整资源建立连接,造成资源浪费。
思考:在两次握手的时候,我们认为再次接到SYN信号会重复分配资源,那么3次握手为什么不会呢?
三、三次握手如何解决
采用三次握手的话,就不用担心握手报文丢失带来的资源重复分配问题。
即使是第三次握手 ACK 丢失了,客户端再次收到第二次握手的 SYN+ACK 报文,也不会重复分配资源。
第三次握手丢失后发生了什么?
客户端收到服务端的 SYN+ACK 报文后,会给服务端回一个 ACK 报文(即第三次握手),此时客户端状态进入 ESTABLISHED,并分配资源。
第三次握手的 ACK 是对第二次握手 SYN 的确认报文。如果这个 ACK 丢失了,服务端迟迟收不到确认,就会触发超时重传机制,重传 SYN+ACK 报文,直到收到第三次握手或达到最大重传次数。
关键点来了:客户端再次收到重传的 SYN+ACK 时,由于它已经处于 ESTABLISHED 状态,内核通过四元组发现连接已存在,不会重新分配资源或建立新连接,只会再次发送一个 ACK 给服务端。
客户端收到重传的 SYN+ACK,为什么不会重复分配资源?
凭的是内核的四元组哈希表和 TCP 状态机。
客户端在第一次收到 SYN+ACK 时,内核会通过四元组(源IP、源端口、目的IP、目的端口)查表。由于是第一次,哈希表中不存在该连接,内核才会创建 socket、分配缓冲区,并将状态从 SYN_SENT 切换到 ESTABLISHED,同时把四元组绑定进哈希表。
当第二次收到重传的 SYN+ACK 时,内核提取四元组再次查表,发现该连接已经存在,且状态已经是 ESTABLISHED。内核据此判定这是一个重复报文,于是直接丢弃,不会重复创建 socket 或分配任何新资源,只会补发一个 ACK 作为响应。
这种基于唯一四元组和状态机的机制,保证了客户端不会因为收到重复的 SYN+ACK 而重复分配资源。
服务端保障
而服务端在第三次握手 ACK 到达之前,一直处于 SYN_RCVD(半连接)状态,连接只在半连接队列(SYN Queue) 里。它重传 SYN+ACK 并不会分配完整资源,只有等最终收到客户端的 ACK 后,才会把连接移入全连接队列(Accept Queue),正式分配完整资源
具体的见第三次握手丢失了会发生什么

