TCP- 半连接队列 -全连接队列

image.png

1. SYN_REVD, ESTABELLISHED 状态对应的队列

TCP 建立连接时要经过 3 次握手,在客户端向服务器发起连接时,
对于服务器而言,一个完整的连接建立过程,服务器会经历 2 种 TCP 状态:SYN_REVD, ESTABELLISHED。

对应也会维护两个队列:

  1. 一个存放 SYN_REVD状态的连接 队列(半连接队列)
  2. 一个存放ESTABLISHED状态但是还未被accept的连接队列。
    如果一个服务器要处理大量网络连接,且并发性比较高,那么这两个队列长度就非常重要了。因为,即使服务器的硬件配置非常高,服务器端程序性能很好,
    但是这两个队列非常小,那么经常会出现客户端连接不上的现象,
    因为这两个队列一旦满了后,很容易丢包,或者连接被复位。
    所以,如果服务器并发访问量非常高,那么这两个队列的设置就非常重要了。

2. 半连接队列

2.1 半连接队列长度如何计算

半连接队列长度由内核参数 tcp_max_syn_backlog 决定,
当使用 SYN Cookie 时(就是内核参数 net.ipv4.tcp_syncookies = 1),这个参数无效,
半连接队列长度 = min(backlog, 内核参数 net.core.somaxconn,内核参数 tcp_max_syn_backlog),半连接队列的长度肯定小于全连接队列的长度
这个公式实际上规定半连接队列长度不能超过全连接队列长度。

2.2 半连接满之后的动作

首先,全连接满会影响半连接满。全连接满而且半连接中有一定数目处于SYN_REVD状态的连接时,没有必要再继续新的半连接,因为此时全连接已满,此时的动作是直接忽略该连接。
半连接满了之后的动作是直接忽略(ignore or dropped),此时客户端需要不断的重发SYNC进行重试,重试的参数由tcp_syn_retries决定,该参数默认是5。如果超过客户端设置的超时时间,会报连接超时异常


2.3 syn flood攻击

客户端发出SYNC之后,不响应ACK,此时造成半连接队列满,server不能再提供服务,正常的客户端一直报连接超时。
为了应对该攻击,有两种办法:

  1. tcp_syncookies
    不建议该方案,因为违背了TCP规范
  2. 其他参数
    2.1 tcp_synack_retries 减小重传次数
    2.2 tcp_max_syn_backlog 调整半连接队列大小

2.4 怎么监控半连接满了

667399 SYNs to LISTEN sockets ignored表明已经忽略SYN次了,此时说明半连接队列满了,或者因为全连接满而影响了半连接的进行。

[root@server ~]#  netstat -s | egrep "listen|LISTEN" 
667399 times the listen queue of a socket overflowed
667399 SYNs to LISTEN sockets ignored

3. 全连接队列

3.1 全连接队列长度如何计算

全连接队列长度 = min(backlog, 内核参数 net.core.somaxconn)net.core.somaxconn 默认为 128
这个很好理解,net.core.somaxconn 定义了系统级别的全连接队列最大长度,
backlog 只是应用层传入的参数,不可能超过内核参数,所以 backlog 必须小于等于 net.core.somaxconn。

3.1.1 backlog-应用参数

对于 Linux 而言,基本上任意语言实现的通信框架或服务器程序在构造 socket server 时,都提供了 backlog 这个参数,
因为在监听端口时,都会调用系统底层 API: int listen(int sockfd, int backlog);
listen 函数中 backlog 参数的定义如下:

Now it specifies the queue length for completely established sockets waiting to be accepted,
instead of the number of incomplete connection requests.
The maximum length of the queue for incomplete sockets can be set using the tcp_max_syn_backlog sysctl.
When syncookies are enabled there is no logical maximum length and this sysctl setting is ignored.
If the socket is of type AF_INET, and the backlog argument is greater than the constant SOMAXCONN(128 default),
it is silently truncated to SOMAXCONN.
backlog 参数描述的是服务器端 TCP ESTABELLISHED 状态对应的全连接队列长度。

3.1.2 net.core.somaxconn-内核参数

