Netty-编码和解码

编码和解码的基本介绍

  1. 编写网络应用程序时,因为数据在网络中传输的都是二进制字节码数据,在发送数据时就需要编码,接收数据时就需要解码
  2. codec(编解码器) 的组成部分有两个:decoder(解码器)和 encoder(编码器)。encoder 负责把业务数据转换成字节码数据,decoder 负责把字节码数据转换成业务数据

在这里插入图片描述

Netty 本身的编码解码的机制和问题分析

  1. Netty 自身提供了一些 codec(编解码器)
  2. Netty 提供的编码器
    • StringEncoder,对字符串数据进行编码
    • ObjectEncoder,对 Java 对象进行编码
    • …
  3. Netty 提供的解码器
    • StringDecoder, 对字符串数据进行解码
    • ObjectDecoder,对 Java 对象进行解码
    • … 4) Netty 本身自带的 ObjectDecoder 和 ObjectEncoder 可以用来实现 POJO 对象或各种业务对象的编码和解码,底层使用的仍是 Java 序列化技术 , 而Java 序列化技术本身效率就不高,存在如下问题
    • 无法跨语言
    • 序列化后的体积太大,是二进制编码的 5 倍多。
    • 序列化性能太

Protobuf基本介绍和使用示意图

  1. Protobuf 是 Google 发布的开源项目,全称 Google Protocol Buffers,是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC[远程过程调用 remote procedure call ] 数据交换格式 。目前很多公司 http+json  tcp+protobuf
  2. 参考文档 : https://developers.google.com/protocol-buffers/docs/proto 语言指南
  3. Protobuf 是以 message 的方式来管理数据的.
  4. 支持跨平台、跨语言,即[客户端和服务器端可以是同的语言编写的] (支持目前绝大多数语言,例如 C++、C#、Java、python 等)
  5. 高性能,高可靠性
  6. 使用 protobuf 编译器能自动生成代码,Protobuf 是将类的定义使用.proto 文件进行描述。说明,在idea 中编写 .proto 文件时,会自动提示是否下载 .ptotot 编写插件. 可以让语法高亮。
  7. 然后通过 protoc.exe 编译器根据.proto 自动生成.java 文件
  8. protobuf 使用示意图
    在这里插入图片描述

使用

pom.xml

 <properties>
     <maven.compiler.source>8</maven.compiler.source>
     <maven.compiler.target>8</maven.compiler.target>
     <!--protobuf 版本号-->
     <protobuf.version>3.6.1</protobuf.version>
     <!--grpc版本号-->
 </properties>


 <dependencies>
     <dependency>
         <groupId>com.google.protobuf</groupId>
         <artifactId>protobuf-java-util</artifactId>
         <version>${protobuf.version}</version>
     </dependency>
 </dependencies>

 <build>
     <defaultGoal>package</defaultGoal>
     <extensions>
         <extension>
             <groupId>kr.motd.maven</groupId>
             <artifactId>os-maven-plugin</artifactId>
             <version>1.5.0.Final</version>
         </extension>
     </extensions>

     <plugins>
         <!-- protobuf 编译组件 -->
         <plugin>
             <groupId>org.xolstice.maven.plugins</groupId>
             <artifactId>protobuf-maven-plugin</artifactId>
             <version>0.5.1</version>
             <extensions>true</extensions>
             <configuration>
                 <protoSourceRoot>${project.basedir}/src/main/java/com/jhj/netty/proto</protoSourceRoot>
                 <protocArtifact>com.google.protobuf:protoc:3.2.0:exe:${os.detected.classifier}</protocArtifact>
             </configuration>
             <executions>
                 <execution>
                     <goals>
                         <goal>compile</goal>
                     </goals>
                 </execution>
             </executions>
         </plugin>

         <!-- 编译jar包的jdk版本 -->
         <plugin>
             <groupId>org.apache.maven.plugins</groupId>
             <artifactId>maven-compiler-plugin</artifactId>
             <version>3.1</version>
             <configuration>
                 <source>${java.version}</source>
                 <target>${java.version}</target>
             </configuration>
         </plugin>
     </plugins>
 </build>

安装插件

Protobuf

发送单一对象

Student.proto文件
syntax = "proto3";
option java_outer_classname = "StudentPOJO"; //生成的外部类名,同时也是文件名
message Student{
    int32 id=1; //Student 类中有 一个属性 名字为id 类型为 int32(proto 类型) 1表示属性序号,不是值
    string name=2;
}
利用插件转换

在这里插入图片描述

将target中编译后的class文件拿过来
客户端加入到pipeline
ch.pipeline().addLast("encoder",new ProtobufEncoder());//在pieline中加入ProtoBufEncoder
客户端发送一个对象
//发送一个student 对象
StudentPOJO.Student jhj =StudentPOJO.Student.newBuilder().setId(101).setName("jhj").build();

ctx.writeAndFlush(jhj);
服务端加入到piepline
ch.pipeline().addLast("decoder",new ProtobufDecoder(StudentPOJO.Student.getDefaultInstance()));//加入ProtoBufDecoder

服务端监听
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

    //读取从客户端发送的StudentPojo.student
    StudentPOJO.Student student = (StudentPOJO.Student) msg;

    System.out.println("客户端发送消息是:" + student.getId()+student.getName());

}

