• linux tcp 半连接队列和全连接队列


    TCP建立连接的“三次握手”过程

    上图就是tcp建联的三次握手过程。

    1. Server端需要先调用bind()方法,绑定ip和端口号,再调用listen()方法,然后就可以等待来自Client连接了
    2. Client 调用connect()后,就会发送SYN包到Server,此时Client端处理SYN_SENT状态
    3. Server收到SYN后,Server进入SYN_RCVD状态,回复SYN + ACK,此时该socket被放入半连接队列(SYN QUEUE)中。(这里要侧重说明一下,这里并不是说从Listen状态变成了SYN_RCVD,而是会生成一个新的(或者复用旧的)socket,新socket状态会变成SYN_RCVD状态,Server端的原来监听的socket状态还依然是LISTEN状态)
    4. Client 收到Server发回的ACK后,会再发送一次ACK到Server,Server收到ACK后三次连接建立,此时会把该socket从半连接队列中取出来放入全连接队列(ACCEPTED QUEUE)中,此时Client与Server端全部处理ESTABLISHED状态
    5. Server端调用accept()方法后,该连接会从全连接队列移出,交给应用层处理。

    什么是“半连接队列(syn queue)" 和 "全连接队列(accept queue)"?

    半连接队列:保存处于SYN_RCVD状态的socket的队列。

    全连接队列:保存处于ESTABLISHED状态的socket的队列。(已经完成三次握手,Server还未调用accept()函数将socket交由应用层处理)

    全连接队列(accept queue)

    全连接对列最大长度计算公式:

    min(net.core.somaxconn, backlog)

    ps:

    • net.core.somaxconn为/proc/sys/net/core/somaxconn
    • backlog,listen(int sockfd, int backlog)函数传入的参数。比如:nginx中的listen指令的backlog参数。例如:'listen 80 backlog=1000;'

    查看全连接队列长度

    注意:连接状态不同,Recv-Q和Send-Q的含义不相同。

    当连接在LISTEN状态下时,Recv-Q表示当前全连接队列的长度, Send-Q表示最大的全连接队列长度

    # ss -lnt

    State       Recv-Q Send-Q  Local Address:Port   Peer Address:Port

    LISTEN      0         32768               *:9999              *:*  

    LISTEN      0         100                 *:8080              *:*  

    LISTEN      0         128                 *:18822             *:*  

    LISTEN      0         50                  *:7879              *:*  

    当连接在非LISTEN状态下时,Recv-Q表示已收到但未被应用进程读取的字节数;Send-Q表示的是已发送但未收到确认的字节数。

    # ss -lnt | grep 80

    State       Recv-Q Send-Q   Local Address:Port   Peer Address:Port

    LISTEN     51        128          *:80                       *:*                 

    LISTEN     0          128         :::80                      :::*                 

    LISTEN     0          128         :::58803                   :::*                 

    # ss -lnt | grep 80

    LISTEN     129    128          *:80                       *:*                 

    LISTEN     0      128         :::80                      :::*                 

    LISTEN     0      128         :::58803                   :::*

    查看全连接队列(accept queue)溢出的统计信息

    # netstat -s | grep overflowed

        172308 times the listen queue of a socket overflowed

    # netstat -s | grep overflowed

        175641 times the listen queue of a socket overflowed

    注意:“172308 ,"175641 "是个累计值,常用后一次与前一次的取差值,若为正,则表示队列溢出。

    全连接队列满了,怎么办?

    当全连接队列满了,linux默认是丢弃连接,还可以通过/proc/sys/net/ipv4/tcp_abort_on_overflow值来控制。

    若为0,则丢弃连接(默认)

    若为1,则server 发送一个 reset 包给 client

    如何调整全连接队列大小?

    因为全连接队列大小=min(/proc/sys/net/core/somaxconn,backlog)。所以调整全连接队列大小时,两个都需要调。

    比如:nginx默认backlog是511,linux 3.10.0内核/proc/sys/net/core/somaxconn 默认是128。

    调整实例:

    echo 2000 > /proc/sys/net/core/somaxconn

    [root@rocketmq-server ipv4]# cat /etc/nginx/nginx.conf | grep listen

            listen       80 default_server backlog=1500;

    [root@rocketmq-server ipv4]# ss -lnt | grep 80

    LISTEN     0      1500         *:80                       *:* 

    所以min(2000,1500)全连接队列变成了1500.

    半连接队列

    半连接队列最大长度计算公式

    很多博客说SYN队列的最大长度是由/proc/sys/net/ipv4/tcp_max_syn_backlog参数指定的。实际上,只有当linux内核版本早于2.6.20时,SYN队列才等于backlog的大小。

    实际上:SYN队列的最大长度由三个参数指定

    • 当你调用listen()时,传入的积压参数backlog
    • /proc/sys/net/core/somaxconn 的默认值为 128
    • /proc/sys/net/ipv4/tcp_max_syn_backlog 的默认值为 1024

    查看当前半连接队列长度(只能粗略估计,SYN-RECV的数量少于实际半连接队列的长度)

    #ss -antp | grep SYN-RECV | wc -l

    查看半连接队列(syn queue)溢出的统计信息

    # netstat -s | grep dropped

        166 SYNs to LISTEN sockets dropped

    注意:“166”是个累计值,常用后一次与前一次的取差值,若为正,则表示队列溢出。

    半连接队列满了,怎么办?

    若半连接队列满了,根据不同情况,对连接可能有不同的处理方式,可能是直接丢弃,也可能是发送reset包

    分如下几种情况:

    1. 如果半连接队列满了,并且没有开启 tcp_syncookies,则会丢弃;
    2. 若全连接队列满了,且没有重传 SYN+ACK 包的连接请求多于 1 个,则会丢弃;
    3. 如果没有开启 tcp_syncookies,并且 max_syn_backlog 减去当前半连接队列长度小于 (max_syn_backlog >> 2),则会丢弃;

    "max_syn_backlog >> 2" :相当于右移2位,即max_syn_backlog / (2*2)

    如何防御syn洪水攻击?

    • 增大半连接队列最大长度 (同时调大:/proc/sys/net/core/somaxconn,/proc/sys/net/ipv4/tcp_max_syn_backlog,listen(int sockfd, int backlog)中的backlog参数<每个服务都不一样>
    • 开启tcp_syncookies功能(echo 1 > /proc/sys/net/ipv4/tcp_syncookies )
    • 减少syn + ack重传次数(echo 1 > /proc/sys/net/ipv4/tcp_synack_retries )

    参考文章:

    4.4 TCP 半连接队列和全连接队列 | 小林coding

    TCP SYN Queue and Accept Queue Overflow Explained - Alibaba Cloud Community

     

  • 相关阅读:
    阿基米德优化算法AOA附Matlab代码
    转行做程序员,从月薪5k到30k,45岁测试员道出了一路的心酸
    Java 线上机器 CPU 100% 的一次排查过程
    可变形卷积(Deformable Convolution)
    如何使用django 结合websocket 进行实时目标检测呢?以yolov5 为例
    《机器学习实战》学习笔记(十一)
    个人和企业如何做跨境电商?用API电商接口教你选平台选品决胜跨境电商
    PD快充芯片IP6566 遥遥领先 集成多种快充协议车充SOC芯片 支持CV/CC模式
    深度之眼(十七)——Python标准库
    算法题:给定一个字符串,字符串中包含一些空格,将字符串中由空格隔开的单词反序,并反转每个字符的大小写。
  • 原文地址:https://blog.csdn.net/zpsimon/article/details/126742354