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、 HOMEWORK)

    

// 枚举类型需要先定义才能进行使用

// 枚举类型 定义
 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 文件消息类型的情况,但使用都是一样,此处不作过多描述。