TCP socket SYN队列和Accept队列区别原理解析

TCP socket SYN队列和Accept队列区别原理解析,第1张

TCPsocketSYN队列和Accept队列区别原理解析

本文详细介绍了TCP套接字SYN队列与接受队列区别的原理分析。原文根据示例代码非常详细,对大家的学习培训或者工作都有一定的参考价值,朋友们一定要参考。

首先,我们必须清楚,在“监听”情况下,TCP套接字有两个单独的队列:

  • 同步队列(同步队列)
  • 接受队列(接受队列)
  • 这两个技术术语有时被称为“reqsk_queue”、“ACKbacklog”、“listenbacklog”甚至“TCPbacklog”,但我们在本文中应该使用以上两个技术术语,以免混淆。

    SYN队列

    SYN队列存储到SYN包的连接(与内核代码匹配的构建结构:structinet_request_sock)。它的工作职责是响应SYNACK包,并在请求超时之前没有收到ACK包时重新传输它们。在Linux下,重新传输的频率是:

    $sysctlnet.IPv4.TCP_synack_retries

    net.ipv4.tcp_synack_retries=5

    文档中对tcp_synack_retries的描述如下:

    Tcp_synack_retries-intshaping
    被动TCP连接重新传输synack的频率。该值不能超过255。
    初始值为5。如果原始RTO是1秒,则最后匹配的重传是31秒。
    最后一个匹配请求的超时时间是63秒之后。

    推送SYNACK后,SYN队列等待来自移动客户端的ACK包(即三次握手的最后一个包)。当收到一个ACK包时,首先寻找匹配的SYN队列,然后检查匹配的SYN队列中的相关数据信息,看是否匹配。如果匹配,内核将从SYN队列中清除与连接相关的数据信息,建立一个详细的连接(结构匹配内核代码:structinet_sock),并将这个连接添加到接受队列中。

    接受队列

    存储在接受队列中的是创建的连接,即等待顶层应用软件取走的连接。当accept()被启用时,该套接字从队列中移除并被发送到顶层应用软件。

    这是Linux如何解决SYN包的简单描述。顺带一提,socket打开TCP_DEFER_ACCEPT和TCP_FASTOPEN时,工作模式会略有不同,本文不做详细描述。

    队列大小限制

    应用软件通过使能系统进程监听(2),根据传送给backlog主要参数设置较大的SYN队列和接受队列。例如,如下所示,较大的SYN队列和Accept队列被额外设置为1024:

    听(sfd,1024)

    注意,在4.3版之前的内核中,SYN队列的大小是用另一种方法来衡量的。

    更大尺寸的SYN队列曾经配备了net.ipv4.tcp_max_syn_backlog,现在已经不用了。现在用net.core.somaxconn来表示SYN队列和Accept队列更大的尺寸。在您的web服务器上,您将其设置为16k:

    $sysctlnet.core.somaxconn

    net.core.somaxconn=16384

    知道了以上信息,你大概会问,队列设置多少钱合适?队列的长度是多少?

    回答:看情况。对于大多数TCP服务来说,这并不重要。比如在Go语言1.11版本号之前,没有办法设置队列大小。

    尽管如此,还是有一些正当的理由来解释为什么必须扩大队列的大小:

  • 当所需的连接创建速率非常高时,即使对于一个性能非常好的服务项目,SYN队列也可能必须设置得更大。
  • SYN队列的大小,换句话说,就是等待ACK包的连接数。也就是说,与移动客户端的平均往返时间越长,存放在SYN队列中的连接就越多。针对大部分手机客户端距离网络服务器太远的情况,比如几百ms的往返时间,可以将队列大小设置的更大。
  • 如果TCP_DEFER_ACCEPT选项打开,会导致socket在SYN-RECV情况下停留的时间更长,也就是说会延长在SYN队列中的时间。
  • 然而,将backlog设置得太大也会带来危害:SYN队列中的每个槽都必须占用一些运行内存。当遇到SYNFlood攻击时,不需要为此攻击包破坏环境。SYN队列中的inet_request_sock架构,在4.14内核下,每个会占用256字节的运行内存。

    在linux下,如果要查询SYN队列的当前情况,可以使用ss指令检查SYN-RECV的套接字。例如,以下实现结果显示,端口80的SYN队列中有119个元素,端口443中有78个元素。

    $ss-n州syn-recvsport=:80|WC-l
    119
    $ss-n州syn-recvsport=:443|WC-l
    78

    如果程序进程启动accept()不够快怎么办?也可以根据大家的SystemTap脚本:resq.stp来观察这些数据信息

    如果程序进程没有足够快地启用accept()会发生什么?

  • 之后收到的SYN包不容易被SYN队列解决。
  • 收到的ACK包(用于创建连接)不容易被SYN队列解决。
  • tcpextlisnovelflows/Linux_MIB_listenoverflows计数改进
  • tcpextlistendrops/Linux_MIB_listendrops计数改进
  • 当这种情况发生时,大家只能寄希望于程序流的解特征能在后面恢复,手机客户端再推送一次被服务器丢失的包。

    内核的这种主要性能对于大多数服务项目来说是可以接受的。顺带一提,这种主要性能是可以通过调整net.ipv4.tcp_abort_on_overflow的全局主参数来改变的,但是这个主参数还是不改变的好。

    根据查询nstat的计数,可以观察到接受队列溢出:

    $nstat-azTcpExtListenDrops
    TcpExtListenDrops491990.0

    但这是一个全球性的统计。看起来不够直观。比如有时候大家看到在改善,但是系统服务看起来都很正常。这时,我们可以使用ss指令来观察接受队列的大小,以便单独监控端口号:

    $ss-plntsport=:6443|cat
    StateRecv-QSend-Q本地地址:port对等地址:Port
    listEN01024*:6443*:*

    Recv-Q列显示了接受队列中套接字的总数,Send-Q列显示了更大的队列。在上面的例子中,我们发现没有一个套接字没有被程序流接受(),但是我们仍然发现ListenDrops的计数在提高。

    这是因为每个人的程序流只是有规律的,短暂的,并没有解决新的连接,不是永久的。一段时间后,程序流修复了一切。在这种情况下,用ss指令很难观察到这种情况,于是大家写了一个SystemTap脚本,它会挂钩到内核中,把丢失的SYN包复制出来:

    $sudostap-vacceptq.stp time(us)acceptqqmaxlocaladdrremote_addr 1495634198449075102510240.0.0.0:644310.0.1.92:28585 1495634198449253102510240.0.0.0:644310.0.1.92:50500 1495634198450062102510240.0.0.0:644310.0.1.92:65434 ...

    根据上面的实际 *** 作,我们可以看到ListenDrops对SYNhug有什么伤害。然后你就可以知道是什么进程在失去连接。

    文章里的内容就这些了。期待对大家的学习和培训有所帮助,也期待大家的应用。

    欢迎分享,转载请注明来源:内存溢出

    原文地址: http://outofmemory.cn/zz/774028.html

    (0)
    打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
    上一篇 2022-05-03
    下一篇 2022-05-03

    发表评论

    登录后才能评论

    评论列表(0条)

    保存