Protobuf基础使用

Protobuf是什么

在我们日常编写代码的过程中,经常会涉及到网络传输的部分。我们通常会在网络之间传递各种各样的请求,但是在我们日常架构之中,经常会涉及后端服务器之间的通信,通信过程中,可能传递的对象就是一个类。这种情况下我们该如何在网络通信中传递类和对象的内容呢。目前市面上的方法也有很多,比如Json序列化,XML进行类的传递。但是一旦涉及到音视频和图片的传递之后该怎么办呢。有一种办法是把图片,音视频进行序列化成二进制数据后进行一个传输。

下列类就是基于java标准库提供的ObjectOutputStream实现的一个简易实现对象序列化的代码

package com.czl.myRabbitMq.commom;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
//将一个对象进行序列化和反序列化 java中的对象都可以通过一下逻辑进行序列化 但前提是实现接口 Serializable
@Slf4j
public class BinaryTransformTool {
    /**
     * 这样写是设置版本号的意思
     * 就是比如我今天定义了一个Message对象 对其进行了序列化 并存入了文件中
     * 要是第二天有人对Message对象进行修改 然后我如果读取该序列化数据 很大概率
     * 是会报错的 为了防止上述情况发生 这里定义一个版本表示 表示如果我今天定义版本一
     * 然后将数据序列化写入文件的时候 这个1这个数据就被保存下来了 要是有人对Message对象
     * 进行了修改 就手动加一 这样反序列化的时候就能防止读取旧版本数据了
     *
     * 这个属性一般加在进行序列化的对象中 而不是这里!!!!!!!!!!!!!!!!
     */
    private static final long serialVersionUID = 1L;
    //将对象进行序列化操作
    @SneakyThrows
    public static byte[] getBinaryData(Object data){
        //因为我们不知道将对象转为二进制数据后 这个数据的长度是几个字节  所以我们使用
        //ByteArrayOutputStream 这个对象 这个对象本质是一个可变长的 字节数组
        try(ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
            //java标准库中提供了将对象序列化的类 就是 ObjectOutputStream 后面构造器中传入的参数就是
            //序列化时将数据写入的地方 这里传入的是byteArrayOutputStream 所以写入这个对象里面
            //要是传入的是文件 则写入文件里面
            try (ObjectOutputStream outputStream = new ObjectOutputStream(byteArrayOutputStream)) {
                outputStream.writeObject(data);
            }
            return byteArrayOutputStream.toByteArray();
        }
    }
    //将二进制数据转化为对象
    @SneakyThrows
    public static Object fromData(byte[] data){
        Object object;
          try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(data)){
              try(ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream)){
                   object = objectInputStream.readObject();
              }
              return object;
          }
    }
}

但是上述代码是将一个对象和类进行序列化生成一个byte数组,有很多序列化的细节是没有实现的。特别是针对某一个类和对象的某个属性单独进行序列化。所以Protobuf是一个可以快速生成将对象序列化代码的一个组件。
以下是官方对Protobuf的说明以及翻译

Protocol Buffers 是 Google 的⼀种语⾔⽆关、平台⽆关、可扩展的序列化结构数据的⽅法,它可⽤
于(数据)通信协议、数据存储等。
Protocol Buffers 类⽐于 XML,是⼀种灵活,⾼效,⾃动化机制的结构数据序列化⽅法,但是⽐
XML 更⼩、更快、更为简单。
你可以定义数据的结构,然后使⽤特殊⽣成的源代码轻松的在各种数据流中使⽤各种语⾔进⾏编写和
读取结构数据。你甚⾄可以更新数据结构,⽽不破坏由旧数据结构编译的已部署程序。

Protobuf的基础使用

在maven项目中使用protobuf之前需要引入相关的依赖 下面的不是依赖是一个protobuf的插件用来快速对protobuf进行编译的。

<dependencies>
        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java</artifactId>
            <version>4.26.0</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.xolstice.maven.plugins</groupId>
                <artifactId>protobuf-maven-plugin</artifactId>
                <version>0.6.1</version>
                <configuration>
                    <!-- 本地protoc.exe的目录-->
                    <protocExecutable>C:\protobuf\protobuf26.0\bin\protoc.exe</protocExecutable>
                    <!-- proto文件放置的位置-->
                    <protoSourceRoot>${project.basedir}/src/main/java/proto</protoSourceRoot>
                    <!-- 生成的java文件的目录-->
                    <outputDirectory>${project.basedir}/src/main/java</outputDirectory>
                    <!-- 在编译的时候是否删除目标目录下的文件 默认为true 最好为false 以免误删-->
                    <clearOutputDirectory>false</clearOutputDirectory>
                </configuration>
            </plugin>
        </plugins>
    </build>

