Protobuf 编码规则及c++使用详解

Protobuf 编码规则及c++使用详解

Protobuf 介绍

Protocol Buffers (a.k.a., protobuf) are Google’s language-neutral, platform-neutral, extensible mechanism for serializing structured data
Protocol Buffers(简称为protobuf)是谷歌的语言无关、平台无关、可扩展的机制,用于序列化结构化数据。

protobuf的主要目的是在不同应用程序之间高效地传输和存储数据。与XML和JSON等文本格式相比,protobuf的编码格式更紧凑,解析速度更快,占用的存储空间更小。此外,protobuf还支持版本兼容性,使得在数据结构发生变化时能够向后兼容。

在音视频领域,信令和打点数据量非常大,采用JSON等文本方式,虽然可读性较大,但数据提交太大,不利于传输和节省带宽,因此二进制编码方式(protobuf)非常适合这个领域。

c++ 使用详解

1、定义.proto文件 (message.proto)

syntax = "proto3";package tutorial;message Person {string name = 1;int32 id = 2;string email = 3;enum PhoneType {PHONE_TYPE_UNSPECIFIED = 0;PHONE_TYPE_MOBILE = 1;PHONE_TYPE_HOME = 2;PHONE_TYPE_WORK = 3;}message PhoneNumber {string number = 1;PhoneType type = 2;}repeated PhoneNumber phones = 4;
}

包含了常用的整型,字符串,枚举,结构体,repeated(数组)类型

2、使用protoc编译器生成c++源码文件(message.pb.h, message.pb.cc)

protoc --cpp_out=. message.proto
libprotobuf和protoc编译器如何安装?
在ubuntu系统下:

# sudo apt update
# sudo apt install libprotobuf-dev protobuf-compiler

3、编写测试文件(main.cpp)

// g++ -std=c++11 main.cpp message.pb.cc -lprotobuf -o main#include "message.pb.h"
#include <google/protobuf/util/json_util.h>
#include <iostream>
#include <fstream>
#include <initializer_list>struct CppPerson {std::string name = "John";int32_t id = 1;std::string email = "john@163.com";enum CppPhoneType {UNSPECIFIED = 0,MOBILE = 1,HOME = 2,WORK = 3,};struct CppPhoneNumber {std::string number;CppPhoneType type;};std::vector<CppPhoneNumber> phones;
};int main() {// Create a CppPerson objectCppPerson cpp_person; cpp_person.phones.push_back({"123456789", CppPerson::HOME});cpp_person.phones.push_back({"987654321", CppPerson::WORK});// 创建一个 Person 对象tutorial::Person person;person.set_name(cpp_person.name);person.set_id(cpp_person.id);person.set_email(cpp_person.email);for (auto &s : cpp_person.phones) {auto phone = person.add_phones();phone->set_number(s.number);phone->set_type(static_cast<tutorial::Person::PhoneType>(s.type));}// 将 Person 对象序列化为字节流std::string serialized;person.SerializeToString(&serialized);std::cout << "serialized size: " << serialized.size() << std::endl;// message <--> jsonstd::string json;google::protobuf::util::JsonOptions json_options;//  json_options.add_whitespace = true;google::protobuf::util::MessageToJsonString(person, &json, json_options);std::cout << "JSON: " << json << std::endl;std::cout << "JSON size: " << json.size() << std::endl;tutorial::Person person_from_json;google::protobuf::util::JsonStringToMessage(json, &person_from_json);std::string person_from_json_serialized;person_from_json.SerializeToString(&person_from_json_serialized);std::cout << "Person from JSON serialized size: " << person_from_json_serialized.size() << std::endl;// 将字节流写入文件std::fstream output("person.bin", std::ios::out | std::ios::binary);output.write(serialized.c_str(), serialized.size());output.close();// 从文件中读取字节流std::fstream input("person.bin", std::ios::in | std::ios::binary);std::string serialized_input((std::istreambuf_iterator<char>(input)), std::istreambuf_iterator<char>());// 将字节流反序列化为 Person 对象tutorial::Person person2;person2.ParseFromString(serialized_input);// 输出 Person 对象的信息std::cout << "Person:" << std::endl;std::cout << "  name: " << person2.name() << std::endl;std::cout << "  id: " << person2.id() << std::endl;std::cout << "  email: " << person2.email() << std::endl;for (int i = 0; i < person2.phones_size(); i++) {auto &phone_number = person2.phones(i);std::cout << "  phone number: " << phone_number.number() << std::endl;std::cout << "  phone type: " << phone_number.type() << std::endl;}return 0;
}

