消息序列化工具-protobuf介绍及安装使用技巧

2023-05-16

简介

protobuf是google团队开发的用于高效存储和读取结构化数据的工具。
xml、json也可以用来存储此类结构化数据,但是使用protobuf表示的数据能更加高效,并且将数据压缩得更小,大约是json格式的1/10,xml格式的1/20。

以下介绍基于protobuf 2.6版本

定义message结构

protobuf将一种结构称为一个message类型,对应C/C++中的struct;
我们以电话簿中的数据为例。

struct Person {
	string name;
	int32 id;
	string email;
	list<int32> samples;
};

对应到protobuf的定义如下:

message Person {
  required string name = 1;
  required int32 id = 2; [default = 0]
  optional string email = 3;
  repeated int32 samples = 4 [packed=true];
}

其中Person是message这种结构的名称,name、id、email是其中的Field,每个Field保存着一种数据类型,=后面的1、2、3是Filed对应的id。
id在1-15之间编码只需要占一个字节,包括Filed数据类型和Filed对应数字id,在16-2047之间编码需要占两个字节,所以最常用的数据对应id要尽量小一些。

Field最前面的required,optional,repeated是这个Filed的规则,分别表示该数据结构中这个Filed有且只有1个,可以是0个或1个,可以是0个或任意个。

  • required: 必须设置它的值
  • optional: 可以设置,也可以不设置它的值
  • repeated: 可以认为是动态分配的数组

google工程师认为使用required危害更大,他们更喜欢使用optional, repeated.
optional后面可以加default默认值,如果不加,数据类型的默认为0,字符串类型的默认为空串。repeated后面加[packed=true]会使用新的更高效的编码方式。

注意:使用required规则的时候要谨慎,因为以后结构若发生更改,这个Filed若被删除将可能导致兼容性的问题。

保留Filed和保留Filed number
每个Filed对应唯一的数字id,但是如果该结构在之后的版本中某个Filed删除了,为了保持向前兼容性,需要将一些id或名称设置为保留的,即不能被用来定义新的Field

message Person {
  reserved 2, 15, 9 to 11;
  reserved "samples", "email";
}

引用其它message类

在同一个文件中,可以直接引用定义过的message类型。
在同一个项目中,可以用import来导入其它message类型。如 import “OnstarInterfaceAndroid.proto”;
也可以在一个message类型中嵌套定义其它的message类型。

message扩展
如果要import的 proto 中有如下定义

message Person {
  // ...
  extensions 100 to 199;
}

在另一个文件中,import 这个proto之后,可以对Person这个message进行扩展。

extend Person {
  optional int32 bar = 126;
}

枚举类型

比如电话号码,只有移动电话、家庭电话、工作电话三种类型,因此枚举作为选项,枚举类型的默认值为第一项。
在上面的例子中在个人message中加入电话号码这个Filed。如果枚举类型中有不同的名字对应相同的数字id,需要加入option allow_alias = true这一项,否则会报错。
枚举类型中也有reserverd Filed和number,定义和message中一样。

message Person {
  required string name = 1;
  required int32 id = 2;
  optional string email = 3;

  enum PhoneType {
    //allow_alias = true;
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }

  message PhoneNumber {
    required string number = 1;
    optional PhoneType type = 2 [default = HOME];
  }

  repeated PhoneNumber phones = 4;
}

枚举定义之后,在生成源代码时会在编解码的接口中检查有效性

数据类型对应关系

在使用规则创建proto类型的数据结构文件之后,会将其转化成对应编程语言中的头文件或者类定义。
proto中的数据类型和c++,Python中的数据类型对应规则如下:
.proto C++ Python 介绍

  • double double float
  • float float float
  • int32 int32 int 可变长编码,对负数效率不高
  • int64 int64 int/long
  • uint32 uint32 int/long
  • uint64 uint64 int/long
  • sint32 int32 int 可变长编码,对负数效率较高
  • sint64 int64 int/long
  • fixed32 uint32 int/long 32位定长编码
  • fixed64 uint64 int/long
  • sfixed32 int32 int
  • sfixed64 int64 int/long
  • bool bool bool
  • string string str/unicode UTF-8编码或者7-ASCII编码
  • bytes string str

