【protobuf】ProtoBuf——序列化概念、序列化和反序列化、为什么需要序列化和反序列化、如何实现序列化、ProtoBuf 是什么、ProtoBuf 的使用特点

ProtoBuf

在这里插入图片描述

  

1. 序列化概念

1.1 序列化和反序列化

  序列化是将数据结构或对象转换为可以存储或传输的格式(通常是字节序列)的过程。 其目的是将复杂的数据结构转化为一种便于存储、传输或在不同系统之间交换的形式。

  例如,在网络通信中,要将内存中的一个对象通过网络发送给另一台计算机,就需要先将这个对象序列化为字节流。

  
  反序列化则是序列化的逆过程,它将序列化后的数据(如字节流)重新转换回原始的数据结构或对象。

  比如说,当另一台计算机接收到序列化后的字节流数据后,需要通过反序列化将其还原为原始的对象,以便进行后续的处理和操作。

  假设我们有一个包含学生姓名、年龄和成绩的对象。序列化可能会将这个对象转换为一个特定格式的字符串,如 {“name”:“张三”,“age”:20,“score”:90} 。而反序列化就是把接收到的这样的字符串重新转换回包含姓名、年龄和成绩的学生对象。

  
  总结:

  序列化:把对象转换为字节序列的过程 称为对象的序列化。

  反序列化:把字节序列恢复为对象的过程 称为对象的反序列化。

  

1.2 为什么需要序列化和反序列化

  数据存储:

  当需要将数据持久化保存到文件或数据库中时,内存中的对象结构不能直接进行存储。通过序列化将对象转换为一种适合存储的格式,如二进制、JSON 或 XML 等,以便后续能够从存储介质中读取并反序列化还原为对象进行使用。

  例如,一个电商网站需要定期将用户的购物车信息保存到数据库中,就需要先对购物车对象进行序列化。

  
  网络通信:

  网络只能传输字节流,无法直接传输对象。序列化将对象转换为字节流在网络中传输,接收方再进行反序列化得到原始对象,实现不同节点之间的数据交换。

  比如,在分布式系统中,一个客户端发送请求给服务器,请求中的对象需要序列化后才能通过网络发送给服务器,服务器接收后反序列化进行处理。

  
  跨语言和平台:

  不同的编程语言和平台对数据的表示和处理方式可能不同。序列化可以使用一种通用的格式(如 Protocol Buffers、JSON 等),使得数据能够在不同语言和平台之间进行传输和转换。

  例如,一个用 Java 编写的服务需要与用 Python 编写的服务进行通信,通过序列化和反序列化可以实现数据的共享和交互。

  
  提高性能:

  某些序列化格式经过优化,能够更高效地表示数据,减少存储空间和传输带宽的消耗。

  比如,在高并发的网络环境中,使用高效的序列化方式可以显著提高系统的性能和响应速度。

  
  版本控制和兼容性:

  当对象的结构发生变化时,通过合理设计的序列化和反序列化机制,可以处理不同版本的数据,保证一定的兼容性。

  假设一个系统升级,对象新增了一些字段,旧版本序列化的数据在新版本中进行反序列化时,可以根据特定规则进行处理,避免数据丢失或错误。

  

  一般来说这两个地方需要序列化较多:

  存储数据:当您想要将内存中的对象状态保存至一个文件中或者存到数据库中时。

  网络传输:网络直接传输数据,但无法直接传输对象,所以需要在传输前序列化,传输完成后反序列化成对象。例如我们之前学习过的 socket 编程中发送与接收数据。

  

1.3 如何实现序列化

  (1)使用编程语言提供的内置序列化机制:

  许多编程语言都内置了序列化的功能。 例如,Java 中的 java.io.Serializable 接口,通过实现这个接口,对象可以被 Java 的序列化机制处理。

import java.io.Serializable;

public class Student implements Serializable {
    private String name;
    private int age;

    // 构造函数、getter 和 setter 方法
}

  

  (2)使用特定的序列化库或框架:

  如 Protocol Buffers、JSON 库(如 Jackson、Gson 等)、XML 序列化库(如 JAXB)。

  使用 Protocol Buffers 时,需要定义 .proto 文件来描述数据结构,然后使用相关工具生成代码进行序列化和反序列化操作。

  
Protocol Buffers(Protobuf)为例:

// --- protobuf
syntax = "proto3";

message Student {
    string name = 1;
    int32 age = 2;
    float score = 3;
}

  

  然后,使用 protoc 编译器编译这个 .proto 文件,生成相应语言的代码。

假设使用的是 C++ 语言,在命令行中执行以下命令:

