当前位置: 首页 > news >正文

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.lryc.cn/news/269175.html

相关文章:

  • Kafka优异的性能是如何实现的?
  • (二)MaterializedMySQL具体实施步骤举例
  • 日志框架简介-Slf4j+Logback入门实践 | 京东云技术团队
  • c 语言, 随机数,一个不像随机数的随机数
  • Git三种方法从远程仓库拉取指定分支
  • 7.6分割回文串(LC131-M)
  • stata回归结果输出中,R方和F值到底是用来干嘛的?
  • Windows搭建RTMP视频流服务(Nginx服务器版)
  • IP地址SSL证书
  • 关于“Python”的核心知识点整理大全49
  • 爬虫学习(1)--requests模块的使用
  • 【Vue2 + ElementUI】el-table中校验表单
  • PgSQL技术内幕 - ereport ERROR跳转机制
  • 【验证概括 SV的数据类型_2023.12.18】
  • 如何在无公网IP环境下远程访问Serv-U FTP服务器共享文件
  • 电子工程师如何接私活赚外快?
  • 数据库进阶教学——读写分离(Mycat1.6+Ubuntu22.04主+Win10从)
  • MidJourney笔记(9)-daily_theme-docs-describe
  • 鸿蒙 - arkTs:网络请求封装和使用
  • 多功能演示工具ProVideoPlayer2 mac特色介绍
  • java设计模式学习之【责任链模式】
  • docker 安装可视化工具 Protainer 以及 汉化
  • 【SpringBoot篇】详解Bean的管理(获取bean,bean的作用域,第三方bean)
  • 彭涛:2023年终复盘,工作,团队,个人!
  • 【数据结构和算法】---二叉树(2)--堆的实现和应用
  • 【大模型实践】基于文心一言的对话模型设计
  • 聊聊PowerJob的StoreStrategy
  • HTML+CSS+JS网页设计期末课程大作业 web课程设计 web前端开发 网页规划与设计
  • vscode | python | remote-SSH | Debug 配置 + CLIP4Clip实验记录
  • 【Linux】实现windows主机与ubuntu虚拟机系统之间文件/字符复制粘贴