服务端启动最小化代码

      以下是服务端启动的一个Demo:

public class NettyServer {
    public static void main(String[] args) {
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();

        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap.group(bossGroup, workerGroup)
                // 指定IO模型为NIO
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {

                    }
                });
        serverBootstrap.bind(8000);
    }
}
  • 首先创建了两个NioEventLoopGroup,这两个对象可以看作传统IO编程模型的两大线程组,bossGroup 表示监听端口,接收新连接的线程组;workerGroup表示处理每一个连接的数据读写的线程组。
  • 其次创建了一个引导类ServerBootstrap,这个类将引导服务端的启动工作。
  • 通过.group(bossGroup,workerGroup)给引导类配置两大线程组,这个引导类的线程模型就定型了。
  • 然后指定服务端的 IO 模型为 NIO,上述代码通过 .channel(NioServerSocketChannel.class) 来指定 IO 模型,也可以有其他选择。如果你想指定 IO 模型为 BIO,那么这里配置 OioServerSocketChannel.class 即可。
  • 接着调用 childHandler() 方法,给这个引导类创建一个 ChannelInitializer。在 ChannelInitializer 这个类中,有一个泛型参数 NioSocketChannel,这个类就是 Netty 对 NIO 类型连接的抽象,而面前的 NioServerSocketChannel 也是对 NIO 类型连接的抽象,NioServerSocketChannelNioSocketChannel 的概念可以与 BIO 编程模型中的 ServerSocketChannel 和 Socket 两个概念对应。

自动绑定递增端口

      上述代码指定绑定了8000端口,接下来实现一个稍微复杂点的逻辑。我们指定一个起始端口号,然后从起始端口往上找一个端口,直到这个端口能够绑定成功。
      serverBootstrap.bind(8000) 方法是一个异步方法,调用之后是立即返回的,它的返回值是一个 ChannelFuture。我们可以给这个 ChannelFuture 添加一个监听器 GenericFutureLinstener,然后在 GenericFutureLinsteneroperationComplete 方法里,监听端口是否绑定成功。如下代码所示。

serverBootstrap.bind(8000)
                .addListener(future -> {
                    if (future.isSuccess()) {
                        System.out.println("端口绑定成功");
                    } else {
                        System.out.println("端口绑定失败");
                    }
                });

      我们要做的就是在 else 逻辑中重新绑定端口接口。为此我们抽取出一个 bind 方法。

private static void bind(ServerBootstrap serverBootstrap, int port) {
    serverBootstrap.bind(port).addListener(future -> {
        if (future.isSuccess()) {
            System.out.println(port + " 端口绑定成功");
        } else {
            System.out.println(port + " 端口绑定失败");
            bind(serverBootstrap, port + 1);
        }
    });
}

服务端启动的其他方法

handler() 方法

serverBootstrap.group(bossGroup, workerGroup)
    .channel(NioServerSocketChannel.class)
    .handler(new ChannelInitializer<NioServerSocketChannel>() {
        @Override
        protected void initChannel(NioServerSocketChannel ch) throws Exception {
            System.out.println("服务端启动中");
        }
    })

      handler() 方法可以和 childHandler() 方法对应起来:childHandler() 方法用于指定处理新连接数据的读写处理逻辑;handler() 方法用于指定在服务端启动过程中的一些逻辑。通常不使用。

attr() 方法

serverBootstrap.group(bossGroup, workerGroup)
    .attr(AttributeKey.newInstance("Demo"),"DemoServer")

      attr() 方法可以给服务端 Channel,也就是 NioServerSocketChannel 指定一些自定义属性,然后通过 channel.attr() 取出这个属性。除了可以给服务端 Channel 指定一些自定义属性,我们还可以通过 childAttr 方法给每个连接都指定自定义属性。

option() 方法

      option() 方法可以给服务端 Channel 设置一些 TCP 参数,最常见的就是 so_backlog,设置如下:

serverBootstrap.option(ChannelOption.SO_BACKLOG, 1024)

      这个设置表示系统用于临时存放已完成三次握手的请求的队列的最大长度,如果连接建立频繁,服务器处理创建新连接较慢,则可以适当调大这个参数。

childOption() 方法

      childOption() 方法可以给每个连接都设置一些 TCP 参数。

serverBootstrap
    .childOption(ChannelOption.SO_KEEPALIVE,true)
    .childOption(ChannelOption.TCP_NODELAY,true)
  • ChannelOption.SO_KEEPLIVE 表示是否开启 TCP 底层心跳机制,true 表示开启。
  • ChannelOption.TCP_NODELAY 表示是否开启 Nagle 算法,true 表示关闭,false 表示关闭。如果要求高实时性,有数据发送时就马上发送,就设置为关闭;如果需要减少发送次数,减少网络交互就设置为开启。
Last modification:March 23rd, 2024 at 09:26 pm
如果觉得我的文章对你有用,请随意赞赏