Netty 入门

2021-08-29 14:56:51

从官网入门文档中找到最简化的完整的服务端与服务端的代码。

服务端

一个 Netty 服务端的代码实在太精简了,只考虑好几个部分就可以:初始化、编/解码器、数据处理。非官方的笼统的说记住这三步骤就足够了。

初始化

初始化部分是信息量最多的部分,在这里需要创建线程模型,指定网络参数配置,将编/解码器,数据处理器等进行配置,然后绑定到一个端口就可以。

//NettyServer.java


public class NettyServer {
    static final int PORT = 9000;

    public static void main(String[] args) throws InterruptedException {
       
        //监听线程(池)
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        //工作线程(池)
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            
            //服务端的辅助类
            ServerBootstrap b = new ServerBootstrap();
            
            //将线程组配置进服务
            b.group(bossGroup, workerGroup)
            
                    //指定服务端监听类
                    .channel(NioServerSocketChannel.class)
                    
                    //配置网络参数
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    .childOption(ChannelOption.TCP_NODELAY,true)
                    
                    //配置一个日志处理器
                    .handler(new LoggingHandler(LogLevel.INFO))
                    
                    //定义一个匿名初始始化器,用来指定挂载器
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline p = ch.pipeline();
                            
                            //后面定义处理器
                            p.addLast(new EchoServerHandler());
                        }
                    });

            
            //启动监听线程,绑定在一个指定的端口上
            ChannelFuture f = b.bind(PORT).sync();

            // 关闭了网络监听后
            f.channel().closeFuture().sync();
        } finally {
            // 完全关闭后
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

编/解码器

编/解码器按照接收和发送的顺序处理,这里以熟悉代码结构为主可以忽略,在一个实际项目中是很难避免不使用编/解码器的。

消息处理器

这里继承 ChannelInboundHandlerAdapter 类的是接收处理器,如果是继承 'ChannelOutboundHandlerAdapter 类的是发送类型的处理器。

//EchoServerHandler.java

public class EchoServerHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buf=(ByteBuf)msg;
        System.out.println("客户端发送消息是:"+buf.toString(CharsetUtil.UTF_8));
        System.out.println("客户端地址:"+ctx.channel().remoteAddress());
        ctx.write(msg);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

望码知意,回调函数都是被框架自动调用的。

客户端

客户端大体结构与服务端很类似

EchoClient.java

public class EchoClient {

    static final String HOST = System.getProperty("host", "127.0.0.1");
    static final int PORT = Integer.parseInt(System.getProperty("port", "9000"));
    static final int SIZE = Integer.parseInt(System.getProperty("size", "256"));

    public static void main(String[] args) throws Exception {

        // 这里只配置工作线程即可
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            //客户端辅助类
            Bootstrap b = new Bootstrap();
            b.group(group)
                    .channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY, true)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline p = ch.pipeline();
                            p.addLast(new EchoClientHandler());
                        }
                    });

            // 连接
            ChannelFuture f = b.connect(HOST, PORT).sync();

            // 结束
            f.channel().closeFuture().sync();
        } finally {
            // 回收线程
            group.shutdownGracefully();
        }
    }
}

编/解码

编/解码和服务端要相对应,是类似的,这里可以不处理。

处理器

客户端的收发也是在处理器

//EchoClientHandler.java

public class EchoClientHandler extends ChannelInboundHandlerAdapter {
    private final ByteBuf firstMessage;

    /**
     * Creates a client-side handler.
     */
    public EchoClientHandler() {
        firstMessage = Unpooled.buffer(EchoClient.SIZE);
        for (int i = 0; i < firstMessage.capacity(); i ++) {
            firstMessage.writeByte((byte) i);
        }
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(firstMessage);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ctx.write(msg);
        System.out.println(msg);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
Copyright tg-blog 京ICP备15066502号-2