­

Netty編解碼之ProtoBuf案例二

  • 2020 年 2 月 10 日
  • 筆記

  前面我們介紹了Protobuf的基本使用,但是我們是一個POJO對象創建一個proto文件,那麼在實際環境中將要創建多個文件,會非常的不方便,本文我們來看看怎麼根據類型來動態處理

Protobuf案例二

proto文件

  在proto文件中我們通過message來管理類型,具體如下

syntax = "proto3";  option optimize_for = SPEED; // 加快解析  option java_package="com.dpb.netty.codec2";   //指定生成到哪個包下  option java_outer_classname="MyDataInfo"; // 外部類名, 文件名    //protobuf 可以使用message 管理其他的message  message MyMessage {        //定義一個枚舉類型      enum DataType {          StudentType = 0; //在proto3 要求enum的編號從0開始          WorkerType = 1;      }        //用data_type 來標識傳的是哪一個枚舉類型      DataType data_type = 1;        //表示每次枚舉類型最多只能出現其中的一個, 節省空間      oneof dataBody {          Student student = 2;          Worker worker = 3;      }    }      message Student {      int32 id = 1;//Student類的屬性      string name = 2; //  }  message Worker {      string name=1;      int32 age=2;  }

對應的POJO文件

  通過proto.exe文件來動態生成pojo文件。

服務端程式碼

  將生成的文件拷貝進項目中,然後編寫服務端程式碼。

