关于TCP 半连接队列和全连接队列

环境centos7内核版本3.10.0-327.el7.x86_64、nginx1.10.3

一、先来回顾下三次握手里面涉及到的问题:

Linux内核协议栈为一个tcp连接管理使用两个队列,一个是半链接队列(用来保存处于SYN_SENT和SYN_RECV状态的请求),一个是accpetd队列(用来保存处于established状态,但是应用层没有调用accept取走的请求)。

1.半连接队列 syn squeue roundup_pow_of_two(max_t(u32,min(somaxconn,sysctl_max_syn_backlog,backlog),8) +1) 用来保存 SYN_SENT 以及 SYN_RECV 的信息。

2.全连接队列 accept queue(min(somaxconn, backlog)), 保存 ESTAB 的状态。

a. tcp_max_syn_backlog参数位于/proc/sys/net/ipv4/tcp_max_syn_backlog,默认是128,可以通过/etc/sysctl.conf文件调整。

b. somaxconn参数位与/proc/sys/net/core/somaxconn,默认是128, 表示最多有129个established链接等待accept。可以通过/etc/sysctl.conf文件调整。

c. backlog参数这个和具体的应用程序有关,比如nginx默认为511

这个可以通过 ss -lnt 的 Send-Q 确认:
State      Recv-Q Send-Q        Local Address:Port          Peer Address:Port
LISTEN     0      511                       *:80                       *:*

可以通过适当的增大 nginx 的 backlog 以及 somaxconn 来增大队列:
listen 80 backlog=1638;

Tomcat默认为100,也可通过server.xml中的<Connector acceptCount="300"/>来调整

ss获取到的 Recv-Q/Send-Q 在 LISTEN 状态以及非 LISTEN 状态所表达的含义是不同的。从 / source/net/ipv4/tcp_diag.c源码中可以看到二者的区别:

LISTEN 状态: Recv-Q 表示的当前等待服务端调用 accept 完成三次握手的 listen backlog 数值,也就是说,当客户端通过 connect() 去连接正在 listen() 的服务端时,这些连接会一直处于这个 queue 里面直到被服务端 accept();Send-Q 表示的则是最大的 listen backlog 数值,这就就是上面提到的 min(backlog, somaxconn) 的值。

其余状态: 非 LISTEN 状态。Recv-Q 表示 receive queue 中的 bytes 数量;Send-Q 表示 send queue 中的 bytes 数值

当用户进程调用服务器程序的listen()时,内核将创建一个队列来存储积压连接。

listen() -> inet_listen() -> inet_csk_listen_start() -> reqsk_queue_alloc()

二、我们先来看半连接数,内核版本为3.10.0-327.el7.x86_64

首先定位到tcp_v4_conn_request函数,        (source/net/ipv4/tcp_ipv4.c)

跟进关键函数inet_csk_reqsk_queue_is_full,(source/include/net/inet_connection_sock.h)

跟进关键函数reqsk_queue_is_full,                (/source/include/net/request_sock.h)

查找qlen和max_qlen_log的定义,                  (/source/include/net/request_sock.h)

关键是如何计算max_qlen_log,                         (/source/net/socket.c)

sock->ops->listen其实是inet_listen,               (/source/net/ipv4/af_inet.c)

跟进inet_csk_listen_start,                                (/source/net/ipv4/inet_connection_sock.c)

跟进reqsk_queue_alloc,                                      (/source/net/core/request_sock.c)

然后我们计算一下为何在Server端的SYN_RECV状态数量会是256

nr_table_entries = listen的第二个参数int backlog ,上限是系统的somaxconn
若 somaxconn = 128,max_syn_backlog = 4096, backlog = 511 则nr_table_entries = 128

nr_table_entries = min_t(u32, nr_table_entries, sysctl_max_syn_backlog);
取两者较小的一个 nr_table_entries = 128

nr_table_entries = max_t(u32, nr_table_entries, 8);
取两者较大的一个 nr_table_entries = 128

nr_table_entries = roundup_pow_of_two(nr_table_entries + 1);   //roundup_pow_of_two - round the given value up to nearest power of two
roundup_pow_of_two(128 + 1) = 256

for (lopt->max_qlen_log = 3;

(1 << lopt->max_qlen_log) < nr_table_entries;

lopt->max_qlen_log++);
max_qlen_log = 8

总结:半连接队列长其实为 half open queue roundup_pow_of_two(max_t(u32,min(somaxconn,sysctl_max_syn_backlog,backlog),8) +1)

判断半连接队列是否满 queue->listen_opt->qlen >> queue->listen_opt->max_qlen_log;
queue->listen_opt->qlen = 256 时reqsk_queue_is_full返回1 , 进入drop
所以queue->listen_opt->qlen 取值 0~255, 因此SYN_RECV状态数量会是 256

三、现在我们再来看看全连接队列

查了nginx文档关于ListenBackLog 指令的说明,默认值是511. 可见最终全连接队列(backlog)应该是net.core.somaxconn = 258.