首先我们先创建一个新的maven工程,然后在main/java目录下创建出一个proto目录用来存放我们的proto文件

此时我们可以进入proto文件中进行一个protobuf的代码编写了
在proto文件的首行中我们一般会指定当前proto文件使用的语法规则,(要是不指定的话默认使用的是proto2) proto3是最新版本的语法在proto2的基础上进行了优化,便于使用。

首行指定完语法之后,第二行一般会为当前proto文件指定一个名称空间(唯一) 用来标识当前文件和其他文件不冲突 

后面紧接着一般是选项 option后面带上一些参数

定义完之后就可以定义我们要生成的类的属性了,他就会自动帮我们生成当前类的序列化和反序列化方法。  在下面的代码中我们定义了一个学生类 其中带有2个属性 一个是name 一个是age,但是注意此时name后面跟的那个1不是name的值,而是字段的一个编号,且当前标号是不能重复的。

当前文件配置了三个选项,具体含义全在注释中

//首行proto 希望我们为当前文件指定一个版本
syntax = "proto3";
//表示当前proto文件的一个命名空间 避免定义的消息进行一个冲突
package start;
//是否可以将编译之后的Java代码分为多个文件进行存放 true 可以 false 不行
option java_multiple_files = true;//不配置这个只会生成一个文件
//想要生成的Java文件的package
option java_package = "com.ex.test1";
//编译完文件之后生成的类名
option java_outer_classname = "protoMy";


//定义自定义的字段
//定义格式:
//定义类段 字段名  = 字段唯一编号; 字段编号一旦被使用就不要轻易改变了
//19000 - 19999 是源码的预留号 一般不设置这个区间的号码
//1到15之间的字段一般用来标记出现非常频繁的字段,为将来有可能添加的,频繁出现的字段预留一些出来
//因为在序列化的过程中,不仅仅要把值编译进去,还需要把对应的编号也编译进去,而1到15只需要一个字节即可编码了
//16-2047需要2个字节

message MyTestStudent{
  string name = 1; //这个1不是字段的值 是字段的编号。 同时这个字段的编号不能重复
  int32 age = 2;
}

写完proto文件之后我们就可以开始编译了

protoc [--proto_path=IMPORT_PATH] --java_out=DST_DIR path/to/file.proto
在cmd中输入上述指令就可以编译了,但是这段指令的参数一大堆,是很麻烦的,这里就不得不提到开始引入依赖时,我们配置的快速编译插件了。

点击maven的图标 ,点击刷新找到出现的protobuf。就可以进行编译了 

可以看到在编译结束之后,出现了2个类和一个接口,其中咱们在proto文件中定义的MyTestStudent就自动生成出来了

我只写了2个参数生成代码都600行,所以这里我们不展示代码了 ,重点展示一下其中比较核心的内容。

可以看到这个类中,有一个内部类build

可以看到里面封装了关于我们描述属性的get和set方法,可以自己去瞅瞅太多了,这里不展示了

同时还有一个build方法可以把我们构造的属性构造成一个对象并返回

所以也就以为着我们需要使用builder对象来设置我们需要类的属性,并通过其中的build方法具体进行类的构造。

可以看到我们生成的类是继承了com.google.protobuf.GeneratedMessage这个对象

同时这个对象又继承了AbstractMessage这个对象

 AbstractMessage又继承了AbstractMessageLite这个对象,对象的序列化方法就在AbstractMessageLite中的

所以我们只需要调用我们生成类方法中的toByteArray就可以实现对象序列化了。

代码展示

下面是一段简单的使用代码 其中描述了我们对一个MyTestStudent对象进行序列化打印处序列化内容之后,在进行反序列化并打印出反序列化的结果。

import com.ex.test1.MyTestStudent;
import com.google.protobuf.InvalidProtocolBufferException;
public class Main {
    public static void main(String[] args) throws InvalidProtocolBufferException {
        MyTestStudent myTestStudent = MyTestStudent.newBuilder()
                .setAge(18)
                .setName("张三")
                .build();
        byte[] by = myTestStudent.toByteArray();
        System.out.println(by.toString());
        MyTestStudent student = MyTestStudent.parseFrom(by);
        System.out.println(student.getName());
    }
}