package com.dpb.netty.codec2;    import com.dpb.netty.codec.StudentPojo;  import io.netty.bootstrap.ServerBootstrap;  import io.netty.channel.ChannelFuture;  import io.netty.channel.ChannelInitializer;  import io.netty.channel.ChannelOption;  import io.netty.channel.EventLoopGroup;  import io.netty.channel.nio.NioEventLoopGroup;  import io.netty.channel.socket.SocketChannel;  import io.netty.channel.socket.nio.NioServerSocketChannel;  import io.netty.handler.codec.protobuf.ProtobufDecoder;    /**   * @program: netty4demo   * @description:   * @author: 波波烤鴨   * @create: 2019-12-23 11:15   */  public class NettyServerDemo {      public static void main(String[] args) {          // 創建對應的 執行緒池          // 創建Boss group          EventLoopGroup boosGroup = new NioEventLoopGroup(1);          // 創建 workgroup          EventLoopGroup workGroup = new NioEventLoopGroup();          // 創建對應的啟動類          ServerBootstrap bootstrap = new ServerBootstrap();          try{              // 設置相關的配置資訊              bootstrap.group(boosGroup,workGroup) // 設置對應的執行緒組                      .channel(NioServerSocketChannel.class) // 設置對應的通道                      .option(ChannelOption.SO_BACKLOG,1024) // 設置執行緒的連接個數                      .childHandler(new ChannelInitializer<SocketChannel>() { // 設置                          /**                           * 給pipeline 設置處理器                           * @param socketChannel                           * @throws Exception                           */                          @Override                          protected void initChannel(SocketChannel socketChannel) throws Exception {                              // 指定Protobuf解碼                              socketChannel.pipeline().addLast("decoder"                                      ,new ProtobufDecoder(MyDataInfo.MyMessage.getDefaultInstance()));                              socketChannel.pipeline().addLast(new NettyServerHandler());                          }                      });              System.out.println("服務啟動了....");              // 綁定埠  啟動服務              ChannelFuture channelFuture = bootstrap.bind(6668).sync();              // 對關閉通道進行監聽              channelFuture.channel().closeFuture().sync();          }catch (Exception e){            }finally {              // 優雅停服              boosGroup.shutdownGracefully();              workGroup.shutdownGracefully();          }        }  }
package com.dpb.netty.codec2;    import com.dpb.netty.codec.StudentPojo;  import io.netty.buffer.Unpooled;  import io.netty.channel.ChannelHandlerContext;  import io.netty.channel.SimpleChannelInboundHandler;  import io.netty.util.CharsetUtil;    /**   * @program: netty4demo   * @description:   * @author: 波波烤鴨   * @create: 2019-12-23 11:24   */  public class NettyServerHandler extends SimpleChannelInboundHandler<MyDataInfo.MyMessage> {            @Override      protected void channelRead0(ChannelHandlerContext channelHandlerContext, MyDataInfo.MyMessage data) throws Exception {          // 得根據不同的類型來獲取對應的數據          MyDataInfo.MyMessage.DataType type = data.getDataType();          if(type == MyDataInfo.MyMessage.DataType.StudentType){              // 表示傳遞過來的是 Student類型              System.out.println("學生資訊:" + data.getStudent().getId() + " " + data.getStudent().getName());          }else if(type == MyDataInfo.MyMessage.DataType.WorkerType){              // 表示傳遞的是 worker類型              System.out.println("worker資訊:" + data.getWorker().getName() + " " + data.getWorker().getAge());          }else{              System.out.println("類型不匹配.... ");          }      }        /**       * 讀取客戶端發送數據完成後的方法       *    在本方法中可以發送返回的數據       * @param ctx       * @throws Exception       */      @Override      public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {          // writeAndFlush 是組合方法          ctx.writeAndFlush(Unpooled.copiedBuffer("你好啊,客戶端....^_^",CharsetUtil.UTF_8));      }  }

注意解碼器的位置

客戶端程式碼

package com.dpb.netty.codec2;    import io.netty.bootstrap.Bootstrap;  import io.netty.channel.ChannelFuture;  import io.netty.channel.ChannelInitializer;  import io.netty.channel.EventLoopGroup;  import io.netty.channel.nio.NioEventLoopGroup;  import io.netty.channel.socket.SocketChannel;  import io.netty.channel.socket.nio.NioSocketChannel;  import io.netty.handler.codec.protobuf.ProtobufEncoder;    /**   * @program: netty4demo   * @description:   * @author: 波波烤鴨   * @create: 2019-12-23 11:31   */  public class NettyClientDemo {        public static void main(String[] args) {          // 客戶端就只需要創建一個 執行緒組了          EventLoopGroup loopGroup = new NioEventLoopGroup();          // 創建 啟動器          Bootstrap bootstrap = new Bootstrap();          try{              // 設置相關的參數              bootstrap.group(loopGroup)                      .channel(NioSocketChannel.class)                      .handler(new ChannelInitializer<SocketChannel>() {                          @Override                          protected void initChannel(SocketChannel socketChannel) throws Exception {                              // 指定protobu編碼                              socketChannel.pipeline().addLast("encoder",new ProtobufEncoder());                              socketChannel.pipeline().addLast(new NettyClientHandler());                          }                      });              // 連接服務              ChannelFuture future = bootstrap.connect("localhost",6668).sync();              // 對服務關閉 監聽              future.channel().closeFuture().sync();          }catch (Exception e){            }finally {              loopGroup.shutdownGracefully();          }        }  }
package com.dpb.netty.codec2;    import com.dpb.netty.codec.StudentPojo;  import io.netty.buffer.ByteBuf;  import io.netty.channel.ChannelHandlerContext;  import io.netty.channel.ChannelInboundHandlerAdapter;  import io.netty.util.CharsetUtil;    import java.util.Random;    /**   * @program: netty4demo   * @description:   * @author: 波波烤鴨   * @create: 2019-12-23 11:36   */  public class NettyClientHandler extends ChannelInboundHandlerAdapter {        /**       * 連接上服務的回調方法       * @param ctx       * @throws Exception       */      @Override      public void channelActive(ChannelHandlerContext ctx) throws Exception {          // 發送數據          //隨機的發送Student 或者 Workder 對象          int random = new Random().nextInt(3);          MyDataInfo.MyMessage myMessage = null;            if(0 == random) { //發送Student 對象                myMessage = MyDataInfo.MyMessage.newBuilder().setDataType(MyDataInfo.MyMessage.DataType.StudentType).setStudent(MyDataInfo.Student.newBuilder().setId(666).setName("波波烤鴨").build()).build();          } else { // 發送一個Worker 對象                myMessage = MyDataInfo.MyMessage.newBuilder().setDataType(MyDataInfo.MyMessage.DataType.WorkerType).setWorker(MyDataInfo.Worker.newBuilder().setAge(20).setName("鄧師傅").build()).build();          }            ctx.writeAndFlush(myMessage);      }        /**       * 讀取服務端返回的資訊       * @param ctx       * @param msg       * @throws Exception       */      @Override      public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {          ByteBuf buf  = (ByteBuf) msg;          System.out.println("服務端返回的資訊:" + buf.toString(CharsetUtil.UTF_8));      }  }

測試

  先啟動伺服器,然後啟動多個客戶端。

通過輸出結果我們可以看到,伺服器可以根據不同的類型獲取到對應的POJO對象中的數據,會比原來單一的處理要更加的靈活些!