由此可知,proto类型和C++类型几乎完全一致;

编码规则

protobuf有一套高效的数据编码规则。

可变长整数编码

每个字节有8bits,其中第一个bit是most significant bit(msb),0表示结束,1表示还要读接下来的字节。
对message中每个Filed来说,需要编码它的数据类型、对应id以及具体数据。
数据类型有以下6种,可以用3个bits表示。每个整数编码用最后3个bits表示数据类型。所以,对应id在1~15之间的Filed,可以用1个字节编码数据类型、对应id。
Type Meaning Used For

  • 0 Varint int32, int64, uint32, uint64, sint32, sint64, bool, enum
  • 1 64-bit fixed64, sfixed64, double
  • 2 Length-delimited string, bytes, embedded messages, packed repeated fields
  • 3 Start group groups (deprecated)
  • 4 End group groups (deprecated)
  • 5 32-bit fixed32, sfixed32, float

(id + 数据类型) + 数值
比如对于下面这个例子来说,如果给a赋值150,那么最终得到的编码是什么呢?

message Test {
  optional int32 a = 1;
}

首先数据类型编码是000,因此id和它联合起来的编码是00001000.
然后值150的编码是1 0010110,采用小端序交换位置,即0010110 0000001,前面补1后面补0,即10010110 00000001,即96 01,
加上最前面的数据类型编码字节,总的编码为08 96 01。

有符号整数编码

如果用int32来保存一个负数,结果总是有10个字节长度,被看做是一个非常大的无符号整数。
使用有符号类型会更高效。它使用一种ZigZag的方式进行编码。即0还是0,-1编码成1,1编码成2,-2编码成3这种形式。
也就是说,对于sint32来说,n编码成 (n << 1) ^ (n >> 31),注意到第二个移位是算法移位。

定长编码

定长编码是比较简单的情况。

常用API, 可以直接查看生成的代码中的 .h 文件
protoc为message的每个required字段和optional字段都定义了以下几个函数(不限于这几个):
TypeName xxx() const;      //获取字段的值
bool has_xxx();         //判断是否设值
void set_xxx(const TypeName&); //设值
void clear_xxx();          //使其变为默认值

为每个repeated字段定义了以下几个:
TypeName* add_xxx(); //增加结点, 然后需要拿到结构体指针后对成员进行赋值操作;
TypeName xxx(int) const; //获取指定序号的结点,类似于C++的"[]"运算符
TypeName* mutable_xxx(int); //类似于上一个,但是获取的是指针
int xxx_size();   //获取结点的数量

下面几个是常用的序列化函数:

bool SerializeToOstream(std::ostream * output) const; //输出到输出流中
bool SerializeToString(string * output) const; //输出到string
bool SerializeToArray(void * data, int size) const; //输出到字节流,可以通过ByteSize方法计算存储空间后使用new申请一块内存给data;

与之对应的反序列化函数:

bool ParseFromIstream(std::istream * input);    //从输入流解析
bool ParseFromString(const string & data);    //从string解析
bool ParseFromArray(const void * data, int size); //从字节流解析,size为buffer的size

其他常用的函数:

bool IsInitialized(); //检查是否所有required字段都被设值
size_t ByteSize() const; //获取二进制字节序列的大小

对嵌套message成员提供的函数:

bool has_xxx()
void set_has_xxx()
void clear_has_xxx()
void clear_xxx()
const TypeName& xxx() const //前面几个和上面介绍的一致
TypeName* mutable_xxx() //会自动new一块内存并返回,然后拿到结构体指针后对成员进行赋值操作;
TypeName* release_xxx()
void set_allocated_xxx(TypeName* xxx) //传入的参数需要自己手动new一块内存,和mutable_xxx()有所区别;
参考如下

