微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > Linux协议栈accept和syn队列问题

Linux协议栈accept和syn队列问题

时间:10-08 来源:互联网 点击:

环境:

Client 通过tcp 连接server,server端只是listen,但是不调用accept。通过netstat –ant查看两端的连接情况。

server端listen,不调用accept。

client一直去connect server。

问题:

运行一段时间后,为什么server端的ESTABLISHED连接的个数基本是固定的129个,但是client端的ESTABLISHED连接的个数却在不断增加?

分析

Linux内核协议栈为一个tcp连接管理使用两个队列,一个是半链接队列(用来保存处于SYN_SENT和SYN_RECV状态的请求),一个是accpetd队列(用来保存处于established状态,但是应用层没有调用accept取走的请求)。

第一个队列的长度是/proc/sys/net/ipv4/tcp_max_syn_backlog,默认是1024。如果开启了syncookies,那么基本上没有限制。

第二个队列的长度是/proc/sys/net/core/somaxconn,默认是128,表示最多有129个established链接等待accept。(为什么是129?详见下面的附录1)。

现在假设acceptd队列已经达到129的情况:

client发送syn到server。client(SYN_SENT),server(SYN_RECV)

server端处理流程:tcp_v4_do_rcv--->tcp_rcv_state_process--->tcp_v4_conn_request

if(sk_acceptq_is_full(sk) inet_csk_reqsk_queue_yong(sk)>1)

goto drop;

inet_csk_reqsk_queue_yong(sk)的含义是请求队列中有多少个握手过程中没有重传过的段。

在第一次的时候,之前的握手过程都没有重传过,所以这个syn包server端会直接drop掉,之后client会重传syn,当inet_csk_reqsk_queue_yong(sk) 1,那么这个syn被server端接受。server会回复synack给client。这样一来两边的状态就变为client(ESTABLISHED), server(SYN_SENT)

Client收到synack后回复ack给server。

server端处理流程: tcp_check_req--->syn_recv_sock-->tcp_v4_syn_recv_sock

if(sk_acceptq_is_full(sk)

goto exit_overflow;

如果server端设置了sysctl_tcp_abort_on_overflow,那么server会发送rst给client,并删除掉这个链接;否则server端只是记录一下LINUX_MIB_LISTENOVERFLOWS(详见附录2),然后返回。默认情况下是不会设置的,server端只是标记连接请求块的acked标志,之后连接建立定时器,会遍历半连接表,重新发送synack,重复上面的过程(具体的函数是inet_csk_reqsk_queue_prune),如果重传次数超过synack重传的阀值(/proc/sys/net/ipv4/tcp_synack_retries),会把该连接从半连接链表中删除。

一次异常问题分析

Nginx通过FASTCGI协议连接cgi程序,出现cgi程序read读取socket内容的时候永远block。通过netstat查看,cgi程序所在的服务器上显示连接存在,但是nginx所在的服务器上显示不存在该连接。

下面是原始数据图:

我们从上面的数据流来分析一下:

出现问题的时候,cgi程序(tcp server端)处理非常慢,导致大量的连接请求放到accept队列,把accept队列阻塞。

148021 nginx(tcp client端) 连接cgi程序,发送syn

此时server端accpet队列已满,并且inet_csk_reqsk_queue_yong(sk) > 1,server端直接丢弃该数据包

148840 client端等待3秒后,重传SYN

此时server端状态与之前送变化,仍然丢弃该数据包

150163 client端又等待6秒后,重传SYN

此时server端accept队列仍然是满的,但是存在了重传握手的连接请求,server端接受连接请求,并发送synack给client端(150164)

150166 client端收到synack,标记本地连接为ESTABLISHED状态,给server端应答ack,connect系统调用完成。

Server收到ack后,尝试将连接放到accept队列,但是因为accept队列已满,所以只是标记连接为acked,并不会将连接移动到accept队列中,也不会为连接分配sendbuf和recvbuf等资源。

150167 client端的应用程序,检测到connect系统调用完成,开始向该连接发送数据。

Server端收到数据包,由于acept队列仍然是满的,所以server端处理也只是标记acked,然后返回。

150225 client端由于没有收到刚才发送数据的ack,所以会重传刚才的数据包

150296 同上

150496 同上

150920 同上

151112 server端连接建立定时器生效,遍历半连接链表,发现刚才acked的连接,重新发送synack给client端。

151113 client端收到synack后,根据ack值,使用SACK算法,只重传最后一个ack内容。

Server端收到数据包,由于accept队列仍然是满的,所以server端处理也只是标记acked,然后返回。

151896 client端等待3秒后,没有收到对应的ack,认为之前的数据包也丢失,所以重传之前的内容数据包。

152579 server端连接建立定时器生效,遍历半连接链表,发现刚才acked的连接,

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top