protoc --cpp_out=your_output_path student.proto

  
使用生成的代码进行序列化和反序列化的 C++ 示例:

#include <iostream>
#include <fstream>
#include "student.pb.h"  // 包含生成的头文件

int main() {
    // 创建并设置 Student 对象
    your_namespace::Student student;
    student.set_name("Alice");
    student.set_age(20);
    student.set_score(85.5);

    // 序列化
    std::string serializedData;
    student.SerializeToString(&serializedData);

    // 写入文件
    std::ofstream outputFile("student_data.bin", std::ios::binary);
    outputFile.write(serializedData.c_str(), serializedData.size());
    outputFile.close();

    // 反序列化
    your_namespace::Student deserializedStudent;
    std::ifstream inputFile("student_data.bin", std::ios::binary);
    std::string content((std::istreambuf_iterator<char>(inputFile)), std::istreambuf_iterator<char>());
    deserializedStudent.ParseFromString(content);

    // 输出反序列化后的结果
    std::cout << "Deserialized Name: " << deserializedStudent.name() << std::endl;
    std::cout << "Deserialized Age: " << deserializedStudent.age() << std::endl;
    std::cout << "Deserialized Score: " << deserializedStudent.score() << std::endl;

    return 0;
}

  
  假设您设置的学生信息为:student.set_name(“Alice”); student.set_age(20); student.set_score(85.5),那么运行结果是以下内容:

Deserialized Name: Alice
Deserialized Age: 20
Deserialized Score: 85.5

  

  (3)自定义序列化方法:

  对于一些特殊的需求,可以自己编写序列化和反序列化的逻辑。

  
将一个结构体的数据序列化为字符串,并进行反序列化:

#include <iostream>
#include <string>

// 定义一个结构体
struct Student {
    std::string name;
    int age;
};

// 序列化函数
std::string serializeStudent(const Student& student) {
    std::string data = student.name + " " + std::to_string(student.age);
    return data;
}

// 反序列化函数
Student deserializeStudent(const std::string& data) {
    Student student;
    size_t spacePos = data.find(' ');
    student.name = data.substr(0, spacePos);
    student.age = std::stoi(data.substr(spacePos + 1));
    return student;
}

int main() {
    Student student1{"Alice", 20};

    // 序列化
    std::string serializedData = serializeStudent(student1);
    std::cout << "Serialized Data: " << serializedData << std::endl;

    // 反序列化
    Student student2 = deserializeStudent(serializedData);
    std::cout << "Deserialized Name: " << student2.name << ", Age: " << student2.age << std::endl;

    return 0;
}

  

2. ProtoBuf 是什么

Protocol buffers are Google’s language-neutral, platform-neutral,
extensible mechanism for serializing structured data ‒ think XML, but
smaller, faster, and simpler. You define how you want your data to be
structured once, then you can use special generated source code to
easily write and read your structured data to and from a variety of
data streams and using a variety of languages. - - 来自官方

  

  Protocol Buffers 是 Google 的一种语言无关、平台无关、可扩展的序列化结构数据的方法,它可用于(数据)通信协议、数据存储等。

  Protocol Buffers 类比于 XML,是一种灵活、高效、自动化机制的结构数据序列化方法,但是比 XML 更小、更快、更为简单。

  您可以定义数据的结构,然后使用特殊生成的源代码轻松地在各种数据流中使用各种语言进行编写和读取结构数据。您甚至可以更新数据结构,而不破坏由旧数据结构编译的已部署程序。

  
  简单来讲,ProtoBuf(全称为 Protocol Buffer)是让结构数据序列化的方法,其具有以下特点:

  语言无关、平台无关:即 ProtoBuf 支持 Java、C++、Python 等多种语言,支持多个平台。

  高效:即比 XML 更小、更快、更为简单。

  扩展性、兼容性好:您可以更新数据结构,而不影响和破坏原有的旧程序。

  

3. ProtoBuf 的使用特点

  编写.proto 文件,其目的在于定义结构对象(message)及属性内容。

  运用 protoc 编译器对.proto 文件进行编译,生成一系列接口代码,并存储在新生成的头文件和源文件中。

  依靠生成的接口,将编译生成的头文件纳入我们的代码里,达成对.proto 文件中定义的字段进行设置与获取,以及对 message 对象进行序列化和反序列化。

  总的来说:ProtoBuf 是需要依赖通过编译生成的头文件和源文件来加以使用的。有了这种代码生成机制,开发人员再也不必辛辛苦苦地编写那些协议解析的代码了。

  
在这里插入图片描述

            

  • 18
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鳄鱼麻薯球

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值