嵌入式Protobuf扫盲入门
什么是Protobuf
Protocol Buffer (简称Protobuf) 是Google出品的性能优异、跨语言、跨平台的序列化库。
2001年初,Protobuf首先在Google内部创建, 我们把它称之为 proto1,一直以来在Google的内部使用,其中也不断的演化,根据使用者的需求也添加很多新的功能,一些内部库依赖它。几乎每个Google的开发者都会使用到它。
Google开始开源它的内部项目时,因为依赖的关系,所以他们决定首先把Protobuf开源出去。 proto1在演化的过程中有些混乱,所以Protobuf的开发者重写了Protobuf的实现,保留了proto1的大部分设计,以及proto1的很多的想法。但是开源的proto2不依赖任何的Google的库,代码也相当的清晰。2008年7月7日,Protobuf开始公布出来。
Protobuf公布出来也得到了大家的广泛的关注, 逐步地也得到了大家的认可,很多项目也采用Protobuf进行消息的通讯,还有基于Protobuf的微服务框架GRPC。在使用的过程中,大家也提出了很多的意见和建议,Protobuf也在演化,于2016年推出了Proto3。 Proto3简化了proto2的开发,提高了开发的效能,但是也带来了版本不兼容的问题。
目前Protobuf的稳定版本是3.9.2,于2019年9月23日发布。由于很多公司很早的就采用了Protobuf,所以很多项目还在使用proto2协议,目前是proto2和proto3同时在使用的状态。
Protocol Buffer名称来自于初期一个主要的类的名称ProtocolBuffer。
Google当前并没有Protobuf的相关专利,所以不必担心侵权的问题。
序列化
序列化(serialization、marshalling)的过程是指将数据结构或者对象的状态转换成可以存储(比如文件、内存)或者传输的格式(比如网络)。反向操作就是反序列化(deserialization、unmarshalling)的过程。
Protobuf支持很多语言,比如C++、C#、Dart、Go、Java、Python、Rust等,同时也是跨平台的,所以得到了广泛的应用。
Protobuf包含序列化格式的定义、各种语言的库以及一个IDL编译器。正常情况下你需要定义proto文件,然后使用IDL编译器编译成你需要的语言。
鉴于官方推荐新代码采用proto3,这个教程主要介绍proto3的开发。官方建议新项目采用proto3,老项目因为兼容性的问题继续使用proto2,并且会长时间的支持proto2
嵌入式的Protobuf
很遗憾,谷歌官方并没有支持的Protobuf c官方库,
但是在其网页介绍的第三方附加组件中包含了Protobuf c工具实现,Protocol Buffers 的第三方附加组件其中支持c实现的有以下
C: https://github.com/protobuf-c/protobuf-c
C: http://koti.kapsi.fi/jpa/nanopb/
C: https://github.com/cloudwu/pbc/
C: https://github.com/haberman/upb/wiki
C: https://github.com/squidfunk/protobluff
C: https://github.com/eerimoq/pbtools
嵌入式运用比较广泛的是Nanopb,接下来就手把手使用Nanopd 序列化数据移植到STM32工程中。
下载Nanopb工具
这里选择windows版本
解压后的文件夹内容如下:
将\nanopb-0.4.5-windows-x86\generator-bin路径添加到系统环境,(我的电脑->右键属性->高级系统设置->高级->环境变量)
接着准备一个STM串口打印的工程,在工程下创建protobuff文件夹,
将\nanopb-0.4.5-windows-x86\以下七个文件复制到protobuff文件夹下
接着创建一个student.proto文件,注意后缀为.proto
syntax = "proto2";
message Student
{
required uint32 num = 1;
required uint32 py_score = 2;
required uint32 c_score = 3;
}
然后在工程中创建的protobuff文件夹中,按住Shift+鼠标右键,选中在此处打开Powershell窗口,(或者win+r 输入cmd 进入到工程中的protobuff文件夹中),在窗口输入
protoc --nanopb_out=. student.proto
此时在文件夹下生成了student.pb.c和student.pb.h文件
将protobuff文件夹中的源码和头文件添加到工程中,
接着添加测试代码:
#include "student.pb.h"
#include <pb_encode.h>
#include <pb_decode.h>
void protobuf_test(void)
{
uint8_t buffer[64] = {0}; //需要发送的数据缓存
Student pack_stu = {0}; //需要编码的序列化的对象
pb_ostream_t o_stream = {0}; //初始化输出序列化对象
Student unpack_stu = {0}; //初始化输出序列化对象
pb_istream_t i_stream = {0}; //初始化接收序列化对象
// 组包
pack_stu.num = 88;
pack_stu.py_score = 90;
pack_stu.c_score = 99;
o_stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
pb_encode(&o_stream, Student_fields, &pack_stu);
// 解包
i_stream = pb_istream_from_buffer(buffer, sizeof(buffer));
pb_decode(&i_stream, Student_fields, &unpack_stu);
printf("unpack_stu.num = %d\r\n", unpack_stu.num);
printf("unpack_stu.py_score = %d\r\n", unpack_stu.py_score);
printf("unpack_stu.c_score = %d\r\n", unpack_stu.c_score);
}
输出打印:
unpack_stu.num = 88
unpack_stu.py_score = 90
unpack_stu.c_score = 99
使用proto3
将student.proto修改为:
syntax = "proto3";
message Student
{
uint32 num = 1;
uint32 py_score = 2;
uint32 c_score = 3;
}
重新使用
protoc --nanopb_out=. student.proto
可以生成使用proto3语法的student.pb.c和student.pb.h文件
注意
proto2和proto3的语法不能混合使用(即.proto文件要么全为proto2语法要么全为proto3语法),否则生成文件会报错
关于proto3语法介绍下篇再做介绍。