第一次握手丢失了,会发生什么?
当 TCP 三次握手的第一次握手(即客户端发送的 SYN 报文)丢失时,会触发客户端的超时重传机制。
客户端行为
- 启动定时器 :客户端发送 SYN 报文后,会进入
SYN_SENT状态,并启动一个重传定时器。- 超时重传:如果在设定的时间(RTO, Retransmission Timeout)内没有收到服务端的 SYN+ACK 响应,客户端会认为 SYN 报文已丢失,并重新发送 SYN 报文。
- 指数退避:为了应对网络拥塞,重传的等待时间会采用"指数退避"算法,即每次等待的时间都会翻倍。在 Linux 系统中,初始 RTO 通常为 1 秒,后续的重传间隔依次为 2 秒、4 秒、8 秒等。
- 放弃连接 :重传次数并非无限。它受到内核参数
tcp_syn_retries的限制,默认值通常为 5 次。如果达到最大重传次数后依然没有收到服务端的响应,客户端就会放弃连接,并向应用程序返回一个"连接超时"的错误。
服务端行为
在整个过程中,由于服务端从未收到客户端的 SYN 报文,所以它完全感知不到这次连接请求的存在。服务端会一直保持在
LISTEN状态,等待新的连接请求。
第二次握手丢失了,会发生什么?
当 TCP 三次握手的第二次握手(即服务端发送的 SYN+ACK 报文)丢失时,会同时触发客户端和服务端双方的超时重传机制,因为双方都处于等待对方响应的状态。
客户端行为
- 等待确认 :客户端在发出第一次握手(SYN)后,会进入
SYN_SENT状态,等待服务端的 SYN+ACK 报文。- 误判并重传:由于迟迟收不到服务端的 SYN+ACK,客户端会误以为是自己发出的第一次握手(SYN)报文丢失了。
- 触发重传 :因此,客户端会触发超时重传机制,再次发送 SYN 报文。这个重传行为与"第一次握手丢失"时客户端的行为完全一样,同样受
tcp_syn_retries参数控制。
服务端行为
- 等待确认 :服务端在成功接收客户端的 SYN 报文后,会进入
SYN_RCVD状态,并向客户端发送 SYN+ACK 报文。- 触发重传:由于第二次握手(SYN+ACK)在网络中丢失,服务端迟迟收不到客户端的第三次握手(ACK)确认。因此,服务端会认为自己的 SYN+ACK 报文丢失了。
- 重传 SYN+ACK :服务端会触发自己的超时重传机制,重新发送 SYN+ACK 报文。这个重传次数由 Linux 内核参数
tcp_synack_retries控制,默认值通常为 5。
最终结果
- 连接成功:只要客户端或服务端任意一方的重传报文成功到达对方,后续的握手流程就能正常进行,连接最终可以建立。
- 连接失败:如果网络状况极差,导致双方的重传报文都持续丢失,那么客户端和服务端都会在达到各自的最大重传次数后,放弃连接并释放资源。
第三次握手丢失了,会发生什么?
当 TCP 三次握手的第三次握手(即客户端发送的 ACK 报文)丢失时,情况与前两次握手丢失有所不同,因为此时客户端已经认为连接建立成功。
服务端行为
- 等待确认 :服务端在发送了第二次握手(SYN+ACK)后,会进入
SYN_RCVD状态,等待客户端的 ACK 确认。- 触发重传:由于 ACK 报文丢失,服务端在超时时间内未收到确认,会认为自己的 SYN+ACK 报文丢失了。
- 重传 SYN+ACK :服务端会触发超时重传机制,重新发送 SYN+ACK 报文。这个重传次数由 Linux 内核参数
tcp_synack_retries控制,默认值通常为 5 次。如果达到最大重传次数后仍未收到 ACK,服务端将放弃连接并释放资源。
客户端行为
客户端在发出第三次握手(ACK)后,其 TCP 连接状态会立即变为 ESTABLISHED(已建立)。这意味着客户端认为连接已经成功,可以开始发送数据了。接下来会发生什么,取决于客户端是否立即发送数据。
场景一:客户端不立即发送数据
如果客户端在连接建立后没有立即发送数据,它会收到服务端重传的 SYN+ACK 报文。客户端在收到这个重复的报文后,会再次发送一个 ACK 报文作为确认,从而帮助服务端完成连接的建立。
场景二:客户端立即发送数据
客户端在发出 ACK 后,如果立刻向服务端发送数据,会发生以下情况:
- 数据携带确认:TCP 协议规定,数据报文同样可以将 ACK 标志位置为 1。因此,客户端发送的第一个数据包本身就起到了确认的作用。
- 服务端完成握手 :当服务端收到这个数据包时,它会识别出其中的 ACK 标志,从而确认第三次握手已经完成。服务端会立即从
SYN_RCVD状态转变为ESTABLISHED状态,并将该连接从半连接队列移入全连接队列,然后正常处理客户端发来的数据。
一个常见的误区是:认为服务端会因为没收到纯 ACK 包而回复一个 RST(重置)报文来断开连接。实际上,只有在服务端已经放弃该连接(例如,重传次数耗尽后)的情况下,再收到客户端的数据,才会回复 RST 报文。
最终结果
- 连接成功:在绝大多数情况下,通过服务端的 SYN+ACK 重传机制,或者客户端立即发送的数据包,第三次握手的确认都能成功送达服务端,连接最终可以正常建立。
- 连接失败:只有在网络状况极差,导致服务端的所有 SYN+ACK 重传和客户端的所有数据包都丢失,服务端才会在达到最大重传次数后放弃连接。
什么是 SYN 攻击?如何避免 SYN 攻击?
SYN 攻击,也称为 SYN 泛洪(SYN Flood)攻击,是一种非常常见的拒绝服务(DoS)攻击。它通过利用 TCP 协议三次握手的固有缺陷,耗尽目标服务器的资源,使其无法为正常用户提供服务。
什么是 SYN 攻击?
SYN 攻击的原理基于 TCP 三次握手过程:
- 正常握手 :客户端发送一个 SYN 包请求连接 → 服务器回复 SYN+ACK 包并分配资源(进入
SYN_RECV状态) → 客户端回复 ACK 包,连接建立。- 攻击过程:攻击者会伪造大量的虚假 IP 地址,并向服务器持续发送海量的 SYN 请求包。
- 资源耗尽 :服务器为每一个收到的 SYN 请求都会分配内存资源,并发送 SYN+ACK 包进行响应,然后进入
SYN_RECV状态,等待客户端的 ACK 确认。由于源 IP 是虚假的,服务器永远收不到 ACK 回复,这些未完成的连接(称为"半连接")会一直占用服务器资源,直到超时。- 服务瘫痪:当服务器的"半连接队列"被这些虚假连接占满后,它就无法再处理任何新的、来自真实用户的连接请求,从而导致服务中断或系统瘫痪。
如何避免 SYN 攻击?
防御 SYN 攻击需要采取多层次、纵深化的策略,从操作系统内核到网络边界设备协同工作。
1. 操作系统层优化
这是在服务器本机进行的第一道防线,主要通过调整内核参数来增强自身的抵抗能力。
启用 SYN Cookies
这是防御 SYN 攻击最核心、最有效的手段之一。它的原理是,服务器在收到 SYN 请求后,不立即分配内存资源来保存连接状态,而是根据连接信息(源/目的 IP、端口、时间戳等)通过哈希算法计算出一个"Cookie"值,并将其作为初始序列号放入 SYN+ACK 包中发送出去。只有当收到带有正确 ACK 值的回复时,服务器才会验证 Cookie 并分配资源建立连接。这从根本上避免了半连接队列被耗尽的风险。
- Linux 启用命令 :
echo 1 > /proc/sys/net/ipv4/tcp_syncookies调整内核参数
- 扩大半连接队列 :增加
tcp_max_syn_backlog的值,让服务器能容纳更多的半连接,提高应对突发流量的能力。- 缩短超时时间 :减少
tcp_synack_retries的值,让服务器更快地放弃无效的半连接,从而释放资源。
2. 网络设备层防护
在网络入口处部署防火墙或专业的 Anti-DDoS 设备,可以在攻击流量到达服务器之前就进行识别和过滤。
SYN Proxy (代理)
防火墙设备会代替后端服务器与客户端完成 TCP 三次握手。只有当握手成功,确认为真实连接后,防火墙才会与后端服务器建立新的连接。这样,所有的虚假 SYN 请求都在网络边界被拦截。
源认证与首包丢弃
- 源认证:Anti-DDoS 设备拦截 SYN 包,代替服务器回复 SYN+ACK。如果客户端能正确回复 ACK,则被认为是真实源,其后续流量会被放行。
- 首包丢弃:设备会直接丢弃收到的第一个 SYN 包。由于正常的 TCP 客户端在超时后会重传 SYN 包,而攻击者通常使用海量伪造源 IP,不会重传。设备只对重传的 SYN 包进行源认证,这大大减轻了设备自身的处理压力。
速率限制
配置防火墙规则,限制单位时间内来自同一 IP 地址或发往同一端口的 SYN 包数量,超过阈值的包将被直接丢弃。
3. 架构与服务层防护
对于大规模或复杂的攻击,单点防御可能不够,需要从整体架构和外部服务入手。
使用 CDN 或 DDoS 防护服务
将业务流量接入专业的 DDoS 防护服务(如 Cloudflare、阿里云 DDoS 防护等)。这些服务拥有巨大的带宽资源和先进的清洗中心,能够将恶意攻击流量从正常流量中分离出来,只将"干净"的流量回源到你的服务器。
构建多层防御体系最有效的防护是结合以上所有手段。例如,在服务器上启用 SYN Cookies 作为最后一道防线,在网络边界使用防火墙进行 SYN Proxy 和速率限制,并接入云服务商的 DDoS 防护服务来抵御超大流量的攻击。