美文网首页
【tcp】TCP实战(半连接队列、全连接队列)

【tcp】TCP实战(半连接队列、全连接队列)

作者: Bogon | 来源:发表于2023-07-11 00:01 被阅读0次

    TCP半连接队列与全连接队列概念

    在 TCP 三次握手的时候,Linux 内核会维护两个队列,分别是:

    半连接队列,也称 SYN 队列
    全连接队列,也称 accepet 队列

    服务端收到客户端发起的 SYN 请求后,内核会把该连接存储到半连接队列,并向客户端响应 SYN+ACK,接着客户端会返回 ACK,服务端收到第三次握手的 ACK 后,内核会把连接从半连接队列移除,然后创建新的完全的连接,并将其添加到 accept 队列,等待进程调用 accept 函数时把连接取出来。

    image.png

    不管是半连接队列还是全连接队列,都有最大长度限制,超过限制时,内核会直接丢弃,或返回 RST 包。

    在服务端可以使用 ss 命令,来查看 TCP 全连接队列的情况:

    ss是Socket Statistics的缩写。顾名思义,ss命令可以用来获取socket统计信息,它可以显示和netstat类似的内容。但ss的优势在于它能够显示更多更详细的有关TCP和连接状态的信息,而且比netstat更快速更高效。

    netstat命令用来打印Linux中网络系统的状态信息,可让你得知整个Linux系统的网络情况。

    但需要注意的是 ss 命令获取的 Recv-Q/Send-Q 在「LISTEN 状态」和「非 LISTEN 状态」所表达的含义是不同的。

    从下面的内核代码可以看出区别:

    image.png

    在「LISTEN 状态」时,利用 ss -lnt 命令,Recv-Q/Send-Q 表示的含义如下:

    • -l:--listening 显示监听状态的套接字(sockets)
    • -n:--numeric 不解析服务名称
    • -t:--tcp 仅显示 TCP套接字(sockets)
    image.png
    • Recv-Q:当前全连接队列的大小,也就是当前已完成三次握手并等待服务端 accept() 的 TCP 连接;
    • Send-Q:当前全连接最大队列长度,上面的输出结果说明监听 8088 端口的 TCP 服务,最大全连接长度为 128;

    在「非 LISTEN 状态」时,利用 ss -nt 命令Recv-Q/Send-Q 表示的含义如下:

    • Recv-Q:已收到但未被应用进程读取的字节数
    • Send-Q:已发送但未收到确认的字节数

    当超过了 TCP 最大全连接队列,服务端则会丢掉后续进来的 TCP 连接,丢掉的 TCP 连接的个数会被统计起来。

    我们在服务端可以使用 netstat -s 命令来查看:

    image.png

    上面看到的 1750、2287....times ,表示全连接队列溢出的次数,注意这个是累计值。
    可以隔几秒钟执行下,如果这个数字一直在增加的话肯定全连接队列偶尔满了。

    服务端并发处理大量请求时,如果 TCP 全连接队列过小,就容易溢出。
    发生 TCP 全连接队溢出的时候,后续的请求就会被丢弃。

    image.png

    Linux 有个参数可以指定当 TCP 全连接队列满了会使用什么策略来回应客户端。


    image.png

    tcp_abort_on_overflow 共有两个值分别是 0 和 1,其分别表示:

    • 0 :如果全连接队列满了,那么 server 扔掉 client 发过来的 ack
    • 1 :如果全连接队列满了,server 发送一个 reset 包给 client,表示废掉这个握手过程和这个连接

    如果要想知道客户端连接不上服务端,是不是服务端 TCP 全连接队列满的原因,那么可以把 tcp_abort_on_overflow 设置为 1,这时如果在客户端异常中可以看到很多 connection reset by peer 的错误,那么就可以证明是由于服务端 TCP 全连接队列溢出的问题。

    通常情况下,应当把 tcp_abort_on_overflow 设置为 0,因为这样更有利于应对突发流量。

    举个例子,当 TCP 全连接队列满导致服务器丢掉了 ACK,与此同时,客户端的连接状态却是 ESTABLISHED,进程就在建立好的连接上发送请求。只要服务器没有为请求回复 ACK,请求就会被多次重发。如果服务器上的进程只是短暂的繁忙造成 accept 队列满,那么当 TCP 全连接队列有空位时,再次接收到的请求报文由于含有 ACK,仍然会触发服务器端成功建立连接。

    所以,tcp_abort_on_overflow 设为 0 可以提高连接建立的成功率,只有你非常肯定 TCP 全连接队列会长期溢出时,才能设置为 1 以尽快通知客户端。

    如何查看半连接队列大小?

    很遗憾,TCP 半连接队列长度的长度,没有像全连接队列那样可以用 ss 命令查看。

    但是我们可以抓住 TCP 半连接的特点,就是服务端处于 SYN_RECV 状态的 TCP 连接,就是在 TCP 半连接队列。

    当服务端受到 SYN 攻击后,我们在服务端主机上执行查看当前 TCP 半连接队列大小:

    image.png

    可以发现最大值到256就不再变化,说明当前TCP半连接队列的最大值为256。

    同时,如果半连接队列满了且tcp_syncookies未开启,则客户端发送至服务端的正常请求连接数据包将会被丢弃,利用curl命令证明了这一点。

    image.png image.png

    如果SYN半连接队列已经满了,只能丢弃连接吗?

    答案是否定的,在前面我们源码分析也可以看到这点,当开启了 syncookies 功能就可以在不使用 SYN 半连接队列的情况下成功建立连接。

    image.png

    tcp_syncookies 参数主要有以下三个值,可以在 /proc/sys/net/ipv4/tcp_syncookies 修改该值。

    • 0 值,表示关闭该功能
    • 1 值,表示仅当 SYN 半连接队列放不下时,再启用它
    • 2 值,表示无条件开启功能

    参考

    TCP实战(半连接队列、全连接队列)
    https://zhuanlan.zhihu.com/p/619498204

    从一次线上问题说起,详解 TCP 半连接队列、全连接队列
    http://www.hzhcontrols.com/new-1252524.html

    相关文章

      网友评论

          本文标题:【tcp】TCP实战(半连接队列、全连接队列)

          本文链接:https://www.haomeiwen.com/subject/vsueudtx.html