4、编译运行

# g++ -std=c++11 main.cpp message.pb.cc -lprotobuf -o main# ./mainserialized size: 52
JSON: {"name":"John","id":1,"email":"john@163.com","phones":[{"number":"123456789","type":"PHONE_TYPE_HOME"},{"number":"987654321","type":"PHONE_TYPE_WORK"}]}
JSON size: 152
Person from JSON serialized size: 52
Person:name: Johnid: 1email: john@163.comphone number: 123456789phone type: 2phone number: 987654321phone type: 3

查看编码好的二进制(person.bin)
在这里插入图片描述

5、编码后二进制流分析

在这里插入图片描述

编码规则

第一个字节包含了字段编号(Filed Number)和wired type, 组合起来代表message里一个字段的Key信息
bit 7

0-代表该字节自解释完整
1-代表后续还有字节,需要整合后续字节提取信息 (当字段数超过15个是采用这种方式,即由多个字节编码key)

<这个bit就是varint的关键功能位,variant中每个字节的最高位bit称之为most significant bit(MSB),如果该bit为0意味着这个字节为表示当前整数的最后一个字节,如果为1则表示后面还有至少1个字节,可见,varint的终止位置其实是自解释的。>

bit 6-bit 3

存储字段编号,field number (fn)

bit 2-bit 0

存储wired type (wt)

常用的类型主要有VARINT和LEN

  • wire type=0、1、5,编码为key+数据,只有一个数据,可能占数个字节,数据在编码时自带终止标记;
  • wire type=2,编码为key+length+数据,length指示了数据长度,可能有多个数据,顺序排序

在这里插入图片描述
在这里插入图片描述

总结:二进制流存储方式为key data key data key data…
比如上述例子:
在这里插入图片描述

6、番外:protobuf和JSON互转

如上述示例,转换为的json为:

JSON: {"name":"John","id":1,"email":"john@163.com","phones":[{"number":"123456789","type":"PHONE_TYPE_HOME"},{"number":"987654321","type":"PHONE_TYPE_WORK"}]}
JSON size: 152

可见JSON紧压缩方式大小为152Byte,而PB压缩大小为52Byte,非常省空间。

Protobuf,它只需要简单地将一个二进制序列,按照指定的格式读取到C++对应的结构类型中就可以了。消息的decoding过程也可以通过几个位移操作组成的表达式计算即可完成。而对于字符串、自定义对象类型的数据,Protobuf在存储的时候,也存储了该数据的字节长度,读取起来也非常快。

参考

https://protobuf.dev/overview/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/292759.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一键批量管理HTML文档,轻松删除无用内容,提升网站性能!

随着互联网的快速发展&#xff0c;网站性能优化已经成为每个站长和开发者必须面对的问题。为了提升网站性能&#xff0c;我们需要对HTML文档进行高效的管理和优化。但是&#xff0c;手动管理大量HTML文件不仅效率低下&#xff0c;还容易出错。那么&#xff0c;有没有一种简单、…

Leetcode 452. 用最少数量的箭引爆气球

题意理解&#xff1a; 气球用一个闭区间表示&#xff0c;击中区间内任意一个位置&#xff0c;气球会被引爆。 区间指示了气球的位置和气球的可射击范围。 若两个气球位置有重叠&#xff0c;则一只箭能解决引爆两个气球。 目标&#xff1a;尽可能射击气球重叠最多的位置&#xf…

