TCP连接队列
TCP连接队列
在讲 TCP 三次握手 时,操作系统内核实际上维护了两个非常关键的队列:
- 半连接队列(SYN队列)
- 全连接队列(Accept队列)
这两个队列直接关系到服务端的并发能力和抗攻击能力
一、整体流程
客户端发起连接时:
1 | 客户端 → SYN → 服务端 |
在这个过程中:
| 阶段 | 连接状态 | 所在队列 |
|---|---|---|
| 收到 SYN | 半连接(未完成握手) | 半连接队列 |
| 收到 ACK | 完整连接 | 全连接队列 |
二、半连接队列(SYN Queue)
2.1 定义
半连接队列用于存放:
已经收到客户端 SYN,但还没完成三次握手的连接
即状态为:
1 | SYN_RECV |
2.2 工作流程
- 服务端收到客户端 SYN
- 内核创建一个“请求连接”(request sock)
- 放入 半连接队列
- 回复 SYN+ACK,等待客户端 ACK
2.3 特点
- 存的是“未完成连接”
- 每个元素比较轻量(不是完整 socket)
- 容量有限(由内核参数控制)
常见参数(Linux):
1 | net.ipv4.tcp_max_syn_backlog |
2.4 风险:SYN Flood 攻击
攻击方式:
- 大量发送 SYN
- 不回 ACK
结果:
- 半连接队列被占满
- 正常请求无法进入
防御手段:
- SYN Cookie
- 增大 backlog
- 限速
三、全连接队列(Accept Queue)
3.1 定义
全连接队列用于存放:
已经完成三次握手,等待应用层 accept 的连接
状态:
1 | ESTABLISHED |
3.2 工作流程
- 收到客户端 ACK
- 三次握手完成
- 连接从半连接队列移除
- 加入 全连接队列
- 等待应用调用:
1 | accept() |
3.3 特点
- 存的是完整 socket
- 可以直接读写数据
- 队列满会影响新连接
3.4 队列大小
由 listen(fd, backlog) 决定:
1 | listen(sockfd, backlog) |
注意:
- backlog 不是简单等于全连接队列大小
- 实际受以下限制:
1 | net.core.somaxconn |
四、两个队列的关系(核心理解)
1 | SYN |
五、关键问题
5.1 如果半连接队列满了会怎样?
- 新的 SYN 被丢弃
- 或启用 SYN Cookie(不进队列)
5.2 如果全连接队列满了?
- 已完成握手的连接无法进入队列
- 客户端可能:
- 收不到响应
- 或被 RST
5.3 为什么要分两个队列?
核心原因:
| 队列 | 作用 |
|---|---|
| 半连接队列 | 防御攻击、降低资源占用 |
| 全连接队列 | 提供稳定的应用层连接 |
本质是:
将“未确认连接”和“已建立连接”隔离,提高系统稳定性
5.4 backlog 到底控制哪个?
这是经典坑点
结论(Linux):
1 | backlog ≈ 全连接队列大小 |
但:
- 实际行为会被内核优化调整
- 两个队列都会受 backlog 影响(间接)
5.5 客户端有这两个队列吗?
半连接队列(SYN队列)和全连接队列(Accept队列)主要只存在于服务端,客户端没有“这两个队列”的概念(但有自己的连接状态管理)
这两个队列是为了解决一个核心问题:服务端要同时处理大量“被动连接请求”
六、总结
可以直接这样答
TCP 服务端维护两个队列:
- 半连接队列:存放收到 SYN 但未完成三次握手的连接(SYN_RECV)
- 全连接队列:存放完成三次握手、等待 accept 的连接(ESTABLISHED)
三次握手过程中:
- 第一次握手进入半连接队列
- 第三次握手完成后进入全连接队列
半连接队列主要用于防御 SYN Flood 攻击,全连接队列用于提供给应用层处理。
两者分别由
tcp_max_syn_backlog和listen backlog控制。