本文作者:qiaoqingyi

编程中的字段(编程中的字符)

qiaoqingyi 06-16 123

  前言

  龙湘君在去年时候写过一篇《IT人的成长 二》,介绍了Protobuf的优缺点,今天作者接着这个话题来介绍Protobuf的数据结构和使用方法。

  Protocol Buffers,简称Protobuf,是一种跨语言、多平台、具有非常良好拓展性的结构化数据协议。它由Google开发,并且经过在Google内部使用后,成为了开源项目。Profobuf和XML一样,都可以作为网络传输协议。但是熟悉XML的人都知道,XML在解析速度上慢的非常惊人。一个简单的Protobuf例子如下所示,将如下消息定义在person.proto文件中:

  message PersonInfo

  {

  required int32 id = 1;

  optional bytes address = 2;

  repeated string telephone = 3;

  }

  这就定义好了消息PersonInfo,它有三种字段,分别为id,address,telephone。每个字段后面有一个唯一的标识号,它会决定序列化时每个字段在二进制文件中的位置。

  required int32 id = 1

  id字段是int32类型,而且是必须设置值的required字段,否则无法对消息进行解析。

  optional bytes address = 2

  address也是bytes类型,因为bytes类型可以处理中文等多字节的字符。它的限定修饰符是optional,说明它是一个可选字段。对于消息发送方而言,无论设置该字段与否,消息接收方都能够解析,并且对未设置的字段自动忽略。

  repeated string telephone = 3

  telephone是string类型,而且是repeated的。repeated的字段可以包含0~N个元素,可以视作一个容器类型。

  以C++编程为例,通过在https://github.com/google/protobuf/releases上下载protoc-$VERSION-win32.zip后,解压得到Protocol Compiler编译器protoc.exe。编译的命令为

  protoc -I=$SRC_DIR --cpp_out=$DST_DIR $SRC_DIR/person.proto

  $SRC_DIR和$DST_DIR分别代表了输入文件位置和输出文件位置。如果将protoc.exe和person.proto文件放在同一目录下,则在当前路径打开cmd,输入

  Protoc –I=.\ --cpp_out=.\ person.proto

  编译成功后,可以得到person.pb.h和person.pb.cc两个文件。将.h和.cc文件包含进C++文件中,就可以创建PersonInfo的类型了。下面介绍一下具体的一些使用方法:

  消息发送方设置传输的Protobuf消息,序列化到string中

  PersonInfo person_info;

  person_info.set_id(1);

  person_info.set_address("福华一路6号");

  person_info.add_telephone("12345678");

  std::string str;

  person_info.SerializeToString(str);

  消息接收方从string中解析出Protobuf消息

  PersonInfo person_info;

  std::string str;

  person_info.ParseFromString(str);

  int id = person_info.id();

  std::string address = person_info.address();

  std::string telephone = person_info.telephone(0);

1字段赋值

  对于optional和required的字段通过set方法就可以设置字段值了,如

  void set_id(__int32 value)

  void set_address(const std::string value)

  对于repeated的字段,通过add方法来增加字段值,或者通过set方法修改字段值。

  void add_telephone(const std::string value)

  void set_telephone(int index, const std::string value)

2字段读取

  对于optional和required的字段,直接用字段名称作为函数名,就可以得到字段值了。

  __int32id();

  const std::stringaddress();

  对于repeated的字段,使用下标作为参数即可得到对应的字段值。

  const std::stringtelephone(int index);

3序列化

  Protobuf中最常见的序列化是将消息实例序列化到array或者string中,分别通过如下两个函数进行

  bool SerializeToArray(void* data, int size)

  bool SerializeToString(string* output)

  除此之外,Protobuf还提供了序列化到Ostream、CodedStream、FileDeor等对象的方法。

4反序列化

  消息接收方将Protobuf消息从对象中反序列化出来,与序列化方法相对应,如从array或者string中反序列化的具体函数为

编程中的字段(编程中的字符)

  bool ParseFromArray(void* data, int size)

  bool ParseFromString(string* output)

  当然序列化和反序列化都是有可能失败的,必要时可以通过返回值来判断。

  Protobuf文件的数据结构字段有required、optional、repeated三种,在上文中已经分别介绍过。消息发送方必须设置required字段的值,否则无法将其序列化和发送。optional字段可以不设置值,在实际应用中也是最常见的字段。对于repeated的字段,如果字段是其他message类型或者string类型,那么内部存储的是它的指针,除了add、set方法外还可以通过set_allocated的方式设置值。对于其它类型的字段,存储的是它的拷贝。

  Protobuf文件的常用数据结构可以映射为C++、Java以及Python的各类数据结构,如下表所示,更完整的信息可以在https://developers.google.com/protocol-buffers/docs/proto中找到。

  Proto

  C++

  Java

  Python

  double

  double

  double

  Float

  float

  float

  float

  Float

  int32

  int32

  int

  int

  int64

  int64

  long

  int/long

  uint32

  uint32

  int

  int/long

  uint64

  uint64

  long

  int/long

  bool

  bool

  boolean

  bool

  string

  string

  String

  str/unicode

  bytes

  string

  ByteString

  str

  ProtoBuf可以使用嵌套类,可以在一个消息中包含另一个消息。ProtoBuf也可以使用枚举变量,和C++一样使用关键词enum。另外,它也支持通过package来定义命名空间。

  使用ProtoBuf作为网络传输协议,可以解决一系列由于平台差异、语言差异等引起的问题,而且有着非常快的解析速度和较小的空间占用。目前作者仍在不断使用ProtoBuf作为服务器和客户端之间数据传输的协议,并配合使用另一个开源工具ZeroMQ作为消息通讯的工具,有兴趣的朋友们可以了解一下。

阅读
分享