Netty学习-3. Netty NIO代码

440 阅读2分钟

前言

上一节,我们简单的介绍了Java 里面NIO的基本用法。我们都知道,我们使用Netty大部分情况都是为了使用它的NIO的。那么这一节,我们也基于Netty来写一个和上一节的一样的功能做下对比吧。

1. 服务端

  1. NioServer
public class NioServer {

  public static void main(String[] args) {
    int port = 8080;
    ServerBootstrap serverBootstrap = new ServerBootstrap();
    EventLoopGroup bossGroup = new NioEventLoopGroup();
    EventLoopGroup workerGroup = new NioEventLoopGroup();
    serverBootstrap.group(bossGroup, workerGroup)
        .channel(NioServerSocketChannel.class)
        .option(ChannelOption.TCP_NODELAY, true)
        .option(ChannelOption.SO_BACKLOG, 1024)
        .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
        .childHandler(new ChannelInitializer<SocketChannel>() {
          @Override
          protected void initChannel(SocketChannel ch) throws Exception {
            ch.pipeline().addLast(new NioServerHandler());
          }
        });
    ChannelFuture bind = serverBootstrap.bind(port);
    try {
      bind.channel().closeFuture().sync();
    } catch (InterruptedException e) {
      bossGroup.shutdownGracefully();
      workerGroup.shutdownGracefully();
    }
  }
}

上面是Netty服务端的入口代码,这里相比Java的服务端,代码就要稍微多了一点了,但是总体可以分为三部分:

  1. 创建ServerBootstrapEventLoopGroup

  2. 配置相关的配置

  3. 绑定端口,并在异常时优雅关闭

  4. NioServerHandler
    public class NioServerHandler extends ChannelInboundHandlerAdapter {
    
      @Override
      public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buf = (ByteBuf) msg;
        byte[] req = new byte[buf.readableBytes()];
        buf.readBytes(req);
        String body = new String(req, "utf-8");
        System.out.println("server received :" + body);
    
        ByteBuf resp = Unpooled.copiedBuffer(body.getBytes(StandardCharsets.UTF_8));
        ctx.write(resp);
      }
    
      @Override
      public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
      }
    }
    

    上面是处理服务端发送消息相关的代码。和Java的处理逻辑比起来,这里的代码逻辑简直是少的吓人。基本上就实现了两个方法,就完成了消息的接收和发送的逻辑。乖乖,光是剩下的代码量得扣掉多少绩效(抖个机灵)。

2. 客户端

  1. NioClient
public class NioClient {

  public static void main(String[] args) {
    Bootstrap bootstrap = new Bootstrap();
    EventLoopGroup group = new NioEventLoopGroup();
    bootstrap.group(group)
        .channel(NioSocketChannel.class)
        .option(ChannelOption.TCP_NODELAY, true)
        .option(ChannelOption.SO_BACKLOG, 1024)
        .handler(new ChannelInitializer<SocketChannel>() {
                   @Override
                   protected void initChannel(SocketChannel ch) throws Exception {
                     ch.pipeline().addLast(new NioClientHandler());
                   }
                 }
        );
    ChannelFuture channelFuture = bootstrap.connect("localhost", 8080);
    try {
      channelFuture.channel().closeFuture().sync();
    } catch (InterruptedException e) {
      group.shutdownGracefully();
    }
  }
}

上面是Netty客户端的代码。可以看到除了Bootstrap用的不一样,总的来说也是和服务端代码没啥区别的。

  1. NioClientHandler
    public class NioClientHandler extends ChannelInboundHandlerAdapter {
    
      @Override
      public void channelActive(ChannelHandlerContext ctx) throws Exception {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入要发送的消息");
        String message = scanner.next();
        byte[] req = message.getBytes(StandardCharsets.UTF_8);
        ByteBuf buf = Unpooled.buffer(req.length);
        buf.writeBytes(req);
        ctx.writeAndFlush(buf);
      }
    
      @Override
      public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buf = (ByteBuf) msg;
        byte[] req = new byte[buf.readableBytes()];
        buf.readBytes(req);
        String body = new String(req, "utf-8");
        System.out.println("client received :" + body);
      }
    }
    

    这里也是一样的,和Java的客户端繁杂的处理SelectionKey的逻辑比起来,这里的代码简直可以说是少的可怜。

    这一节我们只是简单的介绍Netty的基本使用方法,下一节,我们将从源码分析,介绍Netty是如何把Java的NIO的代码封装成如此好用的。