本文是对 从一次线上问题说起,详解 TCP 半连接队列、全连接队列 这篇文章的实验复现和总结,借此加深对 TCP 半连接队列、全连接队列的理解。
实验环境
全连接队列实战
全连接队列长度控制
TCP 半连接队列的长度计算公式为:
min(somaxconn, backlog)
- somaxconn 是系统内核参数
net.core.somaxconn
,默认值为 4096 - backlog 是 TCP 的 listen 函数
int listen(int sockfd, int backlog)
的 backlog 参数。在 Golang 中使用的就是 net.core.somaxconn 的值。
下面我们修改 somaxconn 的值,启动服务后使用 ss -lnt
命令查看全连接队列的长度。下面是实验代码:
|
|
首先我们修改 somaxconn 为 128:
|
|
启动服务后查看全连接队列的长度:
|
|
这里简单解释下 ss 命令输出的含义:
- 对于 Listen 状态的 socket,Recv-Q 表示当前全连接队列的长度,也就是已经完成三次握手,等待应用层调用 accept 的 TCP 连接;Send-Q 表示全连接队列的最大长度。
- 对于非 Listen 状态的 socket,Recv-Q 表示已经收到但尚未被应用读取的字节数;Send-Q 表示已发送但尚未收到确认的字节数。
再次修改 somaxconn 为 1024 重启服务后,查看全连接队列的长度:
|
|
全连接队列溢出
下面我们让服务端只 Listen 端口但不执行 accept() 处理连接,看看全连接队列溢出时会发生什么。
- 服务端代码
|
|
- 客户端代码
|
|
为了验证全队列溢出,我们先将全连接队列的最大长度设置为 5:
|
|
运行服务端和客户端后,实验结果付下:
|
|
之所以这样做,是为了避免在 backlog 设置为 0 时,依然可以有一个连接进入全连接队列,具体可以查看以下 commit 信息:
|
|