TCP socket SYN队列和Accept队列区别原理解析_服务器其它

来源:脚本之家  责任编辑:小易  

首先我们必须明白,处于“LISTENING”状态的TCP socket,有两个独立的队列:

SYN队列(SYN Queue) Accept队列(Accept Queue)

这两个术语有时也被称为“reqsk_queue”,“ACK backlog”,“listen backlog”,甚至“TCP backlog”,但是这篇文章中我们使用上面两个术语以免造成混淆。

SYN队列

SYN队列存储了收到SYN包的连接(对应内核代码的结构体:struct inet_request_sock)。它的职责是回复SYN+ACK包,并且在没有收到ACK包时重传,直到超时。在Linux下,重传的次数为:

$ sysctl net.ipv4.tcp_synack_retries

net.ipv4.tcp_synack_retries = 5

文档中对tcp_synack_retries的描述如下:

 tcp_synack_retries - int整型
 对于一个被动TCP连接,重传SYNACKs的次数。该值不能超过255。
 默认值为5,如果初始RTO是1秒,那么对应的最后一次重传是31秒。
 对应的最后一次超时是63秒之后。

发送完SYN+ACK之后,SYN队列等待从客户端发出的ACK包(也即三次握手的最后一个包)。当收到ACK包时,首先找到对应的SYN队列,再在对应的SYN队列中检查相关的数据看是否匹配,如果匹配,内核将该连接相关的数据从SYN队列中移除,创建一个完整的连接(对应内核代码的结构体:struct inet_sock),并将这个连接加入Accept队列。

Accept队列

Accept队列中存放的是已建立好的连接,也即等待被上层应用程序取走的连接。当进程调用accept(),这个socket从队列中取出,传递给上层应用程序。

这就是Linux处理SYN包的一个简单描述。顺便一提,当socket开启了TCP_DEFER_ACCEPT和TCP_FASTOPEN时,工作方式将会有细微不同,本文不做介绍。

队列大小限制

应用程序通过调用系统调用listen(2),传入backlog参数,来设置SYN队列和Accept队列的最大大小。比如下面这样,将SYN队列和Accept队列的最大大小同时设置为1024:

listen(sfd, 1024)

注意,在4.3版本之前的内核,SYN队列的大小是用另一种方式计算。

SYN队列的最大大小以前是用net.ipv4.tcp_max_syn_backlog来配置,但是现在已经不再使用了。现在用net.core.somaxconn来同时表示SYN队列和Accept队列的最大大小。在我们的服务器上,我们将它设置为16k:

$ sysctl net.core.somaxconn

net.core.somaxconn = 16384

知道了上面这些信息后,你可能会问,队列设置为多大合适?队列设置为多大合适

答案是:看情况。对于大多数的TCP服务来说,这并不太重要。比如,Go语言1.11版本之前,并没有提供设置队列大小的方法。

尽管如此,也存在一些合理的原因,需要增大队列的大小:

当建立连接的请求速度确实很大时,即使是对于一个高性能的服务来说,SYN队列也可能需要设置的大一些。 SYN队列的大小,换言之就是等待ACK包的连接数。也即与客户端的平均往返时间越大,堆积在SYN队列中的连接就越多。对于那些大部分客户端都距离服务器很远的场景,比如说往返时间几百毫秒以上,可以将队列大小设置的大一些。 TCP_DEFER_ACCEPT选项如果打开了,会导致socket在SYN-RECV状态下维持更长的时间,也即增大了处于SYN队列中的时间。

但是,将backlog设置的过大也会带来不好的影响:SYN队列中的每一个槽位都需要占用一些内存。当遇到SYN Flood攻击时,我们没有必要为这些发起攻击的包浪费资源。SYN队列中的inet_request_sock结构体,在4.14内核下,每个将占用256字节的内存。

linux下,如果想查看SYN队列的当前状态,我们可以使用ss命令来查询SYN-RECV状态的socket。比如如下执行结果,表示80端口的SYN队列中当前有119个元素,443端口则为78。

 $ ss -n state syn-recv sport = :80 | wc -l
 119
 $ ss -n state syn-recv sport = :443 | wc -l
 78

假如程序调用accept()不够快?还可以通过我们的SystemTap脚本来观察这个数据:resq.stp

如果程序调用accept()不够快会发生什么呢?

后续收到的SYN包,不会被SYN队列处理 后续收到的(用于建立连接的)ACK包,不会被SYN队列处理 TcpExtListenOverflows / LINUX_MIB_LISTENOVERFLOWS计数增加 TcpExtListenDrops / LINUX_MIB_LISTENDROPS计数增加

发生这种情况时,我们只能寄希望于程序的处理性能稍后能恢复正常,客户端重新发送被服务端丢弃的包。

内核的这种表现对于大部分服务来说是可接受的。顺便一提,可以通过调整net.ipv4.tcp_abort_on_overflow这个全局参数来修改这种表现,但是最好还是不要改这个参数。

