Fork me on GitHub

Protobuf

1.Protobuf基本介绍和使用示意图

  • Protobuf 是 Google 发布的开源项目,全称 Google Protocol Buffers,是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC[远程过程调用 remote procedure call ] 数据交换格式 。目前很多公司 http+json  tcp+protobuf.
  • 参考文档 : https://developers.google.com/protocol-buffers/docs/proto 语言指南.
  • Protobuf 是以 message 的方式来管理数据的.
  • 支持跨平台、跨语言,即[客户端和服务器端可以是不同的语言编写的(支持目前绝大多数语言,例如 C++、C#、Java、python 等)

2.Protobuf使用示意图

 

 

 

 

 

3.代码

1.编写.proto文件

//版本号
syntax = "proto3";
//加快解析
option optimize_for = SPEED; // 加快解析
//指定生成包下
//option java_package="com.hyy.netty.proto.moreObj";
//外部类名
option java_outer_classname = "MyData";
//内部类-1
message Student {
    int32 id = 1; //属性,java的int类型
    string name = 2; //java的String类型
}
//内部类-2
message Worker {
    string name = 1; //java的String类型
    int32 id = 2; //属性,java的int类型
}
//内部类-3,用来管理其他类型
message MyMessage {
    //定义枚举类
    enum DataType {
        StudentType = 0; //在proto3中枚举从0开始
        WorkerType = 1;
    }
    //用来标识传的是哪个枚举类型
    DataType date_type = 1;
    //每次只能出现一个枚举类型
    oneof dataOneType {
        Student student = 2;
        Worker worker = 3;
    }

}

 

2.使用protoc.exe编译后的java文件(部分)

public final class MyData {
  private MyData() {}
  public static void registerAllExtensions(
      com.google.protobuf.ExtensionRegistryLite registry) {
  }
.....代码过长,省略
}

 

3.服务端

public class MyServer {
    public static void main(String[] args) throws InterruptedException {
        NioEventLoopGroup boss = new NioEventLoopGroup();
        NioEventLoopGroup worker = new NioEventLoopGroup();
     try{
         ServerBootstrap serverBootstrap=new ServerBootstrap();
         serverBootstrap.group(boss,worker)
                 .channel(NioServerSocketChannel.class)
                 .childHandler(new ChannelInitializer<SocketChannel>() {
                     @Override
                     protected void initChannel(SocketChannel ch) throws Exception {
                         ChannelPipeline pipeline = ch.pipeline();
                         /**
                          * 加入谷歌解码器ProtobufDecoder,需要指定对象
                          * ProtobufDecoder(MyDataInfo.Student.getDefaultInstance())
                          * 指定类的内部类对象
                          */
                         pipeline.addLast(new ProtobufDecoder(MyData.MyMessage.getDefaultInstance()));
                         pipeline.addLast(new ServerHandler());
                     }
                 });
         ChannelFuture channelFuture = serverBootstrap.bind(7000).sync();
         channelFuture.channel().closeFuture().sync();

     }finally {
         boss.shutdownGracefully();
         worker.shutdownGracefully();

     }

    }
}

 

4.服务端处理类

public class ServerHandler extends SimpleChannelInboundHandler<MyData.MyMessage> {

    /**
     *
     * 读取数据实际(这里我们可以读取客户端发送的消息)
     * @param ctx the {@link ChannelHandlerContext} which this {@link SimpleChannelInboundHandler}
     *            belongs to
     * @param msg the message to handle
     * @throws Exception is thrown if an error occurred
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, MyData.MyMessage msg) throws Exception {
        //根据客户端定义的DataType,显示不同的信息
        MyData.MyMessage.DataType dataType=msg.getDateType();
        if (dataType==MyData.MyMessage.DataType.StudentType){
            MyData.Student student=msg.getStudent();
            System.out.println("学生名字:"+student.getName()+",学生ID:"+student.getId());

        }else if (dataType==MyData.MyMessage.DataType.WorkerType){
            MyData.Worker worker=msg.getWorker();
            System.out.println("工人名字:"+worker.getName()+",工人ID:"+worker.getId());

        }else {
            System.out.println("类型不对");
        }

    }

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

        //writeAndFlush 是 write + flush
        //将数据写入到缓存,并刷新
        //一般讲,我们对这个发送的数据进行编码
        //发送给客户端消息
        ctx.writeAndFlush(Unpooled.copiedBuffer("hello, 客户端!!!!我是服务端.......", CharsetUtil.UTF_8));
    }

    //处理异常, 一般是需要关闭通道

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

 

5.客户端

public class MyClient {
    public static void main(String[] args) throws InterruptedException {
        //创建一个事件循环组
        NioEventLoopGroup eventExecutors = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(eventExecutors)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            //加入谷歌编码器
                            pipeline.addLast(new ProtobufEncoder());
                            //加入自定义处理类
                            pipeline.addLast(new MyClientHandler());
                        }
                    });
            ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 7000).sync();
            //监视关闭
            channelFuture.channel().closeFuture().sync();
        } finally {
            eventExecutors.shutdownGracefully();
        }

    }
}

 

6.客户端处理类

public class MyClientHandler extends ChannelInboundHandlerAdapter {
    //当通道就绪就会触发该方法
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        //随机发送Student/worker对象
        int random = new Random().nextInt(3);
        MyData.MyMessage myMessage = null;
        if (0 == random) {
            myMessage = MyData.MyMessage.newBuilder()
                    .setDateType(MyData.MyMessage.DataType.StudentType)
                    .setStudent(MyData.Student.newBuilder().setId(1).setName("张三"))
                    .build();
        } else {
            myMessage = MyData.MyMessage.newBuilder()
                    .setDateType(MyData.MyMessage.DataType.WorkerType)
                    .setWorker(MyData.Worker.newBuilder().setId(2).setName("李四"))
                    .build();

        }
        ctx.writeAndFlush(myMessage);


    }
}

 

posted @ 2020-06-12 17:40  亲爸爸  阅读(546)  评论(0编辑  收藏  举报