protocol精讲(二)

1、Protocol格式的定义

        .proto 文件中的定义很简单:为要序列化的每个数据结构添加一条消息,然后为消息中的每个字段指定名称和类型。

        addressbook.proto,一个定义消息的 .proto 文件如下所示:

// 文件开头定义一个语法版本 第二版本的proto语法
syntax = "proto2";

// package类似C++的命名空间
package tutorial;

// message定义一条消息,类似C语言的struct关键字
message Person{
	// required,必要的,代表这个字段(name)一定会出现
    required string name = 1;	// 1,说明这个变量在内存里面的排布,它是排在第一个
    // int32就是C语言的int,即32位的整数,也就是四个字节
	required int32 id = 2;
	// optional,可选的
	optional string email = 3;

	// message内可嵌套声明
	// 这里声明了一个枚举
	enum PhoneType{
		MOBILE = 0;
		HOME = 1;
		WORK = 2;
	}
	
	message PhoneNumber{
		required string number = 1;
		optional PhoneType type = 2 [default = HOME];
	}
	
	// repeated,重复的,类似C语言中的数组
	repeated PhoneNumber phones = 4;
}

message AddressBook{
	repeated Person people = 1;
}

        以上所见,语法类似于 C++ 或 Java。 浏览一下文件的每个部分,看看它的作用。

        .proto 文件以包声明开头,这有助于防止不同项目之间的命名冲突。 在 C++ 中,生成的类将放置在与包名称匹配的命名空间中。

        接下来是消息定义。 消息只是包含一组类型字段的聚合。 许多标准简单数据类型均可用作字段类型,包括 bool、int32、float、double 和 string。 还可以通过使用其他消息类型作为字段类型来向消息添加更多的数据结构——在上面的示例中,Person 消息包含 PhoneNumber 消息,而 AddressBook 消息包含 Person 消息。 甚至可以定义嵌套在其他消息中的消息类型——正如所看到的,PhoneNumber 类型是在 Person 内部定义的。 如果希望某个字段具有预定义值列表之一,还可以定义枚举类型 ——此处想要指定PhoneNumber可以是以下电话类型之一:PHONE_TYPE_MOBILE、PHONE_TYPE_HOME 或 PHONE_TYPE_WORK。

        每个元素上的“= 1”、“= 2”标记是标识该字段在二进制编码中使用的唯一字段号。 字段号 1-15 所需的编码比更高的字段号少一个字节,因此作为一种优化,可以将这些字段号用于常用或为repeated的元素,而将字段号 16 和更高的字段号留给不太常用的optional元素。 repeated字段中的每个元素都需要重新编码字段号,因此repeated的字段特别适合此优化。

        每个字段必须使用以下修饰符之一进行注释:

        optional:该字段可以设置也可以不设置。 如果未设置字段值,则使用默认值。 对于简单类型,可以指定自己的默认值,就像在上面示例中为PhoneNumber类型所做的那样。 否则,使用系统默认值:数字类型为零,字符串为空字符串,布尔值为 false。 对于嵌入消息,默认值始终是消息的“默认实例”或“原型”,其未设置任何字段。 调用访问器来获取尚未显式设置的optional(或required)字段的值将始终返回该字段的默认值。

        repeated:该字段可以重复任意次数(包括零次)。 重复值的顺序将保存在Protocol Buffer(协议缓冲区)中。 将repeated字段视为大小动态的数组。

        repeated:必须提供该字段的值,否则消息将被视为“未初始化”。 如果 libprotobuf 在debug模式下编译,序列化未初始化的消息将导致断言失败。 在release版本中,会跳过检查,并且无论如何都会写入消息。 但是,解析未初始化的消息总是会失败(通过从parse方法返回 false)。 除此之外,required字段的表现与optional字段完全相同。

2、Protocol Buffer API

        看一下生成的一些代码,看看编译器创建了哪些类和函数。 如果查看addressbook.pb.h,可以看到在addressbook.proto 中指定的每条消息都有一个类。 仔细观察 Person 类,可以看到编译器已为每个字段生成了访问器。 例如,对于name、id、email和phones字段,可以使用以下方法:

  // name
  inline bool has_name() const;
  inline void clear_name();
  inline const ::std::string& name() const;
  inline void set_name(const ::std::string& value);
  inline void set_name(const char* value);
  inline ::std::string* mutable_name();

  // id
  inline bool has_id() const;
  inline void clear_id();
  inline int32_t id() const;
  inline void set_id(int32_t value);

  // email
  inline bool has_email() const;
  inline void clear_email();
  inline const ::std::string& email() const;
  inline void set_email(const ::std::string& value);
  inline void set_email(const char* value);
  inline ::std::string* mutable_email();

  // phones
  inline int phones_size() const;
  inline void clear_phones();
  inline const ::google::protobuf::
        RepeatedPtrField< ::tutorial::Person_PhoneNumber >& phones() const;
  inline ::google::protobuf::
        RepeatedPtrField< ::tutorial::Person_PhoneNumber >* mutable_phones();
  inline const ::tutorial::Person_PhoneNumber& phones(int index) const;
  inline ::tutorial::Person_PhoneNumber* mutable_phones(int index);
  inline ::tutorial::Person_PhoneNumber* add_phones();

          正如所看到的,getter方法的名称与小写字段的名称完全相同,setter 方法以 set_ 开头。 每个(required或optional)字段有有 has_ 方法,如果该字段已设置,则返回 true。 最后,每个字段都有一个clear_方法,可以将字段恢复为空状态。

        虽然id(数字)字段仅具有上述基本访问器集,但name和email字段有一些额外的方法,因为它们是字符串——一个 mutable_ getter,可获取指向字符串的直接指针,以及一个额外的 setter。 请注意,即使email尚未设置,也可以调用 mutable_email() ; 它将自动初始化为空字符串。 如果本例中有一个重复的消息字段,它也会有一个 mutable_ 方法,但没有 set_ 方法。

3、标准消息方法

        每个消息类还包含许多其他方法,可让应用人员检查或操作整个消息,包括:

bool IsInitialized() const;:检查是否已设置所有必填字段
string DebugString() const;:返回消息的可读表示形式,对于调试特别有用
void CopyFrom(const Person& from);:用给定消息的值覆盖消息,相对于对象的深拷贝
void Clear();:清除所有元素回到空状态

4、解析和序列化

        最后,每个protocol buffer(协议缓冲区)类都具有使用protocol buffer(协议缓冲区)二进制格式写入和读取所选类型的消息的方法。 这些包括:

bool SerializeToString(string* output) const;:序列化消息并将字节存储在给定字符串中。
请注意,字节是二进制的,而不是文本;只使用 string 类作为一个方便的容器
bool ParseFromString(const string& data);:从给定的字符串中解析消息
bool SerializeToOstream(ostream* output) const;: 将消息写入给定的 C++ ostream
bool ParseFromIstream(istream* input);:解析来自给定 C++ istream 的消息

  • 23
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值