miller
发布于

linux tcp socket 请求队列大小参数 backlog

底层

backlog 参数主要用于底层方法int listen(int sockfd, int backlog), 在解释 backlog 参数之前,我们先了解下 tcp 在内核的请求过程,其实就是 tcp 的三次握手

1、client 发送 SYN 到 server,将状态修改为 SYN_SEND,如果 server 收到请求,则将状态修改为 SYN_RCVD,并把该请求放到 syns queue 队列中。
2、server 回复 SYN+ACK 给 client,如果 client 收到请求,则将状态修改为 ESTABLISHED,并发送 ACK 给 server。
3、server 收到 ACK,将状态修改为 ESTABLISHED,并把该请求从 syns queue 中放到 accept queue。

在 linux 系统内核中维护了两个队列:syns queue 和 accept queue

syns queue
用于保存半连接状态的请求,其大小通过 / proc/sys/net/ipv4/tcp_max_syn_backlog 指定,一般默认值是 512,不过这个设置有效的前提是系统的 syncookies 功能被禁用。互联网常见的 TCP SYN FLOOD 恶意 DOS 攻击方式就是建立大量的半连接状态的请求,然后丢弃,导致 syns queue 不能保存其它正常的请求。

accept queue
用于保存全连接状态的请求,其大小通过 / proc/sys/net/core/somaxconn 指定,在使用 listen 函数时,内核会根据传入的 backlog 参数与系统参数 somaxconn,取二者的较小值。

如果 accpet queue 队列满了,server 将发送一个 ECONNREFUSED 错误信息 Connection refused 到 client。

应用层

在 netty 实现中,backlog 默认通过 NetUtil.SOMAXCONN 指定。

当然也可以通过 option 方法自定义 backlog 的大小。

backlog 设置注意点

前面已经提到过,内核会根据 somaxconn 和 backlog 的较小值设置 accept queue 的大小,如果想扩大 accept queue 的大小,必须要同时调整这两个参数。

====================================

backlog 参数的含义

TCP 建立连接是要进行三次握手,但是否完成三次握手后,服务器就处理(accept)呢?

backlog 其实是一个连接队列,在 Linux 内核 2.2 之前,backlog 大小包括半连接状态全连接状态两种队列大小。

  • 半连接状态为:服务器处于 Listen 状态时收到客户端 SYN 报文时放入半连接队列中,即 SYN queue(服务器端口状态为:SYN_RCVD)。
  • 全连接状态为:TCP 的连接状态从服务器(SYN+ACK)响应客户端后,到客户端的 ACK 报文到达服务器之前,则一直保留在半连接状态中;当服务器接收到客户端的 ACK 报文后,该条目将从半连接队列搬到全连接队列尾部,即 accept queue (服务器端口状态为:ESTABLISHED)。

在 Linux 内核 2.2 之后,分离为两个 backlog 来分别限制半连接(SYN_RCVD 状态)队列大小和全连接(ESTABLISHED 状态)队列大小。

  • SYN queue 队列长度由 /proc/sys/net/ipv4/tcp_max_syn_backlog 指定,默认为 2048。
  • Accept queue 队列长度由 /proc/sys/net/core/somaxconn 和使用 listen 函数时传入的参数,二者取最小值。默认为 128。在 Linux 内核 2.4.25 之前,是写死在代码常量 SOMAXCONN ,在 Linux 内核 2.4.25 之后,在配置文件 /proc/sys/net/core/somaxconn 中直接修改,或者在 /etc/sysctl.conf 中配置 net.core.somaxconn = 128 。

两个队列在连接过程中所处的位置如下图所示:

tcp_backlog.png

如何查看监听状态

 

Recv-Q & Send-Q

先看下 man netstat 中的说明:

  • Recv-Q:The count of bytes not copied by the user program connected to this socket.
  • Send-Q:The count of bytes not acknowledged by the remote host.

这里有一篇关于这两个队列的分析文章,https://www.cnblogs.com/leezhxing/p/5329786.html,结论是这样,要区分 LISTEN 状态和其他状态:

  • 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 数值。

在我的机器上做了下验证

  • 操作系统
$ uname -a
Linux ubuntu 3.11.0-15-generic #25~precise1-Ubuntu SMP Thu Jan 30 17:39:31 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux

在 8888 这个端口启动一个监听程序, 程序中不 accept

  • netstat 命令查看
$ netstat -apn |grep 8888
tcp        0      0 127.0.0.1:8888          0.0.0.0:*               LISTEN      5399/thundering_her

  • 打开两个 telnet 连接
telnet 127.0.0.1 8888

  • ss 命令查看
$ ss -l |grep 8888
LISTEN      2      50                                       127.0.0.1:8888                                              *:* 

Send-Q 为 2,Recv-Q 为 listen 中设置的值

结论
两个命令中 Send-Q 和 Recv-Q 的含义是不同的,

  • netstat 中应该是 man 里面说明的含义
  • ss 中才是上面文章中的结论:
    1. Recv-Q 表示的当前等待服务端调用 accept 完成三次握手的 listen backlog 数值;
    2. Send-Q 表示 min(backlog, somaxconn)

原文地址 blog.csdn.net

参考案例: https://twitter.com/Manjusaka_Lee/status/1635314090817261569

参考案例2: php ->dubbo ,由于php短连接问题,netty 3 backlog 设置问题

浏览 (420)
点赞
收藏
评论