cat /proc/sys/net/core/somaxconn或者sysctl -a | grep "net.core.somaxconn"
线上机器(LINK)的运行结果如下:


 
image.png

 
image.png

3.2 查看全连接长度

Recv-Q:全连连队列中数据的个数,也就是等待被accept的个数。
Send-Q:全连接队列长度


 
image.png

3.3 全连接队列满之后的动作

  1. 全连接队列满了之后,收到第三次握手的ACK之后首先根据tcp_abort_on_overflow来执行满之后的的动作,该参数默认为0。
    0表示:如果三次握手第三步的时候全连接队列满了那么server扔掉client 发过来的ack(在server端认为连接还没建立起来)
    1表示:第三步的时候如果全连接队列满了,server发送一个reset包给client,表示废掉这个握手过程和这个连接(本来在server端这个连接就还没建立起来)。
  2. 如果tcp_abort_on_overflow为0,则srver直接忽略收到的ACK,之后server端会不断的重发SYNC+ACK。其中tcp_synack_retries参数决定了重试几次才放弃。tcp_synack_retries的默认值是5,重试的间隔时间从1s开始,下次的重试间隔时间是前一次的双倍,5次的重试时间间隔为1s, 2s, 4s, 8s, 16s,总共31s,第5次发出后还要等32s都知道第5次也超时了,所以,总共需要 1s + 2s + 4s+ 8s+ 16s + 32s = 63s,TCP才会把断开这个连接。
    如果client走完第三步在client看来连接已经建立好了,但是server上的对应连接实际没有准备好,这个时候如果client发数据给server,server会怎么处理呢?(有同学说会reset,还是实践看看)
    先来看一个例子:
     
    image.png

    如上图,150166号包是三次握手中的第三步client发送ack给server,然后150167号包中client发送了一个长度为816的包给server,因为在这个时候client认为连接建立成功,但是server上这个连接实际没有ready,所以server没有回复,一段时间后client认为丢包了然后重传这816个字节的包,一直到超时,client报连接异常并主动发fin包断开该连接。
    这个问题也叫client fooling,可以看这里:https://github.com/torvalds/linux/commit/5ea8ea2cb7f1d0db15762c9b0bb9e7330425a071
    从上面的实际抓包来看不是reset,而是server忽略这些包,然后client重传,一定次数后client认为异常,然后断开连接。
  3. 如果tcp_abort_on_overflow为1,则直接响应RST信号,此时客户端报connection reset by peer。

3.4 怎么监控全连接队列满了

  1. 通过netstat
    667399 times the listen queue of a socket overflowed 说明出现了667399次全连接满的情况
[root@server ~]#  netstat -s | egrep "listen|LISTEN" 
667399 times the listen queue of a socket overflowed
667399 SYNs to LISTEN sockets ignored
  1. 通过ss
    Recv-Q表明了当前当前全连接队列使用了11,超过了最大值10(Send-Q所指示的)
Fri May  5 13:50:23 CST 2017
Recv-Q Send-QLocal Address:Port  Peer Address:Port
11         10         *:3306               *:*

4 参数

  1. tcp_max_syn_backlog
  2. net.core.somaxconn
  3. backlog(应用层传入),java默认为50,LINK线上机器默认为128。
  4. tcp_syncookies
  5. tcp_synack_retries
  6. tcp_abort_on_overflow
  7. tcp_syn_retries

5. 容器中的全连接队列参数

tomcat默认短连接,backlog(在Tomcat里面的术语是Accept count)默认100.

#ss -lnt
Recv-Q Send-Q   Local Address:Port Peer Address:Port
0       100                 *:8080            *:*

Nginx默认是511

$sudo ss -lnt
State  Recv-Q Send-Q Local Address:PortPeer Address:Port
LISTEN    0     511              *:8085           *:*
LISTEN    0     511              *:8085           *:*

因为Nginx是多进程模式,也就是多个进程都监听同一个端口以尽量避免上下文切换来提升性能



 

 

posted on 2021-05-19 17:10  tycoon3  阅读(964)  评论(0编辑  收藏  举报

导航