从/source/net/socket.c源码中也可以看出。

用慢连接攻击测试观察到虚拟机S的80端口ESTABLISHED=1016。基本等于nginx中的work_connections.

现在增加work_connections到1018,慢连接攻击测试观察到虚拟机S的80端口ESTABLISHED=1278(约等于1018+258).

总结,nginx单线程的情况下worker_processes=1,work_connections 约等于1018,

ESTABLISHED基本等于连接数,大于1018的情况下ESTABLISHED的数量要加上backlog;

多线程的情况下ESTABLISHED 2倍的work_connections。至于ESTABLISHED会不会加上backlog,

还是看work_connections是否大于1016。

时间: 2024-08-18 12:03:43

关于TCP 半连接队列和全连接队列的相关文章

关于TCP全连接队列和半连接队列

转:https://www.toutiao.com/a6721163619758768647/ 在TCP的三次握手中存在着两个队列.backlog.tcp_abort_on_overflow等概念知识点.常见的连接服务异常有很多,如Connection refused等问题.通过对这些知识的理解有助于结合一些排查手段有效地解决一些生产上出现的连接服务异常问题.下面将对这些进行讨论分析. 一.TCP三次握手 握手过程 第一次:client发送syn到server进行握手 第二次:server收到s

TCP半连接队列和全连接

概述 如上图所示, 在TCP三次握手中,服务器维护一个半连接队列(sync queue) 和一个全连接队列(accept queue). 当服务端接收到客户端第一次SYN握手请求时,将创建的request_sock结构,存储在半连接队列中(向客户端发送SYN+ACK,并期待客户端响应ACK),此时的连接在服务器端出于SYN_RECV状态.当服务端收到客户端最后的ACK确认时,将半连接中的相应条目删除,然后将相应的连接放入 全连接队列中, 此时服务端连接状态为ESTABLISHED. 进入全连接队

[TimLinux] TCP全连接队列满

0. TCP三次握手 该图来自:TCP SOCKET中backlog参数的用途是什么? syns queue: 半连接队列 accept queue: 全连接队列 控制参数存放在文件:/proc/sys/net/ipv4/tcp_abort_on_overflow中,0:表示如果三次握手第三步的时候全连接队列满了,那么server扔掉client发过来的ack(在server端因为全连接队列满了,认为连接还没有建立起来),1:表示第三步的时候如果全连接队列满了,server发送一个reset包给

Linux 半连接队列,全连接队列

socket 中 listen api中参数backlog指定的是 全队列大小 accept api是从全队列中获取, 没有就阻塞了, 直到有新连接进来. listen中指定的值大小,有一个最大上限, 这个上限是系统内核中设定的. 在配置文件中: /proc/sys/net/core/somaxconn 这个值默认是128. 三次握手: 客户端发一个syn包, 服务器发一个包(syn+ack), 客户端发一个ack确认包. 至此连接完成 半连接是是未完成队列: /proc/sys/net/ipv

tcp的半连接与完全连接队列

队列及参数 server端的半连接队列(syn队列) 在三次握手协议中,服务器维护一个半连接队列,该队列为每个客户端的SYN包开设一个条目(服务端在接收到SYN包的时候,就已经创建了request_sock结构,存储在半连接队列中),该条目表明服务器已收到SYN包,并向客户发出确认,正在等待客户的确认包(会进行第二次握手发送SYN+ACK 的包加以确认).这些条目所标识的连接在服务器处于Syn_RECV状态,当服务器收到客户的确认包时,删除该条目,服务器进入ESTABLISHED状态.该队列为S

tcp的半连接与完全连接队列(二)

队列及参数 server端的半连接队列(syn队列) 在三次握手协议中,服务器维护一个半连接队列,该队列为每个客户端的SYN包开设一个条目(服务端在接收到SYN包的时候,就已经创建了request_sock结构,存储在半连接队列中),该条目表明服务器已收到SYN包,并向客户发出确认,正在等待客户的确认包(会进行第二次握手发送SYN+ACK 的包加以确认).这些条目所标识的连接在服务器处于Syn_RECV状态,当服务器收到客户的确认包时,删除该条目,服务器进入ESTABLISHED状态.该队列为S

(转)tcp的半连接与完全连接队列

队列及参数 tcp-sync-queue-and-accept-queue-small.jpg server端的半连接队列(syn队列) 在三次握手协议中,服务器维护一个半连接队列,该队列为每个客户端的SYN包开设一个条目(服务端在接收到SYN包的时候,就已经创建了request_sock结构,存储在半连接队列中),该条目表明服务器已收到SYN包,并向客户发出确认,正在等待客户的确认包(会进行第二次握手发送SYN+ACK 的包加以确认).这些条目所标识的连接在服务器处于Syn_RECV状态,当服

TCP连接之未连接队列的理解[转]

tcp服务器在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接. 第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认; 第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态; 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入

TCP连接之未连接队列的理解

tcp服务器在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接. 第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认; 第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态; 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入