关于TCP 半连接队列和全连接队列

来源:互联网 发布:淘宝客seo推广教程 编辑:程序博客网 时间:2024/05/16 04:21
转载自:http://jm.taobao.org/2017/05/25/525-1/

问题描述
JAVA的client和server,使用socket通信。server使用NIO。
    1.间歇性的出现client向server建立连接三次握手已经完成,但server的selector没有响应到这连接。
    2.出问题的时间点,会同时有很多连接出现这个问题。
    3.selector没有销毁重建,一直用的都是一个。
    4.程序刚启动的时候必会出现一些,之后会间歇性出现。

分析问题
正常TCP建连接三次握手过程:
    第一步:client 发送 syn 到server 发起握手;
    第二步:server 收到 syn后回复syn+ack给client;
    第三步:client 收到syn+ack后,回复server一个ack表示收到了server的syn+ack。

       从问题的描述来看,有点像TCP建连接的时候全连接队列(accept队列)满了,尤其是症状2、4。可以通过 ss -s 去看队列的溢出统计数据,反复看了几次之后发现socket overflowed一直在增加,那么可以明确的是server上全连接队列一定溢出了。
       接着查看溢出后,OS怎么处理:
# cat /proc/sys/net/ipv4/tcp_abort_on_overflow0
       tcp_abort_on_overflow 为0表示如果三次握手第三步的时候全连接队列满了,那么server扔掉client 发过来的ack(在server端认为连接还没建立起来)。
       为了证明客户端应用代码的异常跟全连接队列满有关系,先把tcp_abort_on_overflow修改成 1,1表示第三步的时候如果全连接队列满了,server发送一个reset包给client,表示废掉这个握手过程和这个连接(本来在server端这个连接就还没建立起来)。接着测试然后在客户端异常中可以看到很多connection reset by peer的错误,到此证明客户端错误是这个原因导致的。
       java 源代码中socket 默认的backlog(这个值控制全连接队列的大小)是50,于是改大重新跑,经过12个小时以上的压测,这个错误一次都没出现过,同时 overflowed 也不再增加了。
       到此问题解决,简单来说TCP三次握手后有个accept队列,进到这个队列才能从Listen变成accept,默认backlog 值是50,很容易就满了。满了之后握手第三步的时候server就忽略了client发过来的ack包(隔一段时间server重发握手第二步的syn+ack包给client),如果这个连接一直排不上队就异常了。


深入理解TCP握手过程中建连接的流程和队列


       如上图所示,这里有两个队列:syns queue(半连接队列);accept queue(全连接队列)。
       三次握手中,在第一步server收到client的syn后,把相关信息放到半连接队列中,同时回复syn+ack给client(第二步),此时server处于SYN_RCVD状态,比如syn floods攻击就是针对半连接队列的,攻击方不停地建连接,但是建连接的时候只做第一步,第二步中攻击方收到server的syn+ack后故意扔掉什么也不做,导致server上这个队列满其它正常请求无法进来;syns queue的长度由/proc/sys/net/ipv4/tcp_max_syn_backlog设置。
       第三步的时候server收到client的ack,如果这时全连接队列没满,那么从半连接队列拿出相关信息放入到全连接队列中,表示连接建立,进入ESTABLISHED状态,否则按tcp_abort_on_overflow指示的执行。这时如果全连接队列满了并且tcp_abort_on_overflow是0的话,server过一段时间再次发送syn+ack给client(也就是重新走握手的第二步),如果client超时等待比较短,就很容易异常了;accept queue的长度取值min(backlog, somaxconn)


如果TCP连接队列溢出,有哪些指标可以看呢?

netstat –s
[root@server ~]#  netstat -s | egrep "listen|LISTEN" 667399 times the listen queue of a socket overflowed667399 SYNs to LISTEN sockets ignored
       比如上面看到的 667399 times ,表示全连接队列溢出的次数,隔几秒钟执行下,如果这个数字一直在增加的话肯定全连接队列偶尔满了。

ss 
[root@server ~]# ss -lntRecv-Q Send-Q Local Address:Port  Peer Address:Port 0        50               *:3306             *:*
       上面看到的第二列Send-Q 表示第三列的listen端口上的全连接队列最大为50,第一列Recv-Q为全连接队列当前使用了多少
       全连接队列的大小取决于:min(backlog, somaxconn) . backlog是在socket创建的时候传入的,somaxconn是一个os级别的系统参数/proc/sys/net/core/somaxconn。
       半连接队列的大小取决于:max(64, /proc/sys/net/ipv4/tcp_max_syn_backlog)。 不同版本的os会有些差异。




原创粉丝点击