Protocol Buffers(protobuf)是一种轻量级的数据交换格式,可以用于结构化数据的序列化和反序列化。它使用二进制格式来编码数据,以提高传输效率和数据压缩比。 在protobuf中,我们可以使用.proto文件来定义消息类型,并使用编译器生成针对各种编程语言的序列化和反序列化代码。序列化是将结构化数据转换为一系列字节的过程,反序列化则是将字节流解析为结构化数据的过程。 序列化的过程通常涉及以下步骤: 定义消息类型:使用.proto文件定义消息类型和字段。 编写应用程序:编写应用程序,创建消息对象并填充字段。 序列化数据:使用protobuf库,将消息对象序列化为字节数组。 传输数据:将字节数组发送给接收方。 反序列化数据:接收方使用protobuf库,将字节数组反序列化为消息对象,并访问其中的字段。 在序列化过程中,protobuf使用压缩技术来减小数据的大小,从而提高传输效率。此外,protobuf支持向前和向后兼容的特性,可以使得我们在更新消息类型时,不会破坏现有的序列化数据
序列化 (Serialization)将对象的状态信息转换为可以存储或传输的形式的过程,与之相对应的过程称之为反序列化(Unserialization)。序列化和反序列化主要用于解决在跨平台和跨语言的情况下, 模块之间的交互和调用,但其本质是为了解决数据传输问题。
实现数据序列化:
序列化目的不是为了加密, 为的是数据的跨平台传输 序列化的整体过程:
发送过程中遇到的一些问题?
<?xml version="1.0" encoding="utf-8"?>
<Library>
<Type name="小说">
<Book author="J.K.ROWLING" price="12$">哈利波特1</Book>
<Book author="J.K.ROWLING" price="12$">哈利波特2</Book>
<Book author="J.K.ROWLING" price="12$">哈利波特3</Book>
<Book author="J.K.ROWLING" price="12$">哈利波特4</Book>
</Type>
<Type name="历史">
<Book author="司马迁" price="20$">史记</Book>
</Type>
</Library>
JSON起源于弱类型语言Javascript,它的产生来自于一种称之为"关联数组(Associative array)"的概念,其本质是就是采用"键值对"的方式来描述对象。
JSON格式保持了XML的人眼可读的优点,非常符合工程师对对象的理解。
相对于XML而言,序列化后的数据更加简洁(XML所产生序列化之后文件的大小接近JSON的两倍),而且其协议比较简单,解析速度比较快。
JSON格式具备Javascript的先天性支持,所以被广泛应用于Web browser的应用常景中,是Ajax的事实标准协议。
// json是一种数据格式, 不是语言, 和平台语言无关
// json数组
[整形, 浮点型, 布尔类型, 字符串, json数组, json对象]
[12, 12.44, true, "hello", [1,2,3]]
// json对象
{
"key":"value"
}
json对象中是n个键值对
key: 必须是字符串
value:
整形
浮点型
布尔
字符串
json数组
json对象
注意事项:
在一个文件中只能存储一个大的数组或者对象, 但是可以嵌套使用
原素和原始之间使用逗号间隔(一个键值对视为一个元素)
最后一个元素后边没有逗号
{
"lilii":"717",
"tom":"nihao",
"lucy":"yyyyhhhh"
}
["洁洁", "姐姐"]
{
"洁洁":{
"father":"张三",
"mother":"xxx",
"sister""xxx",
"favorite":["跳舞", "唱歌", "游泳"]
}
"姐姐":{
}
}
Protobuf是一个纯粹的展示层协议,可以和各种传输层协议一起使用,Protobuf的文档也非常完善。google 提供了多种语言的实现:java、c#、c++、go 和 python,每一种实现都包含了相应语言的编译器以及库文件。 Protobuf支持的数据类型相对较少,不支持常量类型。由于其设计的理念是纯粹的展现层协议,目前并没有一个专门支持Protobuf的RPC框架。
xxx.proto
protoc
将xxx.proto
文件生成一个c++的类 protoc xxx.proto --cpp_out=./
变量名()
set_变量名(arg)
// 要序列化的数据
struct Persion
{
int id;
string name;
string sex; // man woman
int age;
};
int id;
在.proto文件中定义消息格式
// protobuf的版本
syntax = "proto3"; // proto2
// 组织Persion结构体
// 语法格式
message 关键字(相当于被创建出的类的名字)
{
// 成员变量
数据类型 变量名 = 变量的编号; // 编号从1开始, 不能重复
}
// .proto文件 生成 c++ 类的命令
protoc proto文件名 --cpp_out=生成目录
具体转换类型规则如下所示:
.proto类型 | C++类型 | 备注 |
---|---|---|
double | double | 64位浮点数 |
float | float | 32位浮点数 |
int32 | int32 | 32位整数 |
int64 | int64 | 64位整数 |
uint32 | uint32 | 32位无符号整数 |
uint64 | uint64 | 64位无符号整数 |
sint32 | sint32 | 32位整数,处理负数效率比int32更高 |
sint64 | sint64 | 64位整数,处理负数效率比int64更高 |
fixed32 | uint32 | 总是4个字节。如果数值总是比总是比228大的话,这个类型会比uint32高效。 |
fixed64 | uint64 | 总是8个字节。如果数值总是比总是比256大的话,这个类型会比uint64高效。 |
sfixed32 | int32 | 总是4个字节 |
sfixed64 | int64 | 总是8个字节 |
bool | bool | 布尔类型 |
string | string | 一个字符串必须是UTF-8编码或者7-bit ASCII编码的文本 |
bytes | string | 处理多字节的语言字符、如中文 |
enum | enum | 枚举 |
message | object of class | 自定义的消息类型 |
用于定义一个字段可以包含多个值。它只能应用于特定的字段类型,例如int32、string等。
使用repeated修饰符可以定义一个重复字段,表示该字段可以包含一个或多个值,并以列表的形式进行存储和传输。这使得我们能够在一个字段中存储多个相关的数据,而无需定义多个独立的字段。
以下是一个示例,展示了如何使用repeated修饰符创建一个重复字段:
message MyMessage {
repeated int32 numbers = 1;
repeated string names = 2;
}
在上述示例中,numbers字段和names字段都被修饰为repeated,允许存储多个整数和字符串值。
通过使用repeated修饰符,我们可以轻松地处理包含多个值的字段,例如迭代访问、添加和删除元素等操作。
枚举类型是一种定义常量值列表的方式,用于表示一组相关的命名常量。它可以在消息类型中定义,并用于描述消息类型中的字段。
以下是一个示例,展示了如何使用protobuf中的枚举类型:
// 定义枚举
enum PhoneType {
MOBILE = 0;// protbuf中第一个枚举值必须为0
HOME = 1;
WORK = 2;
}
message Person {
string name = 1;
repeated PhoneNumber phones = 2;
}
message PhoneNumber {
string number = 1;
PhoneType type = 2; // 枚举变量
}
在上述示例中,我们定义了一个PhoneType枚举类型,其中包含三个常量值:MOBILE、HOME和WORK。这个枚举类型用于描述电话号码的类型,包括移动电话、家庭电话和工作电话。
然后,我们在PhoneNumber消息类型中使用了这个枚举类型,将电话号码与电话类型关联起来。这样,我们就可以轻松地区分不同类型的电话号码,并按照类型进行处理。
在protobuf中,枚举类型的值对应一个整数,可以使用=操作符为其指定具体的值。默认情况下,第一个枚举值的值为0,后续的枚举值依次递增。如果需要指定特定的值,可以使用类似于MY_ENUM_VALUE = 100;这样的语法进行设置。
proto文件的导入
// Persion.proto
syntax = "proto3";
// 导入另外一个proto文件
import "Info.proto";
enum Color
{
Red = 0; // protbuf中第一个枚举值必须为0
Green = 6;
Blue = 9;
}
message Persion
{
int32 id = 1; // 编号从1开始
repeated bytes name = 2;
string sex = 3;
int32 age = 4;
Color color = 5;
Info info = 6; // Info对象, 导入的proto文件中的类
}
// Info.proto
syntax = "proto3";
message Info
{
bytes address = 1; // 地址
int32 number = 2; // 门牌号
}
包 -> 命名空间
// Persion.proto
syntax = "proto3";
// 导入另外一个proto文件
import "Info.proto";
// 添加命名空间
package itcast; // Persion类属于itcast这个命名空间
enum Color
{
Red = 0; // protbuf中第一个枚举值必须为0
Green = 6;
Blue = 9;
}
message Persion
{
int32 id = 1; // 编号从1开始
repeated bytes name = 2;
string sex = 3;
int32 age = 4;
Color color = 5;
// 命名空间.类名
itheima.Info info = 6; // Info对象, 导入的proto文件中的类
}
// Info.proto
syntax = "proto3";
// Persion类属于itheima这个命名空间
package itheima;
message Persion
{
bytes address = 1; // 地址
int32 number = 2; // 门牌号
}
使用protobuf编译器生成C++类
# protobuf编译器, 编译源码得到的 protoc.exe
# 语法
# --cpp_out 生成的c++类的位置
protoc.exe xxx.proto --cpp_out=目录
使用C++ API来读写消息
变量名()
set_变量名(arg1, arg2, ...)