從零開始netty學習筆記之protobuf

zhumeilu發表於2017-12-14

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());

複製程式碼

相關文章