可以通过查看nstat的计数来观察Accept队列溢出的状态:

 $ nstat -az TcpExtListenDrops
 TcpExtListenDrops 49199 0.0

但是这是一个全局的计数。观察起来不够直观,比如有时我们观察到它在增长,但是所有的服务程序看起来都是正常的。此时我们可以使用ss命令来观察单个监听端口的Accept队列大小:

 $ ss -plnt sport = :6443|cat
 State Recv-Q Send-Q Local Address:Port Peer Address:Port
 LISTEN 0 1024 *:6443 *:*

Recv-Q这一列显示的是处于Accept队列中的socket数量,Send-Q显示的是队列的最大大小。在上面的例子中,我们发现并没有未被程序accept()的socket,但是我们依然发现ListenDrops计数在增长。

这是因为我们的程序只是周期性的短暂卡住不处理新的连接,而非永久性的不处理,过段时间程序又恢复了正常。这种情况下,用ss命令比较难观察这种现象,因此我们写了一个SystemTap脚本,它会hook进内核,把被丢弃的SYN包打印出来:

$ sudo stap -v acceptq.stp
time (us)    acceptq qmax local addr  remote_addr
1495634198449075 1025  1024 0.0.0.0:6443 10.0.1.92:28585
1495634198449253 1025  1024 0.0.0.0:6443 10.0.1.92:50500
1495634198450062 1025  1024 0.0.0.0:6443 10.0.1.92:65434
...

通过上面的操作,可以观察到哪些SYN包被ListenDrops影响了。从而我们也就可以知道哪些程序在丢失连接。


  • 本文相关:
  • 在mac上安装虚拟机搭载windows服务的方法
  • zabbix 上windows性能监控的方法
  • 在windows平台下安装与配置memcached的方法分享
  • n点主机管理系统的重装步骤(图文)
  • windows系统搭建zookeeper服务器的教程
  • tortoisesvn使用方法_动力节点java学院整理
  • 树莓派 msmtp和mutt 的安装和配置教程
  • hadoop ssh免密码登录以及失败解决方案
  • 使用.htaccess设置图片防盗链的详细方法
  • 基于xen的vps 配置squid服务器
  • tcp服务器怎么处理好socket和消息队列的关系
  • tcp服务器怎么处理好socket和消息队列的关系
  • 怎么建立一个SOCKET侦听队列
  • C++ socket listen() 第二个参数的问题
  • 如何通过socket消息队列机制实现同步非阻塞与异步...
  • 如何通过socket消息队列机制实现同步非阻塞与异步阻塞
  • linux socket 怎么处理大量的数据
  • java ActiveMq与Tcp协议相关问题 ActiveMq怎样直接...
  • 请教一个uWSGI socket队列的问题
  • tcp服务器怎么处理好socket和消息队列的关系
  • 网站首页网页制作脚本下载服务器操作系统网站运营平面设计媒体动画电脑基础硬件教程网络安全星外虚拟主机华众虚拟主机linuxwin服务器ftp服务器dns服务器tomcat nginxzabbix云和虚拟化服务器其它首页服务器服务器其它基于python模拟tcp3次握手连接及发送数据tcp/ip协议中三次握手四次挥手的原理及流程分析tcp的三次握手与四次挥手详细介绍wireshark基本介绍和学习tcp三次握手tcp三次握手及原理java基于tcp协议socket网络编程的文件传送的实现tcp性能调优实现原理及过程解析java 基于tcp协议实现文件上传tcp第三次握手传数据过程图解在mac上安装虚拟机搭载windows服务的方法zabbix 上windows性能监控的方法在windows平台下安装与配置memcached的方法分享n点主机管理系统的重装步骤(图文)windows系统搭建zookeeper服务器的教程tortoisesvn使用方法_动力节点java学院整理树莓派 msmtp和mutt 的安装和配置教程hadoop ssh免密码登录以及失败解决方案使用.htaccess设置图片防盗链的详细方法基于xen的vps 配置squid服务器rsync 常见错误与解决方法整理git 常用命令速查表(图文+表格)raid教程 全程图解手把手教你做rgit客户端tortoisegit(windows系服务器共享文件夹设置软件、文件用两块硬盘组建raid0磁盘阵列简单正确开启win2008远程桌面的方法hp ilo2 使用详细教程[图文]502 bad gateway是什么意思 502 nas(synology 群晖)首次使用教svn无法提交出现 can""t set file "/db/twin2008 蓝屏漏洞揭秘centos svn服务器如何管理多个项目如何让phpnow支持wordpress静态化链接的方memcached常用命令_动力节点java学院整理远程桌面超出最大连接数的解决方法rsync相对于其他同步工具的优缺点介绍nats服务器配置的详细介绍游戏服务器开发的基本体系与服务器端开发集中化管理平台ansible详解
    免责声明 - 关于我们 - 联系我们 - 广告联系 - 友情链接 - 帮助中心 - 频道导航
    Copyright © 2017 www.zgxue.com All Rights Reserved