inline ::RadioInterfaceTBox::RadioInfo* RadioTxCommunicationSyncNotify::mutable_radiobasicinfo() {
  set_has_radiobasicinfo();
  if (radiobasicinfo_ == NULL) radiobasicinfo_ = new ::RadioInterfaceTBox::RadioInfo;
  return radiobasicinfo_;
}
inline void RadioTxCommunicationSyncNotify::set_allocated_radiobasicinfo(::RadioInterfaceTBox::RadioInfo* radiobasicinfo) {
  delete radiobasicinfo_;
  radiobasicinfo_ = radiobasicinfo;
  if (radiobasicinfo) { set_has_radiobasicinfo(); } else { clear_has_radiobasicinfo(); }
}

安装protobuf包

官方地址:https://github.com/google/protobuf/blob/master/src/README.md
首先官方下载源码并解压
以ubuntu安装为例

$ sudo apt-get install autoconf automake libtool curl make g++ unzip

$ sudo ./autogen.sh
$ sudo ./configure
$ sudo make -j8
$ sudo make check
$ sudo sudo make install
$ sudo sudo ldconfig #refresh shared library cache.

实例演示

首先,定义下面的test.proto文件

message Info{
    required string name = 1;
    optional int32   age = 2;
}

message是protobuf中定义的数据结构;

  1. reuqied关键字表示这个参数必须包含在消息体中(如果用optional关键字来定义这个参数,那表示该参数可以缺省)
  2. string是参数Info的数据类型
  3. name是参数名称
  4. =1表示name是该消息体的第1个参数

定义proto文件之后需要用protobuf提供的编译工具将proto文件编译成不同语言的源码,此处使用C++。

protoc -I=./ --cpp_out=./ test.proto

将会生成两个文件, test.pb.h 和test.pb.cc;按照C++习惯我们把test.pb.cc文件更名为test.pb.cpp

编写C++源代码进行测试

/* helloproto.cpp */
#include <stdio.h>
#include <iostream>
#include <iomanip>
#include <string>
#include "test.pb.h"
using namespace std;

int main() {
	int length = 0;
	/* 首先设置各字段的value,然后序列化 */
	Info *pinfo = new Info();
	pinfo->set_name("testname");
	pinfo->set_age(655384);
	cout << "info.name=" << pinfo->name() << ", age=" << hex << pinfo->age() << endl;
	length = pinfo->ByteSize();
	uint8_t *buf = new uint8_t[length];
	pinfo->SerializeToArray(buf, length); /* 序列化 */

	for (int i = 0; i < length; i++)
	{
		printf("%02X(%c) ", buf[i], buf[i]);
		/* cout << hex << buf[i] << " "; */
	}
	cout << endl; 

	/* 首先解序列化,然后获取各字段的value; */
	Info *pinfo2 = new Info();
	pinfo2->ParseFromArray(buf, length);
	cout << pinfo2->name() << endl;
	cout << pinfo2->age()  << endl;

	delete buf;
	delete pinfo;
	delete pinfo2;
	return 0;
}

编译

首先确定libprotobuf.so的位置

$ which protoc
#/usr/local/bin/protoc

$ sudo find /usr/ -name libprotobuf.so
#/usr/local/lib/libprotobuf.so

添加连接选项-L 路径 -lprotobuf

$ g++ helloproto.cpp test.pb.cpp -L /usr/local/lib/ -lprotobuf -pthread -o helloproto
$ ./helloproto

参考资料

https://developers.google.com/protocol-buffers/docs/cpptutorial
https://www.jianshu.com/p/419efe983cb2

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

