Protobuf语法介绍

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Protobuf语法介绍相关的知识,希望对你有一定的参考价值。

参考技术A

我们先看看官方文档给出的定义和描述

简单来讲, ProtoBuf 是结构数据序列化方法,可简单类比于,其具有以下特点:

在protobuf中,协议是由一系列的消息组成的。因此最重要的就是定义通信时使用到的消息格式。一个Protobuf 消息(对应JAVA类),由至少一个字段(对应Java类属性)组合而成。

消息的定义很简单,就是message关键字加上消息的名字

字段定义格式:

限定修饰符 | 数据类型 | 字段名称 | = | 字段编码

required:

表示是一个必须字段,必须相对于发送方,在发送消息之前必须设置该字段的值,对于接收方,必须能够识别该字段的意思。发送之前没有设置required字段或者无法识别required字段都会引发编解码异常,导致消息被丢弃。

optional:

表示是一个可选字段,可选对于发送方,在发送消息时,可以有选择性的设置或者不设置该字段的值。对于接收方,如果能够识别可选字段就进行相应的处理,如果无法识别,则忽略该字段,消息中的其它字段正常处理。---因为optional字段的特性,很多接口在升级版本中都把后来添加的字段都统一的设置为optional字段,这样老的版本无需升级程序也可以正常的与新的软件进行通信,只不过新的字段无法识别而已,因为并不是每个节点都需要新的功能,因此可以做到按需升级和平滑过渡。

repeated:

表示该字段可以包含0~N个元素。其特性和optional一样,但是每一次可以包含多个值。可以看作是在传递一个数组的值。类比于Java这边的List

protobuf定义了一套基本的数据类型,几乎都映射了Java语言的基础数据类型(主要以Java为例)

详见下面表格

字段的命名方式与Java的命名方式大致一致

字段编码是一个序列化和反序列化的标记值,有了该值,通信双方才能互相识别对方的字段。当然相同的编码值,其限定修饰符和数据类型必须相同。编码值的取值范围为 1~2^32(4294967296)

其中 1~15的编码时间和空间效率都是最高的,编码值越大,其编码的时间和空间效率就越低(相对于1-15),当然一般情况下相邻的2个值编码效率的是相同的,除非2个值恰好实在4字节,12字节,20字节等的临界区。比如15和16

1900~2000编码值为Google protobuf 系统内部保留值,建议不要在自己的项目中使用。protobuf 还建议把经常要传递的值把其字段编码设置为1-15之间的值

完整的消息定义示例

枚举的定义和Java 相同,使用 enum 关键字,但是有一些限制。

枚举值必须大于等于0的整数。

使用分号 ; 分隔枚举变量而不是Java 语言中的逗号 ,

示例:

你可以将其他消息类型用作字段类型。已我们现在IM的为例,在每一个CSSendLiveRoomMsgReq消息中包含ImMsgBody消息,此时可以在相同的.proto文件中定义一个ImMsgBody消息类型,然后在CSSendLiveRoomMsgReq消息中指定一个ImMsgBody类型的字段

示例:

如果你的消息中有很多可选字段, 并且同时至多一个字段会被设置, 你可以加强这个行为,使用oneof特性节省内存,Oneof字段就像可选字段, 除了它们会共享内存, 至多一个字段会被设置。 设置其中一个字段会清除其它字段。

为了在.proto定义Oneof字段, 你需要在名字前面加上oneof关键字, 比如下面例子的ImMsgBody:

oneof的特性

protobuf介绍和语法

目录

前言

语法

标识符 

字段

字段类型

proto2和proto3区别


前言

        Protobuf即Protocol Buffers,是Google公司开发的一种跨语言和平台的序列化数据结构的方式,是一个灵活的、高效的用于序列化数据的协议。
        与XML和JSON格式相比,protobuf更小、更快、更便捷。protobuf是跨语言的,并且自带一个编译器(protoc),只需要用protoc进行编译,就可以编译成Java、Python、C++、C#、Go等多种语言代码,然后可以直接使用,不需要再写其它代码,自带有解析的代码。
        只需要将要被序列化的结构化数据定义一次(在.proto文件定义),便可以使用特别生成的源代码(使用protobuf提供的生成工具)轻松的使用不同的数据流完成对结构数据的读写操作。甚至可以更新.proto文件中对数据结构的定义而不会破坏依赖旧格式编译出来的程序。

Protobuf的优点如下:

  • 性能号,效率高
    序列化后字节占用空间比XML少3-10倍,序列化的时间效率比XML快20-100倍。
  • 有代码生成机制
    将对结构化数据的操作封装成一个类,便于使用。
  • 支持向后和向前兼容
    当客户端和服务器同时使用一块协议的时候, 当客户端在协议中增加一个字节,并不会影响客户端的使用
  • 支持多种编程语言
    Protobuf目前已经支持Java,C++,Python、Go、Ruby等多种语言。

Protobuf的缺点如下:

  • 二进制格式导致可读性差
  • 缺乏自描述

语法

        protobuf协议文件名后缀名为.proto。一个简单的protobuf协议如下:

  1 syntax="proto3";
  2 
  3 package protobuf.addressbook;
  4 
  5 enum PhoneType
  6 
  7   MOBILE = 0;
  8   HOME = 1;
  9   WORK = 2;
 10 
 11 
 12 message Person
 13 
 14   optional string name = 1;
 15   optional uint32 age = 2;
 16   optional string email = 3;
 17 
 18   message PhoneNumber
 19   
 20     optional string number = 1;
 21     optional PhoneType type = 2;
 22   
 23 
 24   repeated PhoneNumber phone = 4;                                                                                                                                                                                                      
 25                                            
 26                                           
 27                                            
 28                              
 29 message AddressBook          
 30                             
 31   repeated Person person = 1;
 32 

