从零开始netty学习笔记之protobuf

281 阅读2分钟

LoginRequest.LoginCommand的生成见http://www.jianshu.com/p/a36f31aa55de 服务端

public class ProtobufServer {

    public void bind(int port) throws Exception{

        //配置服务端的NIO线程组
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try{
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup,workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG,1024)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(new ProtobufVarint32FrameDecoder());
                            socketChannel.pipeline().addLast(new ProtobufDecoder(LoginRequest.LoginCommand.getDefaultInstance()));
                            socketChannel.pipeline().addLast(new ProtobufVarint32LengthFieldPrepender());
                            socketChannel.pipeline().addLast(new ProtobufEncoder());
                            socketChannel.pipeline().addLast(new ProtobufServerHandler());
                        }
                    });
            //绑定端口,同步等待成功
            ChannelFuture f= b.bind(port).sync();
            //等待服务器监听端口关闭
            f.channel().closeFuture().sync();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //优雅退出,释放线程池资源
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }


    }


    public static void main(String[] args) throws Exception {
        int port = 8080;
        if(args!=null&&args.length>0){
            try{
                port = Integer.valueOf(args[0]);
            }catch (NumberFormatException e){

            }
        }
        new ProtobufServer().bind(port);

    }
}

服务端处理器

public class ProtobufServerHandler extends ChannelHandlerAdapter {

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

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        LoginRequest.LoginCommand command = (LoginRequest.LoginCommand) msg;
        if("zml".equals(command.getUsername())){
            System.out.println("服务器接收到的消息:"+command.toString());
            ctx.writeAndFlush(resp(command.getUsername()));
        }
    }

    private LoginRequest.LoginCommand resp(String username) {
        LoginRequest.LoginCommand.Builder builder = LoginRequest.LoginCommand.newBuilder();

        builder.setUsername("hello zml");
        return builder.build();
    }

    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channel registed----");
        super.channelRegistered(ctx);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channel active----");
        super.channelActive(ctx);
    }
}

客户端

public class ProtobufClient {

    public void connect(int port,String host)throws Exception{

        //配置客户端NIO线程组
        EventLoopGroup group = new NioEventLoopGroup();
        try{

            Bootstrap b = new Bootstrap();
            b.group(group)
                    .channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY,true)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        protected void initChannel(SocketChannel socketChannel) throws Exception {

                            socketChannel.pipeline().addLast(new ProtobufVarint32FrameDecoder());
                            socketChannel.pipeline().addLast(new ProtobufDecoder(LoginRequest.LoginCommand.getDefaultInstance()));
                            socketChannel.pipeline().addLast(new ProtobufVarint32LengthFieldPrepender());
                            socketChannel.pipeline().addLast(new ProtobufEncoder());
                            socketChannel.pipeline().addLast(new ProtobufClientHandler());
                        }
                    });
            //发起异步连接操作
            ChannelFuture f= b.connect(host,port).sync();
            //等待异步连接操作
            f.channel().closeFuture().sync();
        }catch (Exception e){
           e.printStackTrace();
        } finally{
            group.shutdownGracefully();
            System.out.println("优雅关闭");
        }
    }

    public static void main(String[] args) throws Exception {
        int port = 8080;
        if(args!=null&&args.length>0){
            try{
                port = Integer.valueOf(args[0]);
            }catch (NumberFormatException e){

                e.printStackTrace();
                port = 8081;
            }
        }
        new ProtobufClient().connect(port,"127.0.0.1");
        System.out.println("启动完成");
    }
}

客户端处理器

public class ProtobufClientHandler extends ChannelHandlerAdapter {


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

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

        System.out.println("收到服务端的返回:"+msg);

    }

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

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {

        System.out.println("第一次连接");
        for(int i = 0;i<10;i++){
            ctx.writeAndFlush(subReq(i));
        }
        ctx.flush();

    }

    private LoginRequest.LoginCommand subReq(int i) {

        LoginRequest.LoginCommand.Builder builder = LoginRequest.LoginCommand.newBuilder();
        builder.setUsername("zml");
        return builder.build();

    }
}

ProtobufDecoder仅仅负责解码,它不支持读半包。因此,在ProtobufDecoder前面,一点要有能够读半包的解码器,有一下三种方式可以选择。

  • 使用Netty提供的ProtobufVarint32FrameDecoder,它可以处理半包消息。
  • 继承Netty提供的通用半包解码器LengthFieldBasedFrameDecoder。
  • 继承ByteToMessageDecoder类,自己处理半包消息。

netty extend用法 tcp编码

 BaseCommand.HitCommand.Builder builder = BaseCommand.HitCommand.newBuilder();
            builder.setDamage(10);
            builder.setEnemyId(2);
            builder.setId(1);
            BaseCommand.HitCommand build = builder.build();
            BaseCommand.ServerCommand.Builder builder1 = BaseCommand.ServerCommand.newBuilder();
            builder1.setCommandType(BaseCommand.CommandType.Hit);

            builder1.setExtension(BaseCommand.HitCommand.hitCommand,build);

            channel.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer(builder1.build().toByteArray()),new InetSocketAddress("127.0.0.1",port)));

解码

ByteBuf data = datagramPacket.content();
        byte[] b = new byte[data.readableBytes()];
        data.readBytes(b);
        ExtensionRegistry registry = ExtensionRegistry.newInstance();
        BaseCommand.registerAllExtensions(registry);

        BaseCommand.ServerCommand serverCommand = BaseCommand.ServerCommand.parseFrom(b,registry);
        System.out.println("commandType:"+serverCommand.getCommandType());
        BaseCommand.HitCommand extension = serverCommand.getExtension(BaseCommand.HitCommand.hitCommand);
        System.out.println("damage:"+extension.getDamage());
        System.out.println("enemyId:"+extension.getEnemyId());
        System.out.println("id:"+extension.getId());