本文详细介绍了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版本号之前,没有办法设置队列大小。
尽管如此,还是有一些正当的理由来解释为什么必须扩大队列的大小:
然而,将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()会发生什么?
当这种情况发生时,大家只能寄希望于程序流的解特征能在后面恢复,手机客户端再推送一次被服务器丢失的包。
内核的这种主要性能对于大多数服务项目来说是可以接受的。顺带一提,这种主要性能是可以通过调整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有什么伤害。然后你就可以知道是什么进程在失去连接。
文章里的内容就这些了。期待对大家的学习和培训有所帮助,也期待大家的应用。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)