【数据结构和算法】最大连续1的个数 III

其他系列文章导航 Java基础合集数据结构与算法合集 设计模式合集 多线程合集 分布式合集 ES合集 文章目录 其他系列文章导航 文章目录 前言 一、题目描述 二、题解 2.1 方法一&#xff1a;滑动窗口 2.2 滑动窗口解题模板 三、代码 3.1 方法一&#xff1a;滑动窗口 四、…

Ubuntu 常用命令之 top 命令用法介绍

&#x1f4d1;Linux/Ubuntu 常用命令归类整理 top命令是Linux下常用的性能分析工具&#xff0c;可以实时动态地查看系统中各个进程的资源占用状况&#xff0c;类似于Windows的任务管理器。它可以显示系统总的和分区的CPU使用率、内存使用率、交换区使用率、系统负载、进程数、…

质量好的国产主食冻干猫粮有哪些牌子?推荐十大放心猫粮国产名单

养猫的朋友们应该都知道&#xff0c;猫咪对蛋白质的需求很高&#xff0c;如果猫咪摄入的蛋白质含量不足&#xff0c;就会影响到它们的成长发育。因此&#xff0c;蛋白质的含量对于猫咪的成长发育至关重要。而冻干猫粮是一种高蛋白、高营养、适口性好的产品&#xff0c;不仅能满…

Qt前端技术:3.QSS字体样式

small-caps就是让这个文本中的小写字母用大写的形式写出来并且在用大写的形式表达出来后他本身的大小会变小 有绝对尺寸和相对尺寸的区别 绝对尺寸一般是cm&#xff0c;英寸之类的 相对尺寸如px之类的是由显示器的屏幕分辨率来决定的 如windows用户分辨率一般是96像素点每英…

yocto系列讲解[实战篇]93 - 添加Qtwebengine和Browser实例

By: fulinux E-mail: fulinux@sina.com Blog: https://blog.csdn.net/fulinus 喜欢的盆友欢迎点赞和订阅! 你的喜欢就是我写作的动力! 目录 概述集成meta-qt5移植过程中的问题问题1:virtual/libgl set to mesa, not mesa-gl问题2:dmabuf-server-buffer tries to use undecl…

软考中级应该选哪个?

选择软考中级科目&#xff0c;应该怎么做&#xff1f; 1.1 软考中级科目有哪些可供选择&#xff1f; 1.2 如何选择适合自己的软考中级科目&#xff1f; 系统集成项目管理工程师真的容易吗&#xff1f; 如何在软考中级阶段选择科目&#xff1f;软考中级共有15个科目。软考共…

MACBOOK 通过iterm2连接堡垒机跳转服务器

本公司是通过齐治堡垒机连接远程服务器的环境&#xff0c;因为连接过程中需要自动输入密码和选择主机&#xff0c;所以要使用expect工具&#xff0c;编写expect脚本remote.exp #!/usr/bin/expectif { $argc ! 7 } {send_user "usage: expect $argv0 \[JUMP_HOST\] \[JUM…

项目中webpack优化配置(1)

项目中webpack优化配置 一. 开发效率&#xff0c; 体验 1. DLL&#xff08;开发过程中减少构建时间和增加应用程序的性能&#xff09; 使用 DllPlugin 进行分包&#xff0c;使用 DllReferencePlugin(索引链接) 对 manifest.json 引用&#xff0c;让一些基本不会改动的代码先…

【银行测试】金融银行测试难点与工作内容解析,一篇概全...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、金融银行测试的…

C#电源串口调试

目的 记录串口调试的遇到的一些问题以及相应的解决方法 1.串口定义:串口是计算机与其他硬件传输数据的通道&#xff0c;在计算机与外设通信时起到重要作用 2.串口通信的基础知识 C#中的串口通信类 C#使用串口通信类是SerialPort(),该类使用方法是 new 一个 SerialPort对象 为S…