前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >protobuf 序列化和反序列化

protobuf 序列化和反序列化

作者头像
洁洁
发布2024-03-15 08:45:29
1610
发布2024-03-15 08:45:29
举报
文章被收录于专栏:小洁叫你mysql小洁叫你mysql

前言

Protocol Buffers(protobuf)是一种轻量级的数据交换格式,可以用于结构化数据的序列化和反序列化。它使用二进制格式来编码数据,以提高传输效率和数据压缩比。 在protobuf中,我们可以使用.proto文件来定义消息类型,并使用编译器生成针对各种编程语言的序列化和反序列化代码。序列化是将结构化数据转换为一系列字节的过程,反序列化则是将字节流解析为结构化数据的过程。 序列化的过程通常涉及以下步骤: 定义消息类型:使用.proto文件定义消息类型和字段。 编写应用程序:编写应用程序,创建消息对象并填充字段。 序列化数据:使用protobuf库,将消息对象序列化为字节数组。 传输数据:将字节数组发送给接收方。 反序列化数据:接收方使用protobuf库,将字节数组反序列化为消息对象,并访问其中的字段。 在序列化过程中,protobuf使用压缩技术来减小数据的大小,从而提高传输效率。此外,protobuf支持向前和向后兼容的特性,可以使得我们在更新消息类型时,不会破坏现有的序列化数据

1. 序列化

序列化 (Serialization)将对象的状态信息转换为可以存储或传输的形式的过程,与之相对应的过程称之为反序列化(Unserialization)。序列化和反序列化主要用于解决在跨平台和跨语言的情况下, 模块之间的交互和调用,但其本质是为了解决数据传输问题。

实现数据序列化:

  • 要有原始数据
    • 复合类型 -> 最常见的情况
    • 基础数据类型
  • 通过某些方式 -> 另外一种形式的数据
  • 得到的数据干啥? -> 目的: 进行分发, 分发到不同的终端/平台, 保证不同的平台能正确解析
    • 网络传输
    • 磁盘拷贝

序列化目的不是为了加密, 为的是数据的跨平台传输 序列化的整体过程:

  • 发送端
    • 原始数据 -> 序列化 (编码) -> 特殊格式的字符串
    • 发送这个字符串
  • 接收端:
    • 接收数据
    • 特殊格式的字符串 -> 反序列化 (解码) -> 原始数据
    • 对原始数据进行处理

1.1 网络通信中的问题分析

发送过程中遇到的一些问题?

  • 平台不同
  • 如果不是字符串, 需要进行字节序转换
  • 语言不同
  • 字节对齐问题

1.2 常用的序列化方式

  1. XML( Extensible Markup Language )类似于html XML是一种常用的序列化和反序列化协议,具有跨机器,跨语言等优点。XML历史悠久,其1.0版本早在1998年就形成标准,并被广泛使用至今。 XML的最初产生目标是对互联网文档进行标记,所以它的设计理念中就包含了对于人和机器都具备可读性。 但是,当这种标记文档的设计被用来序列化对象的时候,就显得冗长而复杂。 XML基本格式:
代码语言:javascript
复制
   <?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>
  1. Json( JavaScript Object Notation )

JSON起源于弱类型语言Javascript,它的产生来自于一种称之为"关联数组(Associative array)"的概念,其本质是就是采用"键值对"的方式来描述对象。

JSON格式保持了XML的人眼可读的优点,非常符合工程师对对象的理解。

相对于XML而言,序列化后的数据更加简洁(XML所产生序列化之后文件的大小接近JSON的两倍),而且其协议比较简单,解析速度比较快。

JSON格式具备Javascript的先天性支持,所以被广泛应用于Web browser的应用常景中,是Ajax的事实标准协议。

代码语言:javascript
复制
// 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":["跳舞", "唱歌", "游泳"]
    }
	"姐姐":{
    }
}
  1. Protocol Buffer
  2. ASN.1 抽象语法标记(Abstract Syntax Notation One)
  3. boost 序列化的类

2. protobuf

Protobuf是一个纯粹的展示层协议,可以和各种传输层协议一起使用,Protobuf的文档也非常完善。google 提供了多种语言的实现:java、c#、c++、go 和 python,每一种实现都包含了相应语言的编译器以及库文件。 Protobuf支持的数据类型相对较少,不支持常量类型。由于其设计的理念是纯粹的展现层协议,目前并没有一个专门支持Protobuf的RPC框架。

