当前位置: 首页 > 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/

相关文章:

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&#xff08;简称为protobuf&#xff09;是谷歌的语言无关、…...

Kafka优异的性能是如何实现的?

Apache Kafka是一个分布式流处理平台&#xff0c;设计用来处理高吞吐量的数据。它被广泛用于构建实时数据管道和流式应用程序。Kafka之所以能够提供优秀的性能和高吞吐量&#xff0c;主要得益于以下几个方面的设计和实现&#xff1a; 1. 分布式系统设计 Kafka是一个分布式系统…...

(二)MaterializedMySQL具体实施步骤举例

要将 MySQL 中的 test 数据库实时同步到位于同一台服务器&#xff08;IP 地址为 192.168.197.128&#xff09;上的 ClickHouse&#xff0c;您可以使用 MaterializedMySQL 引擎。以下是详细的步骤&#xff1a; 1. 准备工作 确保您的 MySQL 和 ClickHouse 服务都在运行&#xf…...

日志框架简介-Slf4j+Logback入门实践 | 京东云技术团队

前言 随着互联网和大数据的迅猛发展&#xff0c;分布式日志系统和日志分析系统已广泛应用&#xff0c;几乎所有应用程序都使用各种日志框架记录程序运行信息。因此&#xff0c;作为工程师&#xff0c;了解主流的日志记录框架非常重要。虽然应用程序的运行结果不受日志的有无影…...

c 语言, 随机数,一个不像随机数的随机数

c 语言&#xff0c; 随机数&#xff0c;一个不像随机数的随机数 使用两种方式获取随机数&#xff0c;总感觉使用比例的那个不太像随机数。 方法一&#xff1a; rand() 获取一个随机数&#xff0c;计算这个随机数跟最大可能值 RAND_MAX&#xff08;定义在 stdlib.h 中&#xf…...

Git三种方法从远程仓库拉取指定分支

克隆指定分支 git clone -b dev开发分支 https://github.com/521/springboot-rabbitmq.git切换到远程分支 git checkout -b dev开发分支 origin/dev开发分支参考 Git三种方法从远程仓库拉取指定的某一个分支...

7.6分割回文串(LC131-M)

算法&#xff1a; 有很多分割结果&#xff0c;按照for循环去做肯定做不来 这个时候就要想到回溯&#xff01;那就要画树&#xff01; 画树 分割的画树过程其实和组合很像。 例如对于字符串aab&#xff1a; 组合问题&#xff1a;选取一个a之后&#xff0c;在ab中再去选取第…...

stata回归结果输出中,R方和F值到底是用来干嘛的?

先直接回答问题&#xff0c;R方表示可决系数&#xff0c;反映模型的拟合优度&#xff0c;也就是模型的解释能力如何&#xff0c;也可以理解为模型中的各个解释变量联合起来能够在多大程度上解释被解释变量&#xff1b;F值用于模型整体的统计显著性&#xff0c;对应的P值越小&am…...

Windows搭建RTMP视频流服务(Nginx服务器版)

文章目录 引言1、安装FFmpeg2、安装Nginx服务器3、实现本地视频推流服务4、使用VLC或PotPlayer可视化播放器播放视频5、RTSP / RTMP系列文章 引言 RTSP和RTMP视频流的区别 RTSP &#xff08;Real-Time Streaming Protocol&#xff09;实时流媒体协议。 RTSP定义流格式&#xff…...

IP地址SSL证书

IP地址SSL证书是一种专门针对公网IP地址颁发的数字证书。与常规的域名SSL证书类似&#xff0c;其主要目标是提供数据加密和身份验证。以下几点概述了IP地址SSL证书的重要特性及其申请过程&#xff1a; 1. 保护直接IP访问&#xff1a; 当用户直接通过IP地址访问服务时&#xff…...

关于“Python”的核心知识点整理大全49

目录 16.2.10 加亮颜色主题 16.3 小结 第&#xff11;7 章 使用API 17.1 使用 Web API 17.1.1 Git 和 GitHub 17.1.2 使用 API 调用请求数据 17.1.3 安装 requests 17.1.4 处理 API 响应 python_repos.py 注意 17.1.5 处理响应字典 python_repos.py import json i…...

爬虫学习(1)--requests模块的使用

前言 什么是爬虫 爬虫是一种自动化工具&#xff0c;用于从互联网或其他计算机网络上获取数据。它可以模拟人的行为&#xff0c;自动访问网页&#xff0c;提取感兴趣的数据&#xff0c;并将其存储到本地计算机或数据库中。爬虫通常用于搜索引擎、数据分析、信息聚合等领域&…...

【Vue2 + ElementUI】el-table中校验表单

一. 案例 校验金额 阐述&#xff1a;校验输入的金额是否正确。如下所示&#xff0c;点击【编辑图标】会变为input输入框当&#xff0c;输入金额。当输入框失去焦点时&#xff0c;若正确则调用接口更新金额且变为不可输入状态&#xff0c;否则返回不合法金额提示 <templat…...

PgSQL技术内幕 - ereport ERROR跳转机制