标识符 

  • syntax:标识使用的protobuf是哪个版本。上面表示使用的是3.x版本。
  • package:标识生成目标文件的包名。在C++中表示的是命名空间。上面。表示生成的类和函数在protobuf命名空间的addressbook命令空间下。
  • enum:表示一个枚举类型。会在目标.h文件中自动生成一个枚举类型。
  • message:标识一条消息。会在目标文件中自动生成一个类。

字段

        字段格式:

        role type name = tag [default value]

role 有三种取值:

        required:该字段必须给值,不能为空。否则message被认为是未初始化的。如果试图建立一个未初始化的message将会抛出RuntimeException异常,解析未初始化的message会抛出IOException异常。

        optional:表示该字段是可选值,可以为空。如果不设置,会设置一个默认值。也可以自定义默认值。如果没有自定义默认值,会是用系统默认值。

        repeated:表示该字段可以重复,可等同于动态数组。

注意:required字段是永久性的,如果之后不使用该字段,或者该字段标识改为optional或repeated,那么使用就接口读取新协议时,如果发现没有该字段,会认为该消息不完整,会拒收或者丢弃该消息。

字段类型

N 表示打包的字节并不是固定。而是根据数据的大小或者长度。

例如int32,如果数值比较小,在0~127时,使用一个字节打包。

关于枚举的打包方式和uint32相同。

关于message,类似于C语言中的结构包含另外一个结构作为数据成员一样。

关于 fixed32 和int32的区别。fixed32的打包效率比int32的效率高,但是使用的空间一般比int32多。因此一个属于时间效率高,一个属于空间效率高。根据项目的实际情况,一般选择fixed32,如果遇到对传输数据量要求比较苛刻的环境,可以选择int32.

proto2和proto3区别

        总的来说proto3比proto2支持跟多语言,但是更加简洁。去除了复杂的语法和特性。

  • 必须指明版本
syntax = "proto3";
  • 字段规则移除了"required",并把optional改名为了singlar,但是亲测,optional还可以用。

        实际在proto2中也不推荐使用required,因为该字段是永久性的。如果以后因为某种原因,想不用该字段,或者要将该字段改成optional或者repeated,那么使用旧接口读取新的协议时,如果发现没有该字段,他们会认为该字段是不完整的,会拒接接收该消息,或者直接丢弃。

  • “repeated”字段默认采用 packed 编码

        在 proto2 中,需要明确使用 [packed=true] 来为字段指定比较紧凑的 packed 编码方式。

  • 移除了 default 选项

        在 proto2 中,可以使用 default 选项为某一字段指定默认值。在 proto3 中,字段的默认值只能根据字段类型由系统决定。也就是说,默认值全部是约定好的,而不再提供指定默认值的语法。

        

        在字段被设置为默认值的时候,该字段不会被序列化。这样可以节省空间,提高效率。

但这样就无法区分某字段是根本没赋值,还是赋值了默认值。这在 proto3 中问题不大,但在 proto2 中会有问题。

  • 枚举类型的第一个字段约定必须为 0 
  • 移除了对分组的支持

分组的功能完全可以用消息嵌套的方式来实现,并且更清晰。

  • 移除了对扩展的支持,新增了 Any 类型

proto3 中新增的 Any 类型有点像 C/C++ 中的 void* 

  • 增加了 JSON 映射特性

  •  Map

        如果要创建一个关联映射,Protobuf提供了一种快捷的语法:

        key_type可以是任意Integer或者string类型(除了floating和bytes的任意标量类型都可以),value_type可以是任意类型,但不能是map类型。

map<key_type, value_type> map_field = N;

注意:

  • Map的字段可以是repeated。
  • 序列化后的顺序和map迭代器的顺序是不确定的,所以不要期望以固定顺序处理Map。
  • 当为.proto文件产生生成文本格式的时候,map会按照key 的顺序排序,数值化的key会按照数值排序。

map语法序列化后等同于如下内容,所以Map不经常使用。

message MapFieldEntry 
    key_type key = 1;
    value_type value = 2;

repeated MapFieldEntry map_field = N;
  • Oneof

        Oneof定义用来代表在实现的时候,该组属性中的字段有且只能有一个被定义,不能出现多个。

message SampleMessage 
  oneof test_oneof 
    string name = 4;
    SubMessage sub_message = 9;
  

上述定义中只能出现name或者sub_message的出现,不能同时出现。

注意:

  • Oneof不能出现repeated类型
  • 重复传递值到Oneof多个域仅仅最后的会生效,其它的将被忽略掉。

protobuf的编译和使用可以看:下一篇博客

        

 参考文档:

ProtoBuf 语法简介_n大橘为重n的博客-CSDN博客_protobuf语法

Protobuf简介_Shower稻草人的博客-CSDN博客_protobuf介绍

【Protocol Buffer】Protocol Buffer入门教程(三):proto3与proto2的区别_沧海一笑的技术博客_51CTO博客

Protobuf简介_Shower稻草人的博客-CSDN博客_protobuf介绍

以上是关于Protobuf语法介绍的主要内容,如果未能解决你的问题,请参考以下文章

protobuf介绍和语法

Protobuf语法介绍

Netty如何使用Protobuf 序列化

protobuf语法指南

深入protoBuf

ProtoEditor - 如何在Unity中实现一个Protobuf通信协议类编辑器