下一篇写protobuf的语法和其他使用规则 

  • 44
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
### 回答1: ROS2是一种用于构建机器人系统的框架,而Protobuf(Protocol Buffers)是一种用于序列化结构化数据的语言无关、平台无关、可扩展的机制。下面我将说明ROS2为什么使用Protobuf。 首先,ROS2采用Protobuf可以实现轻量级的通信。Protobuf使用二进制编码,将数据压缩成更小的字节序列,从而减少了通信的带宽占用和延迟,提高了数据传输的效率。对于机器人系统来说,特别是在带宽有限的情况下,这一点非常重要。 其次,Protobuf提供了可扩展的消息定义。在ROS2系统中,消息是通过话题进行传输的,定义了ROS2主题中的数据结构。使用Protobuf,我们可以快速定义自定义消息类型,并根据需要进行拓展,从而满足不同应用场景的需求。这为ROS2系统的灵活性和可扩展性提供了基础。 此外,Protobuf还支持语言无关的消息定义和序列化。这意味着我们可以使用不同的编程语言来编写ROS2节点,而不必担心消息格式的一致性。Protobuf提供了生成与多种编程语言兼容的代码的工具,使得消息的序列化和反序列化变得简单可靠,而不会受制于特定编程语言的限制。 总之,ROS2之所以选择使用Protobuf,是因为Protobuf提供了高效的通信机制、可扩展的消息定义和跨语言的兼容性,使得ROS2系统更加灵活、可靠和可扩展。这些特性让ROS2适用于各种机器人应用,并为机器人系统的开发和部署提供了方便。 ### 回答2: ROS 2是一个功能强大的机器人操作系统,其使用了不同的数据传输协议来实现分布式通信。其中,Protocol Buffers(protobuf)是ROS 2中一种常用的数据序列化和反序列化机制。 ROS 2使用protobuf的主要原因是其高效的数据传输和封装能力。protobuf是一种二进制压缩的序列化格式,它可以有效地将ROS 2中的数据结构转换成紧凑的字节流,并在节点之间进行高效的传输。相比于其他传输方式,protobuf具有更小的数据大小和更快的传输速度,适用于具有高带宽和低延迟要求的ROS 2应用。 另外,protobuf还支持自动化的代码生成,使得在ROS 2中使用protobuf更加方便。通过使用protobuf编写消息定义文件(.proto),可以根据这些文件自动生成对应的消息类代码。这减少了手动编写数据结构和序列化/反序列化逻辑的工作量,提高了开发效率。 使用ROS 2与protobuf结合还具有跨语言的优势。由于protobuf支持多种编程语言,节点可以使用不同的编程语言实现。这使得ROS 2更加灵活和可扩展,并且可以与不同的外部系统进行集成。 总之,ROS 2使用protobuf是为了实现高效、快速的数据传输和封装,减少开发工作量,并提供跨语言的支持。通过使用protobuf,ROS 2可以更好地满足复杂机器人系统对数据交换的要求。 ### 回答3: ROS 2是一个灵活的机器人操作系统,可以使用多种通信协议进行通信,其中之一就是protobufprotobuf是一种轻量级高效的数据序列化协议,它可以将结构化数据进行编码和解码,以便在网络上进行传输。 使用protobuf作为ROS 2的通信机制有几个好处。首先,protobuf的编码和解码效率很高,可以大大减少数据在网络上的传输时间和带宽消耗。这对于机器人应用来说非常重要,因为机器人通常需要在实时性要求较高的情况下进行数据传输。 其次,protobuf提供了一种定义数据结构的方法,可以通过编写.proto文件来描述数据的类型和字段。这种方式使得数据结构的定义更加清晰和可维护,减少了开发过程中的错误和混乱。 另外,ROS 2支持使用ROS 1的消息定义,而protobuf是一种常见的消息定义语言,因此可以方便地将ROS 1的消息定义转换为protobuf消息定义,实现ROS 1和ROS 2之间的平滑过渡。 最后,ROS 2的通信层是可插拔的,可以根据需要选择不同的传输协议和编码方式。通过使用protobuf,可以在ROS 2中实现自定义的数据序列化和传输方式,以满足特定应用的需求。 总而言之,使用protobuf作为ROS 2的通信机制可以提高数据传输效率、简化数据结构定义、支持平滑过渡和灵活性。这使得ROS 2成为一个更加强大和可扩展的机器人操作系统。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值