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

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

回顾上篇文章:

nginx+lua 入门

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

查看系统最大半队列大小

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

查看系统当前半队列大小

如何查看是否有溢出的 SYN

4

全连接队列 (Accept queue)

当 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 包。

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

说明:

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 是否有溢出

和我们一起,

成为更酷的运维工程师!

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180712G190BH00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券