ZoyaPatel

深入理解 Linux TCP/IP 优化:listen()、半/全连接队列与 TCP_DEFER_ACCEPT 的工作原理

SohaniSharma

 ##本文详细解析了 Linux 服务器端 TCP 监听的完整流程,从 socket()、bind() 到 listen()。重点探讨了 半连接队列 (SYN Queue) 和 全连接队列 (Accept Queue) 的作用,以及 TCP_DEFER_ACCEPT 选项如何修改 listen() 的。{alertInfo}

由人工编写审核,非AI生成内容,请放心观看!

{getToc} $title={文章目录} 


一、Server 端建立 TCP 监听的完整流程(不考虑 DEFER_ACCEPT)

典型服务器端代码:

int sock = socket(AF_INET, SOCK_STREAM, 0);
bind(sock, ...);
listen(sock, backlog);
for (;;) {
    int conn = accept(sock, ...);
    handle(conn);
}

流程图

socket() → bind() → listen() → accept() → read()/write()

listen() 做了什么?

listen() 主要作用:

  1. 让这个 socket 成为“监听 socket”
  2. 内核为此 socket 分配两个队列:
队列名Linux 名称作用
半连接队列SYN queue(syn backlog)存放握手中的连接或未完成条件的连接
全连接队列accept queue存放已准备好可被 accept 的连接

二、没有 TCP_DEFER_ACCEPT 时的行为

客户端访问过程

  1. 客户端发送 SYN
  2. 内核放入 半连接队列 SYN queue
  3. 服务端回复 SYN/ACK
  4. 客户端发 ACK
  5. 内核确认 3 次握手完成,把连接放入: → accept queue
  6. accept() 被唤醒,返回新 FD

流程图:

客户端      服务器
SYN  ───────►  (入 SYN queue)
     ◄───────  SYN/ACK
ACK  ───────►  (握手完成 → accept queue)
                  ↑
             accept() returns

结论:

默认情况下,只要三次握手完成,就会进入 accept queue,accept 会被立即唤醒。

三、开启 TCP_DEFER_ACCEPT 后的行为(关键)

添加操作

listen 前后均可:

int v = 1;
setsockopt(sock, IPPROTO_TCP, TCP_DEFER_ACCEPT, &v, sizeof(v));

行为改变点:

握手完成后,不会加入 accept queue。

流程变为:

  1. SYN → 入 SYN queue
  2. SYN/ACK
  3. ACK → 握手完成
  4. 连接保持在“半完成状态”,不进入 accept queue
  5. 客户端发送数据时: → 内核才把连接移入 accept queue
  6. accept() 才会返回新连接 完整流程:
客户端                 服务器
SYN      ─────────────►  (入 SYN queue)
          ◄─────────────  SYN/ACK
ACK      ─────────────►  (握手完成,等待数据 ✘不进入 accept queue)
DATA     ─────────────►  (收到数据 → 入 accept queue)
                           ↑
                     accept() returns here

四、为什么要这样做?(listen 的意义与 defer 的优化点)

listen 的正常行为(不 defer)

listen 的作用是:

  • 握手完成 → 放到 accept queue → 唤醒 accept() 对于典型 HTTP server,有 70~95% 的连接:
  • “握手结束后立即发送请求”
  • 但内核唤醒 accept() 需要一次调度(上下文切换),即使数据还没来

开启 TCP_DEFER_ACCEPT 后

listen 的行为被修改为:

  • 握手完成不会立即进入 accept queue
  • 必须客户端发送数据才算真正准备好

优势:

  1. 减少 accept() 被无意义唤醒
  2. 减少上下文切换(性能提升)
  3. 减少空连接占用 worker 资源

这是 Nginx、Apache、HAProxy、libevent 等常用的大规模服务器优化手段。

五、总结:listen 与 TCP_DEFER_ACCEPT 的关系

阶段默认行为开启 TCP_DEFER_ACCEPT 后
listen 后队列创建 SYN queue 和 accept queue相同
握手完成后是否进 accept queue立即进入不进入
accept() 是否立即被唤醒不会
什么时候进入 accept queue握手完成收到数据之后
accept() 返回时机握手完成时数据到来时

用一句话总结:

TCP_DEFER_ACCEPT 修改了 listen 的“连接就绪条件”,
从“握手完成”变成“握手完成 + 已有数据到达”。

TCP_DEFER_ACCEPT 的超时机制

开启 TCP_DEFER_ACCEPT 后

setsockopt(sock, IPPROTO_TCP, TCP_DEFER_ACCEPT, &timeout, sizeof(timeout));
  • timeout 参数(Linux 下是秒数)用于设置 内核等待数据到来的最大时间
  • 当开启后,流程变化如下:
  1. listen + SYN 队列 同上。
  2. 三次握手完成
    • 连接完成,但 内核不会立即将其放入 accept 队列
    • 连接处于“半就绪”状态,等待客户端发送应用层数据。
  3. 数据到达
    • 当客户端在连接上发送数据后,内核才会把连接放入 已完成队列,此时 accept() 才会返回。
  4. 超时机制
    • 如果客户端在 timeout 秒内没有发送数据,内核会丢弃连接(类似 SYN_RECV 超时)。

时序图状态的解释

TCP 服务端状态时序图(含 TCP_DEFER_ACCEPT)


版权声明:感谢您的阅读,资源整理自网络,如果您发现任何侵权行为,请联系 理科生网 管理人员,管理员将及时删除侵权内容。否则均为 理科生网 原创内容,转载时请务必以超链接(而非纯文本链接)标注来源于理科生网及本文完整链接,感谢!{alertInfo}

Ahmedabad
Kolkata
Hyderabad
后一页 Bangalore 前一页

Random Manga

Ads

نموذج الاتصال