消息序列化工具-protobuf介绍及安装使用技巧 的相关文章

  • protobuf中SerializeToString和SerializePartialToString的区别

    文章目录 前言proto2message定义message扩展注意事项 proto3序列化SerializeToString和SerializeAsString区别SerializeToString和SerializePartialToSt
  • protobuf repeated数组类型的使用

    http www cppblog com API archive 2014 12 09 209070 aspx protobuf是Google开发的一个序列化框架 xff0c 类似XML xff0c JSON xff0c 基于二进制 xff
  • protobuf的序列化和反序列化的分析

    一 protobuf的optional 数据类型序列化分析 1 optional 的protobuf的文件 格式 syntax proto2 message test proto optional int32 proto1 1 option
  • protobuf版本冲突问题解决

    1 proto caffe pb h 17 2 error error This file was generated by an older version of protoc 如果你装有anaconda 以及tensorflow 或者在
  • c++ protobuf 可能会遇到的坑

    1 发现存在内存泄露 程序退出时记得调用 google protobuf ShutdownProtobufLibrary 这里一定是在程序退出时调用 如果调用后又使用了 protobuf 会出现异常 因为protobuf 中使用构造 会有创
  • TypeError: Descriptors cannot not be created directly. If this call came from a _pb2.py file……

    问题描述 TypeError Descriptors cannot not be created directly If this call came from a pb2 py file your generated code is ou
  • 【Protobuf速成指南】enum类型的使用

    文章目录 2 1枚举类型 一 如何定义枚举类型 二 语法规范 三 重定义问题 四 enum类型相关函数 五 Contact 2 1 改写 六 总结 2 1枚举类型 本系列文章将通过对通讯录项目的不断完善 带大家由浅入深的学习Protobuf
  • 【go】Unmarshal时候报错提示proto.Unmarshal: missing method ProtoReflect

    问题 使用proto Unmarshal报错 提示以下信息 cannot use promoRule variable of type db PromotionRuleSet as protoreflect ProtoMessage val
  • protobuf反射详解及应用(pb/json相互转换)

    关于protobuf的使用 编码原理 编码原理应用 可以分别参见以下文章 Python 操作 protobuf 常见用法 linux环境下protobuf的安装与使用 Protobuf编码规则详解 protobuf编码原理及其在schema
  • protobuf生成prototxt文件

    使用protobuf可以分为以下几步 1 proto文件的定义 在这个文件中定义了最终生成的prototxt格式 举个例子如下所示 syntax proto2 package label proto message DetectLabel
  • protoc 同时编译多个.protoc文件

    官方的示例 只是编译一个文件的命令行 protoc proto path IMPORT PATH cpp out DST DIR java out DST DIR python out DST DIR go out DST DIR ruby
  • Win10 下 ProtoBuf 安装编译以及在 C++ 中的用法

    ProtoBuf Protocol Buffer protoBuf 或 PB 是 google 的一种数据交换的格式 它独立于语言 独立于平台 google 提供了多种语言的实现 java c c go 和 python 每一种实现都包含了
  • 【Protobuf】pb中类型字段不匹配问题

    文章目录 背景 结论 原始数据 测试1 测试2 背景 客户端更新proto 新增message字段 探索新增字段的数据类型和标签对服务端反序列化数据的影响 结论 新增字段数据类型与服务端相同标签数据类型 不同 无法获取数据 但是不报错 相同
  • protobuf详细介绍和使用

    一 protobuf初识 一 protocol buffers 是什么 protocol buffers 是一种灵活 高效 自动化机制的结构数据序列化方法 可类比 XML 但是比 XML 更小 更快 更为简单 你可以定义数据的结构 然后使用
  • Protobuf在java中的简单使用实例

    TTprotobuf是一种跨语言的数据转换协议 由google开源的 已支持大部份语言 在一般的数据交互过程中都是使用json xml等来做数据的转换 这其中涉及复杂的解析与序列化反序列化问题 如果在大量数据并发请求时 也会导致性能问题 p
  • C++Protobuf的生成与使用

    编写 proto文件 syntax 表明protobuf的版本号 末尾不要忘了 package 表明生成的类对象位于哪一个命名空间 末尾不要忘了 每一个类属性后面的必须跟一个编号 且不能重复 syntax proto3 package Pr
  • protobuf安装教程

    protobuf安装 一 Windows下安装 下载protobuf 配置环境变量 检查是否安装成功 二 Linux下安装 下载protobuf 安装protobuf 检查是否安装成功 一 Windows下安装 下载protobuf 下载地
  • protobuf在C#项目中的使用

    protobuf在C 项目中的使用 在C 项目中 有时候会使用到使用到protobuf来作为通信时数据交换的格式 protobuf ProtocolBuffer 简称PB 是google 的一种数据交换的格式 这是一种二进制的格式 比使用x
  • C++下的protobuf简单使用

    Google Protocol Buffer API简单使用总结 大致步骤如下 1 编写 msg proto 文件 package lm 我理解成命名空间 message helloworld 我理解成类 required int32 id
  • google.api.http

    Http 定义api服务的http配置 它包含一个httprule列表 每个列表指定一个rpc方法到一个或多个http rest api方法的映射 字段 描述 rules HttpRule 一个适用于各个API方法的http配置规则列表 注

