Protobuf文件的编写
介绍:
数据从 SLPI 侧上报到 HAL 层,都需要从底层使用pb接口,将规定的数据格式 publish 上来,在HAL层再通过调用同样的数据格式的接口解析出数据。
数据格式的定义——Protobuf文件的编写
什么是Protobuf?
Protobuf (Protocol Buffers) 是谷歌开发的一款无关平台,无关语言,可扩展,轻量级高效的序列化结构的数据格式,用于将自定义数据结构序列化成字节流,和将字节流反序列化为数据结构。
故适合在不同应用之间相互通信的数据交换格式,需要定义数据格式的.proto文件,编译后即可解析。支持的语言(Java、Python、C/C++等)文件;
Protobuf 语法
使用 protobuf 前需要定义一个 .proto 文件,内容表示消息的格式,下面以一个简单的 demo 举例:
// syntax关键词定义使用的是proto3语法版本
syntax = "proto3";
// message关键词,标记开始定义一个消息
message Student{
// 字段类型 名字 = 唯一标识号
string name = 1;
int32 age = 2;
}
第一行 syntax 指定了要用 proto 的语法,proto3即为proto3对应的语法。
message 表示消息类型,可以有很多个。其结构与 C 语言结构体很相似
Student 表示消息名称,类似于 C 语言结构体的名称
字段类型:用于表示字段的数据类型,包括 string、int32、uint32、float、double、bool、bytes 等等 ,含义和类型名和 C 语言非常相似。
唯一标识号:在消息定义中,每个字段后面都有一个唯一的标识号,标识号是用于消息的二进制格式中识别各个字段。
以上就是 protobuf 最简单的语法。
protobuf-c 的使用
在默认安装的 protobuf 中支持了 C++、java、ptython、rust 众多语言,唯独没有支持 C 语言,在 C 语言中使用 protobuf 需要单独编译安装 protobuf-c。
# protobuf-c 需要 protobuf
sudo apt install protobuf-compiler
# 一键三连安装 protobuf-c
git clone https://github.com/protobuf-c/protobuf-c.git && cd protobuf-c
./autogen.sh && ./configure
make -j && sudo make install
安装完成后,protobuf-c 和其它语言的编译一样,指定需要编译的文件和输出的文件类型即可。
protoc-c demo.proto --c_out=.
protoc-c 是 .proto C 语言的编译器, –c_out 指定输出的文件类型和输出路径
编译完成后,会在指定的路径生成两个文件:demo.pb-c.c 、demo.pb-c.h ,这两个文件分别为打包和解析消息的代码,其编译完头文件提供了以下方法:
// demo.pb-c.h
/* Student methods */
void student__init(Student *message);
size_t student__get_packed_size(const Student *message);
size_t student__pack(const Student *message, uint8_t *out);
size_t student__pack_to_buffer(const Student *message, ProtobufCBuffer *buffer);
Student *student__unpack(ProtobufCAllocator *allocator, size_t len,
const uint8_t *data);
void student__free_unpacked(Student *message, ProtobufCAllocator *allocator);
下面使用这些 API 写一个简单的 demo
#include "demo.pb-c.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
int main() {
Student student = STUDENT__INIT;
void *buffer = NULL;
int32_t len;
Student *msg = NULL;
// 初始化数据
student.name = "student";
student.age = 28;
// 打包数据
len = student__get_packed_size(&student);
buffer = malloc(len);
student__pack(&student, buffer);
// TODO: 发送数据到远端设备
// TODO: 从远端设备接受数据
// 解包数据
msg = student__unpack(NULL, len, buffer);
printf("student name : %s, age : %d\n", student.name, student.age);
// 释放资源
student__free_unpacked(msg, NULL);
free(buffer);
return 0;
}
性能对比
有人简单做了一个各种通用序列化协议技术的对比,在几个比较项中 protobuf 的序列化和反序列化性能都是最优秀的,同时占用的体积却非常小。
在 github 上有更详细完整的性能对比:https://github.com/eishay/jvm-serializers/wiki,可进行查看。
嵌入式使用
在了解完 protobuf 的基本使用后,考虑如何在嵌入式设备上使用时,发现 protobuf 很难在一般的小型嵌入式设备上使用。
因为其几百K的代码大小,已经超过不少芯片 Flash 的大小了,但是查找资料时发现了替代品: nanopb 。
nanopb 的使用和 .proto 的语法与 protobuf 一致, 一共三个核心文件,编译后的代码不到 10K,非常适合在在嵌入式环境下使用。
当然缺点还是有的,就是用时间换空间,序列化和反序列化时间相对较长,不过比 json 等格式仍然快上不少。