找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

4609

积分

0

好友

601

主题
发表于 1 小时前 | 查看: 3| 回复: 0

什么是 Protobuf?

Protobuf(Protocol Buffers)是谷歌开源的一种序列化数据结构格式,不依赖特定平台和语言,具备可扩展、轻量且高效的特点。它能够把自定义数据结构序列化成字节流,也能反向将字节流还原成数据结构,所以很适合不同应用之间的数据交换。使用时需要先编写 .proto 文件定义数据格式,编译后即可解析。Protobuf 支持 Java、Python、C/C++ 等多种语言。

Protobuf 的一个重要应用场景,是作为 RPC(远程过程调用)协议中的序列化/反序列化工具。谷歌推出的 gRPC 框架,底层就采用 Protobuf 负责序列化工作。

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 语言结构体的名称。

字段类型表示字段的数据类型,包括 stringint32uint32floatdoubleboolbytes 等,含义和 C 语言中的类型很接近。唯一标识号:消息定义中每个字段后面都有一个唯一编号,用于在二进制格式中识别各个字段。

以上是 protobuf 最简单的语法。

protobuf-c 的使用

默认安装的 protobuf 支持 C++、Java、Python、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.cdemo.pb-c.h,分别负责消息的打包与解析。生成的头文件提供了以下 API:

// 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", msg->name, msg->age);

  // 释放资源
  student__free_unpacked(msg, NULL);
  free(buffer);
  return 0;
}

性能对比

有人对多种通用序列化协议做过横向对比。从结果来看,protobuf 在序列化和反序列化环节的性能都相当出色,同时产出的数据体积也非常小。

各序列化协议序列化与反序列化响应时间对比

各序列化协议字节数占用对比

数据来源:https://www.iteye.com/blog/agapple-859052

在 GitHub 上有更详细、更完整的性能对比数据:https://github.com/eishay/jvm-serializers/wiki ,可进一步查阅。

嵌入式使用

了解完 protobuf 的基础用法后,再考虑它在嵌入式设备上的落地,就会发现标准 protobuf 很难直接跑在资源受限的小型嵌入式环境中。

它几百 KB 的代码体积,已经超过不少芯片的 Flash 容量了。不过查找资料时,有一个成熟的替代方案:nanopb

nanopb 的使用方式和 .proto 语法与 protobuf 完全一致,核心只有三个文件,编译后代码不到 10K,非常适合嵌入式环境。当然它也有代价:用时间去换空间,序列化和反序列化耗时相对长一些,不过相比 JSON 等文本格式仍然快上不少。




上一篇:多核Cache一致性与ACE协议:Snoop嗅探机制详解(AMBA第3篇)
下一篇:Prompt 正在变成 Agent 时代的重要资产:三种用法与四点建议
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-8-14 06:05 , Processed in 1.115784 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表