发送两个对象

protobuf文件
syntax = "proto3";
option optimize_for = SPEED; //加快解析
option java_package = "com.jhj.netty.protobuf2"; //指定生成到哪个包下
option java_outer_classname = "MyDataInfo"; //生成的外部类名,同时也是文件名

//protobuf 可以使用message 管理其他的message

message MyMessage{
    //定义一个枚举类型
    enum DataType{
        StudentType=0;//在prop3 要求enum的编号从0开始
        WorkerType=1;
    }

    //用data_type 来标识传的是哪一个枚举类型
    DataType data_type = 1;

    //标识每次枚举类型最多只能出现其中的一个,节省空间
    oneof dataBody{
        Student student = 2;
        Worker worker = 3;
    }
}

message Student{
    int32 id=1; //Student 类中有 一个属性 名字为id 类型为 int32(proto 类型) 1表示属性序号,不是值
    string name=2;
}

message Worker{
    string name=1;
    int32 age=2;
}
将target中编译后的class文件拿过来
客户端加入到pipeline
ch.pipeline().addLast("encoder",new ProtobufEncoder());//在pieline中加入ProtoBufEncoder
客户端发送一个对象
 @Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {

     //随机发送Student 或者 Worker对象
     int random = new Random().nextInt(3);
     MyDataInfo.MyMessage myMessage=null;
     if (0==random){
         myMessage=MyDataInfo.MyMessage.newBuilder().setDataType(MyDataInfo.MyMessage.DataType.StudentType).setStudent(MyDataInfo.Student.newBuilder().setId(5).setName("jhj student").build()).build();
     }else{
         myMessage=MyDataInfo.MyMessage.newBuilder().setDataType(MyDataInfo.MyMessage.DataType.WorkerType).setWorker(MyDataInfo.Worker.newBuilder().setAge(5).setName("jhj").build()).build();
     }
     ctx.writeAndFlush(myMessage);
 }
服务端加入到piepline
ch.pipeline().addLast("decoder",new ProtobufDecoder(MyDataInfo.MyMessage.getDefaultInstance()));//加入ProtoBufDecoder

服务端监听
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

     //读取从客户端发送的StudentPojo.student
     MyDataInfo.MyMessage myMessage = (MyDataInfo.MyMessage) msg;
     MyDataInfo.MyMessage.DataType dataType = myMessage.getDataType();
     if (dataType== MyDataInfo.MyMessage.DataType.StudentType){
         System.out.println("客户端发送消息是:" + myMessage.getStudent().getId()+myMessage.getStudent().getName());

     }else if (dataType== MyDataInfo.MyMessage.DataType.WorkerType) {
         System.out.println("客户端发送消息是:" + myMessage.getWorker().getAge()+myMessage.getWorker().getName());
     }else {
         System.out.println("参数不对");
     }

 }

作者声明

如有问题,欢迎指正!
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值