PgSQL技术内幕 - ereport ERROR跳转机制 使用客户端执行SQL的时候经常遇到报ERROR错误&#xff0c;然后SQL语句就退出了。当然&#xff0c;事务也会回滚掉。本文我们看下它是如何做到退出SQL语句并回滚事务的。 1、以insert一个numeric类型值为例 表一个字段为numeric(10,2)类型…...

【验证概括 SV的数据类型_2023.12.18】

验证概括 验证的过程是保证芯片实现符合规格说明书&#xff08;Specification&#xff0c;spec&#xff09;的过程 验证的两项任务&#xff1a; RTL sim&#xff1a;前仿真&#xff0c;验证功能 GLS-Gate (Level Simulation)&#xff1a;后仿真&#xff0c;验证功能和时序 验…...

如何在无公网IP环境下远程访问Serv-U FTP服务器共享文件

文章目录 1. 前言2. 本地FTP搭建2.1 Serv-U下载和安装2.2 Serv-U共享网页测试2.3 Cpolar下载和安装 3. 本地FTP发布3.1 Cpolar云端设置3.2 Cpolar本地设置 4. 公网访问测试5. 结语 1. 前言 科技日益发展的今天&#xff0c;移动电子设备似乎成了我们生活的主角&#xff0c;智能…...

电子工程师如何接私活赚外快?

对电子工程师来说&#xff0c;利用业余时间接私活是个很常见的技术&#xff0c;不仅可以赚取额外收入&#xff0c;也能提升巩固技术&#xff0c;可以说国内十个工程师&#xff0c;必有五个在接私活养家糊口&#xff0c;如果第一次接私活&#xff0c;该如何做&#xff1f; 很多工…...

数据库进阶教学——读写分离(Mycat1.6+Ubuntu22.04主+Win10从)

目录 1、概述 2、环境准备 3、读写分离实验 3.1、安装jdk 3.2、安装Mycat 3.3、配置Mycat 3.3.1、配置schema.xml ​​​​3.3.2、配置server.xml 3.4、修改主从机远程登陆权限 3.4.1、主机 3.4.2、从机 3.5、启动Mycat 3.6、登录Mycat 3.7、验证 1、概述 读写分…...

MidJourney笔记(9)-daily_theme-docs-describe

/daily_theme 切换 #daily-theme 频道更新的通知。 但我发现在对话框那里,是没有这个命令的: 但官网是有介绍,不知道是不是版本问题还是这个命令已经无效。 但后来,我发现这个命令是要在Midjourney服务对话框那里才有,在我们后面添加的Mid...

鸿蒙 - arkTs:网络请求封装和使用

1. module.json5文件配置网络请求 {"module": {"requestPermissions": [{"name": "ohos.permission.INTERNET"}]} } 2. 在pages同级创建一个文件夹&#xff0c;起名为api 3. api文件夹下创建index.ts文件&#xff0c;文件内容&…...

conda相比python好处

Conda 作为 Python 的环境和包管理工具&#xff0c;相比原生 Python 生态&#xff08;如 pip 虚拟环境&#xff09;有许多独特优势&#xff0c;尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处&#xff1a; 一、一站式环境管理&#xff1a…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:

一、属性动画概述NETX 作用&#xff1a;实现组件通用属性的渐变过渡效果&#xff0c;提升用户体验。支持属性&#xff1a;width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项&#xff1a; 布局类属性&#xff08;如宽高&#xff09;变化时&#…...

线程同步:确保多线程程序的安全与高效!

全文目录&#xff1a; 开篇语前序前言第一部分&#xff1a;线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分&#xff1a;synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分&#xff…...

Objective-C常用命名规范总结

【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名&#xff08;Class Name)2.协议名&#xff08;Protocol Name)3.方法名&#xff08;Method Name)4.属性名&#xff08;Property Name&#xff09;5.局部变量/实例变量&#xff08;Local / Instance Variables&…...

ESP32读取DHT11温湿度数据

芯片&#xff1a;ESP32 环境&#xff1a;Arduino 一、安装DHT11传感器库 红框的库&#xff0c;别安装错了 二、代码 注意&#xff0c;DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放

简介 前面两期文章我们介绍了I2S的读取和写入&#xff0c;一个是通过INMP441麦克风模块采集音频&#xff0c;一个是通过PCM5102A模块播放音频&#xff0c;那如果我们将两者结合起来&#xff0c;将麦克风采集到的音频通过PCM5102A播放&#xff0c;是不是就可以做一个扩音器了呢…...

跨链模式:多链互操作架构与性能扩展方案

跨链模式&#xff1a;多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈&#xff1a;模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展&#xff08;H2Cross架构&#xff09;&#xff1a; 适配层&#xf…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

算法岗面试经验分享-大模型篇

文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer &#xff08;1&#xff09;资源 论文&a…...

现有的 Redis 分布式锁库(如 Redisson)提供了哪些便利?

现有的 Redis 分布式锁库&#xff08;如 Redisson&#xff09;相比于开发者自己基于 Redis 命令&#xff08;如 SETNX, EXPIRE, DEL&#xff09;手动实现分布式锁&#xff0c;提供了巨大的便利性和健壮性。主要体现在以下几个方面&#xff1a; 原子性保证 (Atomicity)&#xff…...