您当前的位置: 首页 > 

恐龙弟旺仔

暂无认证

  • 0浏览

    0关注

    282博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

TCP backlog解析与实战

恐龙弟旺仔 发布时间:2022-05-28 10:14:19 ,浏览量:0

前言:

针对TCP连接三次握手,我们都已经非常熟悉了。

那么三次握手之后是不是就一定会被服务端处理请求了呢?

可能我们不太清楚操作系统的具体做法,但是凭感觉我们会有两方面的思考:

1)如果大量的客户端连接发送SYN请求,服务器根本无力处置这么多SYN请求,那么怎么做限流操作来避免自己被冲垮呢?

2)三次握手成功之后,服务端处理程序一直在忙于之前的连接的业务处理,长时间没有感知到新的连接到了,那么对于这个客户端发来的请求应该怎么操作呢?

本文就通过对TCP backlog来回答下上述两个问题。

1.TCP三次握手请求在内核中的展现

三次握手 客户端与服务端的状态变化,如下图所示:

这个描述的是状态变化,那么服务端在接收三次握手请求时,内核中又是具体如何操作的呢?我们先看一张图(图片来自: TCP backlog的解读_Lin~Xu的博客-CSDN博客_backlog tcp  ) 

熟悉java socket编程的同学应该对上述方法并不陌生。我们简单解释下:

server:

    通过bind()方法将ServerSocket绑定到某个端口,并启动。启动完成之后,自然就开启了listen,用于监听客户端对当前端口的连接;

client:

    通过connect()方法对远端的ServerSocket发起tcp连接,连接过程就是下面的三次握手过程(本过程由双方操作系统自动完成);

上图中最右侧有两个队列,它们的作用是什么呢?

2. syns queue

该队列是内核设置的一个请求存放队列。

当服务端接收到客户端的SYN请求后,将当前连接状态修改为SYN_RCVD,并将该连接存放到syns queue队列中。

2.1 syns queue作用

具体来说就是对应我们来前言中提到的第一个问题,用来限制过多SYN请求冲垮服务端的。

当一个新的SYN报文达到时,服务端就会检查当前处于SYN_RCVD状态的连接是否超过某个阈值,如果超过,则直接拒绝该新连接。

2.2 sync queue阈值设置

作为一个Linux系统参数,通过net.ipv4.tcp_max_syn_backlog参数来设置的,如下

 

root@aa4e7f274852:/# sysctl -a | grep net.ipv4.tcp_max_syn_backlog
net.ipv4.tcp_max_syn_backlog = 128
3. accept queue

当服务端接收到最后一次ACK时,当前连接状态修改为ESTABLISHED。此时会将该连接从上面的sync queue移到当前的accept queue。

此时从操作系统层面,已经认可了本次连接。

3.1 accept queue作用

上面的操作都是操作系统行为。

但是有一个问题:就是应用程序不一定认可该连接

这个就是我们在前言中提到的第二个问题,如果当前应用程序一直在忙碌之前的连接处理,而对新连接已经没有关注接收,那么新连接发送过来的请求必然不会被处理。

那么对于这种来不及处理的连接请求怎么办呢?

答案很简单:还是将连接放到一个队列中,等应用程序处理完之前的连接后,就从该队列中获取新的连接请求来处理。

既然是队列,那么肯定会设置一个最大长度(也就是当前accept queue的长度)。

应用系统对这一队列作出限制,通常称为未完成队列(backlog)。

3.2 设置backlog值

backlog的值必须在 [0,系统指定最大值之间]。这个最大值也是通过系统参数设置的,net.core.somaxconn

root@aa4e7f274852:/# sysctl -a | grep net.core.somaxconn
net.core.somaxconn = 4096
4 ServerSocket中的backlog

熟悉java的同学都知道,在创建ServerSocket的时候,有这么一个构造方法

/**
     * The {@code backlog} argument is the requested maximum number of
     * pending connections on the socket. Its exact semantics are implementation
     * specific. In particular, an implementation may impose a maximum length
     * or may choose to ignore the parameter altogther. The value provided
     * should be greater than {@code 0}. If it is less than or equal to
     * {@code 0}, then an implementation specific default will be used.
     * 

* * @param port the port number, or {@code 0} to use a port * number that is automatically allocated. * @param backlog requested maximum length of the queue of incoming * connections. */ public ServerSocket(int port, int backlog) throws IOException { this(port, backlog, null); }

这里的backlog是可以自行设置的,但是我们一般不需要设置这个参数,直接用系统参数即可。

从注释中可以看到,该backlog值的含义就是:设置上述accept queue长度

那么在设置该参数后,tcp连接到底选择系统参数还是该backlog呢?

我们通过一个示例来展示下。

4.1 设置较小ServerSocket backlog值

4.1.1 ServerSocket测试

public static void main(String[] args) throws Exception{
    // 设置backlog为5
    ServerSocket server = new ServerSocket(10000, 5);
    // 创建完ServerSocket监听后,不进行后面的accept()方法,相当于阻塞所有的客户端连接
    while(true)
    {
		
    }

}

通过这种while(true)的设置,阻塞所有的客户端连接,这时客户端连接只能进入accept queue

4.1.2 Socket测试

public class ClientTest {

    private static Socket[] clients = new Socket[1000];
    public static void main(String[] args) throws Exception {
        for (int i = 1; i             
关注
打赏
1655041699
查看更多评论
0.0373s