环境:
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的衔接,synack重传次数在阀值以内,从头发送synack给client端。
152581 cient端收到synack后,依据ack值,运用SACK算法,只重传最终一个ack内容。
Server端收到数据包,因为accept行列依然是满的,所以server端处理也仅仅符号acked,然后回来
153455 client端等候3秒后,没有收到对应的ack,以为之前的数据包也丢掉,所以重传之前的内容数据包。
155399 server端衔接树立定时器收效,遍历半衔接链表,发现方才acked的衔接,synack重传次数在阀值以内,从头发送synack给client端。
155400 cient端收到synack后,依据ack值,运用SACK算法,只重传最终一个ack内容。
Server端收到数据包,因为accept行列依然是满的,所以server端处理也仅仅符号acked,然后回来。
156468 client端等候几秒后,没有收到对应的ack,以为之前的数据包也丢掉,所以重传之前的内容数据包。
161309 server端衔接树立定时器收效,遍历半衔接链表,发现方才acked的衔接,synack重传次数在阀值以内,从头发送synack给client端。