← 返回题目列表

TCP 的半连接队列和全连接队列是什么?

高频 中等 第 9 / 30 题 更新于 2026/07/28
TCP半连接队列全连接队列SYN Flood

简化版

三次握手时,服务端内核维护两个队列半连接队列(SYN 队列)存放「收到 SYN、回了 SYN+ACK、还在等第三次 ACK」的未完成连接(SYN_RCVD 状态);全连接队列(accept 队列)存放「三次握手已完成、等应用 accept() 取走」的已建立连接(ESTABLISHED)。两个队列都可能溢出——SYN Flood 攻击就是塞爆半连接队列

详细版

两个队列的分工(对照三次握手):

客户端 → SYN ─────────────► 服务端:放入【半连接队列】(SYN_RCVD)
       ← SYN+ACK ──────────
客户端 → ACK ─────────────► 握手完成:从半连接队列移到【全连接队列】(ESTABLISHED)
                            应用 accept() ──► 从全连接队列取走,开始处理
  • 半连接队列(SYN Queue):存「握手进行到一半」的连接。收到第三次 ACK 后,连接从这里移入全连接队列。
  • 全连接队列(Accept Queue):存「握手已完成、等应用取走」的连接。应用调用 accept() 就从这里取一个出来处理。

队列满了会怎样

  • 全连接队列满:新完成握手的连接放不进去,服务端可能丢弃这个连接(或按配置回 RST),客户端以为连上了却收不到响应。常见原因是应用 accept() 太慢(处理不过来)。
  • 半连接队列满:新的 SYN 可能被丢弃,客户端建连失败。SYN Flood 攻击正是利用这点。

完整版教学

一、为什么要两个队列

三次握手是个「两阶段」过程,服务端需要分别管理两种状态的连接:

  • 还没握完手的(收到 SYN、等 ACK)——半成品,可能永远等不到第三次 ACK(客户端掉线或恶意不回),不能和「已完成」的混在一起;
  • 握完手、可以交给应用的——成品,等 accept() 领走。

分成两个队列,就是把「握手中」和「握手完成待处理」两种状态分开管理。连接从半连接队列「毕业」到全连接队列,标志着三次握手成功。

二、SYN Flood:打爆半连接队列的攻击

SYN Flood 是经典的 DoS 攻击,利用的正是半连接队列:

  • 攻击者用伪造的源 IP 疯狂发 SYN;
  • 服务端每收到一个 SYN,就回 SYN+ACK、并在半连接队列里占一个坑,等第三次 ACK;
  • 但源 IP 是伪造的,SYN+ACK 发过去石沉大海,第三次 ACK 永远不来
  • 半连接队列被这些「僵尸半连接」迅速塞满,正常用户的 SYN 进不来,服务不可用。

防御手段

  • SYN Cookie:核心思路是收到 SYN 时不占队列——服务端把连接信息编码进一个「cookie」(放在 SYN+ACK 的序列号里)发回去,自己不保存状态;等第三次 ACK 回来,从 ACK 里的 cookie 还原连接信息再建立。这样半连接队列永远填不满,从根上化解 SYN Flood。
  • 调大半连接队列、缩短 SYN+ACK 重试次数/超时、上层防火墙限速过滤。

三、全连接队列满:一个隐蔽的线上问题

半连接队列问题多与攻击有关,而全连接队列满往往是应用自身的问题,更隐蔽:

  • 全连接队列的容量由 listen()backlog 参数和系统 somaxconn 共同决定(取较小值);
  • 如果应用 accept() 太慢(业务处理阻塞、线程不够),完成握手的连接就在全连接队列里排队,队列很快满;
  • 满了之后,新完成握手的连接被丢弃——客户端那边 TCP 已经 established、以为连上了,发请求却得不到响应,表现为偶发的连接超时/卡顿,很难排查。

排查:Linux 用 ss -lnt 看监听套接字的 Recv-Q(当前全连接队列长度)和 Send-Q(队列上限),Recv-Q 接近 Send-Q 就说明队列在积压。解决靠加快 accept(加线程/优化处理)+ 调大 backlog

四、和三次握手、TIME_WAIT 的关系

这三者都是三次握手/连接管理的细节,串起来看:

  • 半连接/全连接队列:管的是建连过程中连接的暂存;
  • 三次握手(详见那道题):连接从半连接队列走到全连接队列的过程;
  • TIME_WAIT(详见那道题):连接关闭后主动方的等待状态。

一个是「建连时的排队」,一个是「关闭后的等待」,容易混但阶段完全不同。

五、常见误区

  • ❌ 把两个队列搞反——半连接队列存握手中(SYN_RCVD),全连接队列存握手完成待 accept(ESTABLISHED)。
  • ❌ 以为全连接队列满是攻击——通常是应用 accept 太慢,是自身性能问题。
  • ❌ 以为 SYN Flood 靠调大队列就能防住——治标,根治靠 SYN Cookie(不占队列)。
  • ❌ 遇到偶发连接超时不查队列——ss -lnt 看 Recv-Q/Send-Q 是排查全连接队列积压的关键。

六、常见误区与追问

考点正确口径
半连接队列收到 SYN、回 SYN+ACK 后等待最终 ACK
全连接队列三次握手完成,等待 accept 取走
问题定位SYN Flood 多看半连接,全连接满多看应用 accept 能力
SYN -> SYN queue
SYN+ACK sent
ACK -> accept queue
accept() -> application connection

半连接队列卡在握手中途,全连接队列卡在握手完成但应用还没取。

  • 误区:三次握手期间只有一个连接队列。 服务端通常区分半连接队列和全连接队列,阶段和问题都不同。
  • 误区:全连接队列满一定是网络攻击。 也可能是应用 accept 慢、线程池阻塞或后端处理能力不足。
  • 误区:SYN Flood 打的是全连接队列。 典型 SYN Flood 发送大量 SYN 不完成握手,主要消耗半连接队列。
  • 追问:SYN Cookie 解决什么? 在半连接队列压力大时不保存完整状态,把状态编码进序列号,等 ACK 回来再校验。
  • 追问:全连接队列满有什么表现? 客户端可能连接超时、被重置或建立连接慢,服务端 accept queue 指标异常。
  • 追问:如何排查? 结合 backlog、SYN_RECV/ESTABLISHED 状态、accept 速率、应用线程和系统队列指标判断。

七、加强记忆

三次握手时服务端有两个队列:半连接队列(SYN 队列)存握手中的连接(SYN_RCVD,等第三次 ACK),全连接队列(accept 队列)存握手完成待应用 accept 的连接(ESTABLISHED)。SYN Flood 用伪造 IP 塞爆半连接队列,靠 SYN Cookie(不占队列)根治;全连接队列满多因 accept 太慢,用 ss -lnt 看 Recv-Q/Send-Q 排查。