TCP backlog的解读

TCP backlog参数

介绍

  • 在实际生产环境中使用netty的过程中,遇到了一个奇怪的坑,就是backlog参数,明明设置得很大了,但是还是不生效,使得server在TCP连接数上来的时候,就会发送ECONNREFUSED给client以示拒绝。
  • 这触及到我的知识盲区,因此我决定加以探索并且记录、分享。我从底层、应用层两个层面进行分析,并最终定位到了这个问题,且加以解决。

底层

backlog参数主要用于底层方法int listen(int sockfd, int backlog), 在解释backlog参数之前,我们先了解下tcp在内核的请求过程,其实就是tcp的三次握手:

img

  • 握手过程:
    • client发送SYN到server,将状态修改为SYN_SEND,如果server收到请求,则将状态修改为SYN_RCVD,并把该请求放到syns queue队列中。
    • server回复SYN+ACK给client,如果client收到请求,则将状态修改为ESTABLISHED,并发送ACK给server。
    • server收到ACK,将状态修改为ESTABLISHED,并把该请求从syns queue中放到accept queue。

因此,在系统内核中,会维护两个队列,syns queueaccept queue

syns queue

用于保存半连接状态的请求,其大小通过/proc/sys/net/ipv4/tcp_max_syn_backlog指定,一般默认值是1024,这个参数涉及到一著名的TCP SYN FLOOD恶意DOS攻击方式就是建立大量的半连接状态的请求,然后丢弃,导致syns queue不能保存其它正常的请求,从而攻击你的系统。

accept queue

用于保存全连接状态的请求,其大小通过/proc/sys/net/core/somaxconn指定,在使用listen函数时,内核会根据传入的backlog参数与系统参数somaxconn,取二者的较小值。如果accpet queue队列满了,server将发送一个ECONNREFUSED错误信息Connection refused到client。

应用层

查看netty中的实现代码:

SOMAXCONN = AccessController.doPrivileged(new PrivilegedAction<Integer>() {
    @Override
    public Integer run() {
        // Determine the default somaxconn (server socket backlog) value of the platform.
        // The known defaults:
        // - Windows NT Server 4.0+: 200
        // - Linux and Mac OS X: 128
        int somaxconn = PlatformDependent.isWindows() ? 200 : 128;
        File file = new File("/proc/sys/net/core/somaxconn");
        BufferedReader in = null;
        try {
            // file.exists() may throw a SecurityException if a SecurityManager is used, so execute it in the
            // try / catch block.
            // See https://github.com/netty/netty/issues/4936
            if (file.exists()) {
                in = new BufferedReader(new FileReader(file));
                somaxconn = Integer.parseInt(in.readLine());
                if (logger.isDebugEnabled()) {
                    logger.debug("{}: {}", file, somaxconn);
                }
            } else {
                // Try to get from sysctl
                Integer tmp = null;
                if (SystemPropertyUtil.getBoolean("io.netty.net.somaxconn.trySysctl", false)) {
                    tmp = sysctlGetInt("kern.ipc.somaxconn");
                    if (tmp == null) {
                        tmp = sysctlGetInt("kern.ipc.soacceptqueue");
                        if (tmp != null) {
                            somaxconn = tmp;
                        }
                    } else {
                        somaxconn = tmp;
                    }
                }

                if (tmp == null) {
                    logger.debug("Failed to get SOMAXCONN from sysctl and file {}. Default: {}", file,
                                 somaxconn);
                }
            }
        } catch (Exception e) {
            logger.debug("Failed to get SOMAXCONN from sysctl and file {}. Default: {}", file, somaxconn, e);
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (Exception e) {
                    // Ignored.
                }
            }
        }
        return somaxconn;
    }
});

可以看到,会尝试打开linux系统下的somaxconn文件,并读取其中的值。这个时候我登录上我的ubuntu系统,查看该文件的内容,如下:

 vim /proc/sys/net/core/somaxconn
 128

原因就是在这里,由于内核会选取somaxconn和应用层设置backlog两个值中较小的值作为accept队列的大小,而我们正常的使用只是配置了应用层的参数,因此导致了这个错误。

解决方案

同时修改somaxconn和backlog参数,来提高accept queue大小,从而接收更多的连接。

扫描二维码关注公众号,回复: 8987017 查看本文章
发布了57 篇原创文章 · 获赞 32 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/rekingman/article/details/100009017
tcp