Protobuf-net
一、什么是Protobuf
Protobuf 是 Google 公司提供的一款简洁高效且开源的二进制序列化数据存储方案。只要遵循其PB语法定义的消息格式,然后通过批处理,就可以生成目标代码。
二、为什么要使用Protobuf

三、Protobuf的序列化和反序列化
序列化 : 将 数据结构或对象 转换成 二进制的过程, 就是序列化。
反序列化 : 将 二进制 转换成 数据结构或对象的过程, 就是反序列化。
三、Protobuf语法

下面详细介绍.proto的消息对象&字段:
1、消息对象
该对象,可以理解为class/struct结构。

在一个.proto文件中可以嵌套多个消息对象。
message Person {
required string name = 1;
required int32 id = 2;
optional string email = 3;
// 该消息类型 定义在 Person消息类型的内部
// 即Person消息类型 是 PhoneNumber消息类型的父消息类型
message PhoneNumber {
required string number = 1;
}
}
<-- 多重嵌套 -->
message Outer { // Level 0
message MiddleAA { // Level 1
message Inner { // Level 2
required int64 ival = 1;
optional bool booly = 2;
}
}
}
注意:尽可能的将某一消息类型对应的响应消息格式都定义在一个proto文件中。如:
message SearchRequest {
required string query = 1;
optional int32 page_number = 2;
optional int32 result_per_page = 3;
}
// 与SearchRequest消息类型 对应的 响应消息类型SearchResponse
message SearchResponse {
…
}
2、字段
- 消息对象的字段 组成主要是:字段 = 字段修饰符 + 字段类型 +字段名 +标识号
![]()
下面对字段修饰符,进行详细描述。字段修饰符是字段解析时的规则:

字段类型,主要有三个:
- 基本数据 类型
- 枚举 类型
- 消息对象 类型
1 message Person { 2 3 // 基本数据类型 字段 4 required string name = 1; 5 required int32 id = 2; 6 optional string email = 3; 7 8 enum PhoneType { 9 MOBILE = 0; 10 HOME = 1; 11 WORK = 2; 12 } 13 14 message PhoneNumber { 15 optional PhoneType type = 2 [default = HOME]; 16 // 枚举类型 字段 17 } 18 19 repeated PhoneNumber phone = 4; 20 // 消息类型 字段 21 }
(1)基本的数据类型,如下:

(2)枚举类型
作用:为字段指定一个 可能取值的字段集合 (该字段只能从 该指定的字段集合里 取值)
说明:如下面例子,电话号码 可能是手机号、家庭电话号或工作电话号的其中一个,那么就将PhoneType定义为枚举类型,并将加入电话的集合( MOBILE、 HOME、WORK)
// 枚举类型需要先定义才能进行使用
// 枚举类型 定义
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
// 电话类型字段 只能从 这个集合里 取值
}
// 特别注意:
// 1. 枚举类型的定义可在一个消息对象的内部或外部
// 2. 都可以在 同一.proto文件 中的任何消息对象里使用
// 3. 当枚举类型是在一消息内部定义,希望在 另一个消息中 使用时,需要采用MessageType.EnumType的语法格式
message PhoneNumber {
required string number = 1;
optional PhoneType type = 2 [default = HOME];
// 使用枚举类型的字段(设置了默认值)
}
// 特别注意:
// 1. 枚举常量必须在32位整型值的范围内
// 2. 不推荐在enum中使用负数:因为enum值是使用可变编码方式的,对负数不够高
(3)消息对象 类型
一个消息对象 可以将 其他消息对象类型 用作字段类型,情况如下:

3.1使用同一个 .proto 文件里的消息类型
a. 使用 内部消息类型
目的:先在 消息类型 中定义 其他消息类型 ,然后再使用
即嵌套,需要 用作字段类型的 消息类型 定义在 该消息类型里
实例:
1 message Person { 2 required string name = 1; 3 required int32 id = 2; 4 optional string email = 3; 5 6 // 该消息类型 定义在 Person消息类型的内部 7 // 即Person消息类型 是 PhoneNumber消息类型的父消息类型 8 message PhoneNumber { 9 required string number = 1; 10 } 11 12 repeated PhoneNumber phone = 4; 13 // 直接使用内部消息类型 14 }
b、使用 外部消息类型
即外部重用,需要 用作字段类型的消息类型 定义在 该消息类型外部
1 message Person { 2 required string name = 1; 3 required int32 id = 2; 4 optional string email = 3; 5 } 6 7 message AddressBook { 8 repeated Person person = 1; 9 // 直接使用了 Person消息类型作为消息字段 10 }
c. 使用 外部消息的内部消息类型
1 message Person { 2 required string name = 1; 3 required int32 id = 2; 4 optional string email = 3; 5 6 // PhoneNumber消息类型 是 Person消息类型的内部消息类型 7 message PhoneNumber { 8 required string number = 1; 9 optional PhoneType type = 2 [default = HOME]; 10 } 11 } 12 13 // 若父消息类型外部的消息类型需要重用该内部消息类型 14 // 需要以 Parent.Type 的形式去使用 15 // Parent = 需要使用消息类型的父消息类型,Type = 需要使用的消息类型 16 17 // PhoneNumber父消息类型Person 的外部 OtherMessage消息类型 需要使用 PhoneNumber消息类型 18 message OtherMessage { 19 optional Person.PhoneNumber phonenumber = 1; 20 // 以 Parent.Type = Person.PhoneNumber 的形式去使用 21 22 }
3.2 使用不同 .proto 文件里的消息类型
- 目的:需要在
A.proto文件 使用B.proto文件里的消息类型 - 解决方案:在
A.proto文件 通过导入(import)B.proto文件中来使用B.proto文件 里的消息类型
1 import "myproject/other_protos.proto" 2 // 在A.proto 文件中添加 B.proto文件路径的导入声明 3 // ProtocolBuffer编译器 会在 该目录中 查找需要被导入的 .proto文件 4 // 如果不提供参数,编译器就在 其调用的目录下 查找
当然,在使用 不同 .proto 文件里的消息类型 时 也会存在想 使用同一个 .proto 文件消息类型的情况,但使用都是一样,此处不作过多描述。