https://www.jianshu.com/p/6a0fcb1008d6
本文主要摘抄自关于TCP 半连接队列和全连接队列
当服务端调用listen函数监听端口的时候,内核会为每个监听的socket创建两个队列:半连接队列和全连接队列。
如上图所示,这里有两个队列:syns queue(半连接队列);accept queue(全连接队列)
三次握手,两个队列如下工作:
半连接队列的大小由/proc/sys/net/ipv4/tcp_max_syn_backlog控制,Linux的默认是1024。
当服务端发送SYN_ACK后将会开启一个定时器,如果超时没有收到客户端的ACK,将会重发SYN_ACK包。重传的次数由/proc/sys/net/ipv4/tcp_synack_retries控制,默认是5次。
全连接队列的大小通过/proc/sys/net/core/somaxconn指定,在使用listen函数时,内核会根据传入的backlog参数与系统参数somaxconn,取二者的较小值。
int listen(int sockfd, int backlog)
Nginx和Redis默认的backlog值等于511,Linux默认的backlog 为 128,Java默认的backlog等于50
默认情况下,全连接队列满以后,服务端会忽略客户端的 ACK,随后会重传SYN+ACK,也可以修改这种行为,这个值由/proc/sys/net/ipv4/tcp_abort_on_overflow决定。
tcp_abort_on_overflow为0表示三次握手最后一步全连接队列满以后服务端会丢掉客户端发过来的ACK,服务端随后会进行重传SYN+ACK。tcp_abort_on_overflow为1表示全连接队列满以后服务端发送RST给客户端,直接释放资源。
syn floods 攻击就是针对半连接队列的,攻击方不停地建连接,但是建连接的时候只做第一步,第二步中攻击方收到server的syn+ack后故意扔掉什么也不做,导致server上这个队列满其它正常请求无法进来。
为了预防这个问题,提出了SYN Cookie技术,它可以让服务器在收到客户端的SYN报文时,不分配资源保存客户端信息,而是将这些信息保存在SYN+ACK的初始序号和时间戳中。对正常的连接,这些信息会随着ACK报文被带回来。更详细的讨论可见 深入浅出TCP中的SYN-Cookies
client认为连接建立成功,但是server上这个连接实际没有ready,所以server没有回复,一段时间后client认为丢包了然后重传这个包,一直到超时,client主动发fin包断开该连接。
这个问题也叫client fooling,可以看这里:tcp/dccp: drop SYN packets if accept queue is full
来看三次握手第一步的源代码:
TCP三次握手第一步的时候如果全连接队列满了会影响第一步半连接的发生。大概流程的如下:
tcp_v4_do_rcv->tcp_rcv_state_process->tcp_v4_conn_request
//如果accept backlog队列已满,且未超时的request socket的数量大于1,则丢弃当前请求
if(sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_yong(sk)>1)
goto drop;
work@ost:~ $ ss -lnt
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 128 *:22 *:*
LISTEN 0 128 [::]:22 [::]:*
work@ost:~ $
work
上述命令抄自TCP的全连接和半连接队列
netstat命令也有输出Recv-Q
和Send-Q
work@ost:~ $ sudo netstat -anlp
(No info could be read for "-p": geteuid()=1000 but you should be root.)
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN -
tcp 0 0 172.17.0.2:22 172.17.0.1:34504 ESTABLISHED -
tcp6 0 0 :::22 :::* LISTEN -
.......
但是他们的含义和ss非listen状态的含义一样。这两个值通常应该为0,如果不为0可能是有问题的。packets在两个队列里都不应该有堆积状态。可接受短暂的非0情况。