2.1 操作流程

  • 准备数据
    • 复合类型: 结构体/ 类
  • 基础类型
  • 创建一个新文件 xxx.proto
  • 将我们要序列化的数据 -> 写入到proto文件
    • 注意写入有语法格式
  • 通过命令 protocxxx.proto文件生成一个c++的类
    • 会生成一个头文件/ 源文件
    • 操作命令-> 在window终端中: protoc xxx.proto --cpp_out=./
  • 使用这两个文件
    • 文件里有对数据操作的api
      • 读数据 API
        • 方法名字 变量名()
      • 写数据 API
      • 方法名字: set_变量名(arg)
      • 等等API
代码语言:javascript
复制
// 要序列化的数据
struct Persion
{
    int id;
    string name;
    string sex;	// man woman
    int age;
};

int id;

在.proto文件中定义消息格式

代码语言:javascript
复制
// 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

自定义的消息类型

  • repeated限定修饰符

用于定义一个字段可以包含多个值。它只能应用于特定的字段类型,例如int32、string等。

使用repeated修饰符可以定义一个重复字段,表示该字段可以包含一个或多个值,并以列表的形式进行存储和传输。这使得我们能够在一个字段中存储多个相关的数据,而无需定义多个独立的字段。

以下是一个示例,展示了如何使用repeated修饰符创建一个重复字段:

代码语言:javascript
复制
message MyMessage {
  repeated int32 numbers = 1;
  repeated string names = 2;
}

在上述示例中,numbers字段和names字段都被修饰为repeated,允许存储多个整数和字符串值。

通过使用repeated修饰符,我们可以轻松地处理包含多个值的字段,例如迭代访问、添加和删除元素等操作。

  • 枚举

枚举类型是一种定义常量值列表的方式,用于表示一组相关的命名常量。它可以在消息类型中定义,并用于描述消息类型中的字段。

以下是一个示例,展示了如何使用protobuf中的枚举类型:

代码语言:javascript
复制
  // 定义枚举
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文件的导入

代码语言:javascript
复制
// 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文件中的类
}
代码语言:javascript
复制
// Info.proto
syntax = "proto3";

message Info
{
    bytes address = 1;  // 地址
    int32 number = 2;   // 门牌号
}

包 -> 命名空间

代码语言:javascript
复制
// 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文件中的类
}
代码语言:javascript
复制
// Info.proto
syntax = "proto3";
// Persion类属于itheima这个命名空间
package itheima;

message Persion
{
    bytes address = 1;  // 地址
    int32 number = 2;   // 门牌号
}

使用protobuf编译器生成C++类

代码语言:javascript
复制
# protobuf编译器, 编译源码得到的 protoc.exe
# 语法
# --cpp_out 生成的c++类的位置
protoc.exe xxx.proto --cpp_out=目录

使用C++ API来读写消息

  • 读: 变量名()
  • 写: set_变量名(arg1, arg2, ...)

总结

  • 效率和紧凑性:protobuf使用二进制编码,提供了高效的数据传输和紧凑的数据存储,相对于文本格式,可以节省带宽和存储空间。
  • 跨平台和可扩展性:protobuf支持多种编程语言,包括C++、Java、Python等,可以在不同平台和语言之间进行数据交换。它还支持向前和向后兼容,可以在消息类型更新时保持数据的兼容性。
  • 结构化数据定义:protobuf使用.proto文件来定义消息类型和字段,提供了结构化的数据模型,使得数据的组织和访问更加清晰和灵活。
  • 强类型和类型安全:protobuf使用强类型系统,可以在编译时检查类型错误,避免在运行时出现错误。这提供了更好的类型安全性和代码可靠性。
  • 支持多种数据类型:protobuf支持各种基本数据类型(如整数、字符串、布尔值等),以及复杂的数据类型(如嵌套消息、枚举等),可以灵活地描述和处理各种数据结构。
  • 序列化和反序列化:protobuf提供了自动生成的序列化和反序列化代码,使得数据的序列化和反序列化过程简单而高效。它还支持压缩技术,可以减小数据大小,提高传输效率。
  • 可读性和可维护性:由于protobuf使用结构化的数据模型和明确的消息类型定义,使得代码更具可读性和可维护性。同时,它也提供了版本控制机制,方便进行更新和演进。
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2024-03-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 1. 序列化
    • 1.1 网络通信中的问题分析
      • 1.2 常用的序列化方式
      • 2. protobuf
        • 2.1 操作流程
        • 总结
        相关产品与服务
        数据保险箱
        数据保险箱(Cloud Data Coffer Service,CDCS)为您提供更高安全系数的企业核心数据存储服务。您可以通过自定义过期天数的方法删除数据,避免误删带来的损害,还可以将数据跨地域存储,防止一些不可抗因素导致的数据丢失。数据保险箱支持通过控制台、API 等多样化方式快速简单接入,实现海量数据的存储管理。您可以使用数据保险箱对文件数据进行上传、下载,最终实现数据的安全存储和提取。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档