随机推荐

  • MAMP PHP5.6、PHP7.4.20 …… 安装redis、mongodb等扩展

    一 下载php对应版本源码 xff1a https www php net releases 解压后放入 对应版本 下面 Application MAMP bin php php5 6 10 include php 二 下载扩展包 http
  • B-Code For 1 Codeforces 768【递归】 好题!

    题意 xff1a 起初 xff0c 序列中仅有数n if n 61 0 amp amp n 61 1 在原来的位置补充3个元素n 2 n 2 n 2 直至该序列用仅有0和1 现在问区间 l r 有多少个1 思路 xff1a 一开始想用vec
  • AtCoder褐名记

    今年四月份开始参加AtCoder比赛 xff0c 至今参加了9次 在第9次结束后 xff0c 涨了一级 xff0c 从最低级的灰名涨到倒数第二级的褐名 相对于我这样的新手而言 xff0c AtCoder比TopCoder和Codeforce
  • 基于FFmpeg H264 + G711A 音视频裸流合并 MP4文件 ( G711A 转 AAC)

    由于 FFmpeg 只支持H264 43 AAC的mp4封装格式的 xff0c 并不支持H264 43 G711的mp4封装格式 所以需要将G711a转码成AAC格式的 然后封装成mp4文件 xff0c 但网上有说 通过修改movenc c
  • YOLOV3 网络结构学习笔记

    注 xff1a 本文非原创 xff0c 文章内容都是引用以下文章中 xff0c 本文只是记录学习笔记 yolo系列之yolo v3 深度解析 木盏的博客 CSDN博客 yolo3 YOLO v3算法详解 Atlas 的博客 CSDN博客 y
  • 基于人脸特征点实现疲劳检测

    为了有效监测驾驶员是否疲劳驾驶 避免交通事故的发生 提出了一种利用人脸特征点进行实时疲劳驾驶检测的新方法 对驾驶员驾驶时的面部图像进行实时监控 首先检测人脸 并利用ERT算法定位人脸特征点 然后根据人脸眼睛区域的特征点坐标信息计算眼睛纵横比
  • 基于 HPSocket , 实现 socket 通讯

    HPSocket HP Socket 是一套通用的高性能 TCP UDP HTTP 通信框架 xff0c 包含服务端组件 客户端组件和 Agent 组件 xff0c 广泛适用于各种不同应用场景的 TCP UDP HTTP 通信系统 xff0
  • windows 基于 MediaPipe 实现 PoseTracking

    MediaPipe是用于构建跨平台多模态应用ML管道的框架 xff0c 其包括快速ML推理 xff0c 经典计算机视觉和媒体内容处理 xff08 如视频解码 xff09 在2019年6月举行的CVPR大会 xff0c MeidaPipe正式
  • windows 基于 MediaPipe 实现 HandTracking

    OverView 感知手的形状和运动的能力可能是改善跨各种技术领域和平台的用户体验的重要组成部分 例如 xff0c 它可以构成手语理解和手势控制的基础 xff0c 还可以在增强现实中将数字内容和信息叠加在物理世界之上 虽然对人们来说很自然
  • DeepStream 部署 RTSP + scaled-yolov4 (tensorrtx)

    DeepStream应用程序将深度神经网络和其他复杂的处理任务引入到流处理管道中 xff0c 以实现对视频和其他传感器数据的近实时分析 从这些传感器中提取有意义的见解为提高运营效率和安全性创造了机会 例如 xff0c 摄像头是当前使用最多的
  • DeepStream 多路拉取RTSP视频流

    上一篇介绍DeepStream 如何集成Yolov4模型 xff0c 那么本篇介绍下如何实现读取多路RTSP 代码 主要代码参考 https github com belarbi2733 deepstream rtspsrc yolo详细代
  • 编码格式(关于utf-8,gb2312,gbk,big5等)

    计算机数据是以二进制的方式来存储 xff0c 符号代表文字 那么二进制数据表示的模式就是编码 xff0c 跟电报的加密解密是一个道理 xff0c 那么如何将这些数据转化成有效字符 xff0c 这就涉及到了编码格式 xff0c 一般常见的编码
  • Ubuntu配置桥接网络

    第一步 xff1a 点击虚拟机 xff0c 点击设置 第二步 xff1a 点击网络适配器 xff0c 选中桥接模式并确定 第三步 xff1a 点击编辑 xff0c 打开虚拟网络编辑器 xff0c 选中桥接模式 点击 网络属性 找到描述 xf
  • 基于 NCNN, 实现 yolov8

    记录下 基于 ncnn 实现 yolov8 的全部过程 修改 ultralytics nn modules py class Detect forward 和 class C2f forward span class token keywo
  • HRNet 训练自定义数据集

    基于 HRNet 训练人脸特征点数据集 INSTALL conda create n openmmlab span class token assign left variable python span span class token
  • Chatgpt 指令收集

    在使用 ChatGPT 时 xff0c 当你给的指令越精确 xff0c 它的回答会越到位 xff0c 举例来说 xff0c 假如你要请它帮忙写文案 xff0c 如果没给予指定情境与对象 xff0c 它会不知道该如何回答的更加准确 一 写报告
  • openEuler 安装图形桌面环境Gnome或DDE或UKUI

    由于openEuler系统主要针对服务器 xff0c 目前默认安装之后没有图形桌面环境 xff0c 需要的用户可以自己手动安装配置 这里推荐安装深度桌面DDE或优麒麟UKUI环境 安装gnome桌面 sudo dnf makecache s
  • Ubuntu更换国内镜像源

    由于Ubuntu官方镜像速度有限 xff0c 可以使用国内镜像加速更新和下载 xff0c 节约时间 常用的国内镜像有很多 xff0c 本人常用的有如下几个 xff0c 仅供参考 163镜像 mirrors 163 com 清华镜像 mirr
  • ubuntu-2204 gerrit ssh 报错Permission denied (publickey).分析及解决

    ubuntu 2204 gerrit ssh 报错Permission denied publickey 分析及解决 使用repo init sync下载代码时遇到报错 Permission denied publickey 分析排查步骤
  • 消息序列化工具-protobuf介绍及安装使用技巧

    简介 protobuf是google团队开发的用于高效存储和读取结构化数据的工具 xml json也可以用来存储此类结构化数据 xff0c 但是使用protobuf表示的数据能更加高效 xff0c 并且将数据压缩得更小 xff0c 大约是j