Netty ChannelOption.SO_BACKLOG配置

ChannelOption.SO_BACKLOG对应的是tcp/ip协议, listen函数 中的 backlog 参数,用来初始化服务端可连接队列。

函数:

// backlog 指定了内核为此套接口排队的最大连接个数;
// 对于给定的监听套接口,内核要维护两个队列: 未连接队列和已连接队列
// backlog 的值即为未连接队列和已连接队列的和。
listen(int socketfd,int backlog)

backlog 用于构造服务端套接字ServerSocket对象,标识当服务器请求处理线程全满时,用于临时存放已完成三次握手的请求的队列的最大长度。

未连接队列 和 已连接队列

在linux系统内核中维护了两个队列:syns queue和accept queue

服务端处理客户端连接请求是顺序处理的,所以同一时间只能处理一个客户端连接,多个客户端来的时候,服务端将不能处理的客户端连接请求放在队列中等待处理。

  • syns queue:保存一个SYN已经到达,但三次握手还没有完成的连接。

    用于保存半连接状态的请求,其大小通过/proc/sys/net/ipv4/tcp_max_syn_backlog指定,一般默认值是512。不过这个设置有效的前提是系统的syncookies功能被禁用。

    互联网常见的TCP SYN FLOOD恶意DOS攻击方式就是建立大量的半连接状态的请求,然后丢弃,导致syns queue不能保存其它正常的请求。

  • accept queue:保存三次握手已完成,内核正等待进程执行accept的调用的连接。

    用于保存全连接状态的请求,其大小通过/proc/sys/net/core/somaxconn指定。

    在使用listen函数时,内核会根据传入的backlog参数与系统参数somaxconn,取二者的较小值。

注意

  1. 如果未设置或所设置的值小于1,Java将使用默认值50。

  2. 如果accpet queue队列满了,server将发送一个ECONNREFUSED错误信息Connection refused到client。

backlog 设置注意点

服务器TCP内核 内维护了两个队列,称为A(未连接队列)和B(已连接队列)

如果A+B的长度大于Backlog时,新的连接就会被TCP内核拒绝掉。

所以,如果backlog过小,就可能出现Accept的速度跟不上,A,B队列满了,就会导致客户端无法建立连接。

需要注意的是,backlog对程序的连接数没影响,但是影响的是还没有被Accept取出的连接。

Netty 应用

在netty实现中,backlog默认通过NetUtil.SOMAXCONN指定;

也可以在服务器启动启动时,通过option方法自定义backlog的大小。

例如:

// server 启动引导
ServerBootstrap serverBootstrap = new ServerBootstrap();
// 配置启动的参数
serverBootstrap.group(bossGroup,workerGroup)
        // 设置非阻塞,用它来建立新accept的连接,用于构造ServerSocketChannel的工厂类
        .channel(NioServerSocketChannel.class)
        // 临时存放已完成三次握手的请求的队列的最大长度。
        // 如果未设置或所设置的值小于1,Java将使用默认值50。
        // 如果大于队列的最大长度,请求会被拒绝
        .option(ChannelOption.SO_BACKLOG,128)
        .childOption(ChannelOption.SO_KEEPALIVE,true)
        .handler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel ch) throws Exception {

            }
        });

TCP的连接状态 (SYN, FIN, ACK, PSH, RST, URG)

  1. SYN 表示建立连接
  2. FIN 表示关闭连接
  3. ACK 表示响应
  4. PSH 表示有 DATA数据传输
  5. RST 表示连接重置。

TCP 连接(三次握手)

yYRw4J.png

  1. 第一次握手: 客户端向服务器端发送SYN标志的包建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;
  2. 第二次握手: 服务器端,向客户端发送 SYN 和ACK的包 服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态
  3. 第三次握手: 客户端向服务器端,收到服务端发送的SYN和ACK包,确认正确后,给服务器发送发送ACK的包, 客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。完成三次握手,客户端与服务器开始传送数据。
  • 17
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
这段代码使用了Netty框架来启动一个服务器并绑定多个端口。下面是对代码的解析和问题的检查: 1. 首先,创建了两个`NioEventLoopGroup`对象,分别用于处理服务器的连接请求(bossGroup)和处理已经建立连接的网络通信(workerGroup)。 2. 然后,创建了一个`ServerBootstrap`对象,并将bossGroup和workerGroup设置到该对象中。 3. 调用`bootstrap.channel(NioServerSocketChannel.class)`设置服务器的通道类型为NIO类型。 4. 使用`option()`方法设置服务器的一些配置选项。例如,使用`ChannelOption.SO_BACKLOG`设置连接请求的最大队列长度为128,使用`ChannelOption.SO_KEEPALIVE`设置保持连接状态为true。 5. 使用`LoggingHandler`设置日志级别为INFO。 6. 调用`consumer.accept(bootstrap)`方法,允许用户自定义配置`ServerBootstrap`实例。 7. 创建了一个线程池,使用`ThreadPoolExecutor`来执行异步任务。线程池的大小与要绑定的端口数量一致。 8. 使用流式操作,遍历输入的`ports`数组。对于每个端口,使用`bootstrap.bind(port)`方法来绑定端口,并返回一个`ChannelFuture`对象。然后,调用`future.sync()`方法等待绑定完成,并调用`future.channel().closeFuture().sync()`等待通道关闭。 9. 在捕获异常的块中,记录绑定异常并打印日志。 10. 在finally块中,调用`workerGroup.shutdownGracefully()`和`bossGroup.shutdownGracefully()`来优雅地关闭线程组,释放资源。 问题检查: - 通过使用线程池来异步执行端口绑定和关闭操作,可以控制并发度,避免创建过多的线程。这样可以更好地管理系统资源。 - 添加了一些服务器的配置选项,如`SO_BACKLOG`和`SO_KEEPALIVE`,以提供更好的性能和可靠性。 综上所述,这段代码在原有的异步绑定和关闭操作的基础上,进一步添加了线程池的支持和服务器的配置选项,提高了代码的可扩展性、性能和可靠性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值