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

本篇文章介绍了TCP建连流程中,半连接队列和全连接队列的区别。

1. 简单的 TCP 建连流程

先来张图,如下:

1)client 端使用 connect() 向 server 端发起连接请求(发送 syn 包),此时 client 端的 TCP 的状态为 SYN_SENT。

2)server 端在收到 SYN 包后,将 TCP 相关信息放到 syn queue(半连接队列)中,同时向 client 发送 syn+ack。server 端 TCP 的状态为 SYN_RCVD。

3)client 端收到 server 端的 syn+ack 后,向 server 端发送 ack,此时 client 端的 TCP 的状态为 ESTABLISHED。Server 端收到 ack 确认后,从 syn queue 里将 TCP 信息取出,并放到 accept quee(全连接队列)中,此时 server 端的 TCP 的状态为 ESTABLISHED。

经过以上三个过程,client 端和 server 端建立了连接,即所有三次握手的过程。

2. 半连接状态与全连接状态

通过上图和上述流程中可以看到 TCP 有两个队列:半连接队列 (SYN queue) 与全连接队列(accept queue),分别用来存放半连接状态和全连接状态信息。

半连接状态:

指 TCP 状态为 SYN_RCVD 的状态。服务器处于 Listen 状态时收到客户端 SYN 报文时放入半连接队列中,即 SYN queue。

全连接状态:

指 TCP 状态为的状态 ESTABLISHED。TCP 的连接状态从服务器(SYN+ACK)响应客户端后,到客户端的 ACK 报文到达服务器之前,则一直保留在半连接状态中;当服务器接收到客户端的 ACK 报文后,该条目将从半连接队列移到全连接队列尾部,即 accept queue。

从 linux 内核 2.2 开始,系统分离成两个 backlog 分别限制半连接(SYN_RCVD 状态)队列大小和全连接(ESTABLISHED 状态)队列大小。

3. 半连接队列 (SYN queue)

SYN queue 队列长度由 net.ipv4.tcp_max_syn_backlog 指定,默认值是根据内存大小而定,如果系统内存超过 128MB,默认值为 1024,低于 128MB 时为 128。

当 SYN queue 满了,系统还在不断的收到 SYN 包时,系统怎么处理的?系统会根据内核参数 net.ipv4.tcp_syncookies 的值来处理请求。tcp_syncookies 是用来防止 SYN flood 攻击,其原理是在半连接队列满时,SYN cookies 并不丢弃 SYN 请求,而是将源目的 IP、源目的端口号、接收到的 client 端初始序列号以及其他一些安全数值等信息进行 hash 运算,并加密后得到 server 端的初始序列号,称之为 cookie。server 端在发送初始序列号为 cookie 的 SYN+ACK 包后,会将分配的连接请求块释放。如果接收到 client 端的 ACK 包,server 端将 client 端的 ACK 序列号减 1 得到的值,与上述要素 hash 运算得到的值比较,如果相等,直接完成三次握手,构建新的连接。SYN cookies 机制的核心就是避免攻击造成的大量构造无用的连接请求块,导致内存耗尽,而无法处理正常的连接请求。

当 syn queue 满了,在 Server 端没有开启 syncookies 的时候,即 syncookies=0,server 端会丢弃新来的 SYN 包,而 client 端在多次重发 SYN 包得不到响应而返回 connection time out 错误。但是,当 Server 端开启了 syncookies, 即 syncookies=1,那么 SYN queue 就在逻辑上的没有最大值了,而是忽略内核参数 net.ipv4.tcp_max_syn_backlog 设置的值。Client 端在多次重发 SYN 包得不到响应而返回 connection time out 错误。

注意,即使开启该机制并不意味着所有的连接都是用 SYN cookies 机制来完成连接的建立,只有在 SYN queue 已满的情况下才会触发 SYN cookies 机制。由于 SYN cookies 机制严重违背 TCP 协议,不允许使用 TCP 扩展,可能对某些服务造成严重的性能影响(如 SMTP 转发),对于防御 SYN flood 攻击的确有效。对于没有收到攻击的高负载服务器,不要开启此选项,可以通过修改 tcp_max_syn_backlog、tcp_synack_retries 和 tcp_abort_on_overflow 系统参数来调节。

查看系统最大半队列大小

可以使用下面两种方面查看系统最大半队列大小:

#sysctl -a|grep max_syn
net.ipv4.tcp_max_syn_backlog = 65536
#cat /proc/sys/net/ipv4/tcp_max_syn_backlog
65536

查看系统当前半队列大小

# ss -s
Total: 18008 (kernel 103906)
TCP:   23359 (estab 188, closed 23104, orphaned 64, synrecv 0, timewait 10000/0), ports 0

Transport Total     IP        IPv6
*         103906    -         -       
RAW       0         0         0       
UDP       1         1         0       
TCP       255       249       6       
INET      256       250       6       
FRAG      0         0         0

如何查看是否有溢出的 SYN

# netstat -s|grep  LISTEN 
    1007759 SYNs to LISTEN sockets ignored

4. 全连接队列 (Accept queue)

在第三次握手时,当 server 端接收到 ACK 报之后, 会进入一个叫 accept queue,Accept queue 队列长度由 net.core.somaxconn 和应用程序使用 listen 函数时传入的参数,二者取最小值。默认为 128,表示最多有 129 的 ESTABLISHED 的连接等待 accept()。

当 Accept queue 满了以后,即使 client 端继续向 server 端发送 ACK 的包,也会不被响应,此时 ListenOverflows+1,系统会根据 net.ipv4.tcp_abort_on_overflow 参数的值来决定如何返回。值为 0 表示直接丢弃该 ACK,但不会从连接信息从 SYN queue 队列中移除。当系统丢弃了最后阶段的 ACK,系统会根据参数 net.ipv4.tcp_synack_retries 的设置重新向 client 端发送 SYN+ACK 包。而 client 端在收到多个 SYN+ACK 包,会认为之前的 ACK 丢包了,于是 client 端又重新向 server 端发送 ACK 包。当在 accept queue 有空闲的时候最终完成连接。如果 accept queue 始终满员,则最终 client 将收到 RST 包。

如果 net.ipv4.tcp_abort_on_overflow 参数值为 1 表示 server 端发送 RST 通知 client;与此同时,client 则会分别返回 read timeout 或者 connection reset by peer。

可以通过 ss/netstat 查看全队列的长度

#ss –ln
Recv-Q Send-Q             Local Address:Port               Peer Address:Port
0      128                            *:80                            *:*

说明:

TCP 状态为 LISTEN 状态的 Send-Q 字段的值为全队列最大长度,即 min(backlog, somaxconn) 的值,Recv-Q 字段的值,表示当前等待服务端调用 accept 完成三次握手的 listen backlog 数值,即当前全队列已用长度。

非 LISTEN 状态中 Recv-Q 表示 receive queue 中的 bytes 数量;Send-Q 表示 send queue 中的 bytes 数值。

查看 Accept queue 是否有溢出

# netstat -s | grep TCPBacklogDrop
TCPBacklogDrop: 8924

转自:https://www.itcodemonkey.com/article/5834.html

猜你喜欢

转载自blog.csdn.net/liufuchun111/article/details/86304267
今日推荐