一 什么是Protocol Buffer
Protocol Buffer是一种支持多平台、多语言、可扩展的的数据序列化机制,相较于XML来说,protobuf更小更快更简单,支持自定义的数据结构,用protobuf编译器生成特定语言的源代码,目前protoBuf对主流的编程语言都提供了支持,非常方便的进行序列化和反序列化。
官方网址
官方支持的语言有:
- Java
- Python
- Objective-C
- C++
- Dart
- Go
- Ruby
- C#
这里对Protocol Buffer的语法不做过多解释,这篇文章主要介绍两部分内容
- MCU端集成nanopb
- Android端集成Protocol Buffer
Protocol Buffer语法
二 MCU端集成nanopb
可以看到官方并没有对C语言进行支持,想要在MCU上跑Protocol Buffer需要另外一个开源工具Nanopb
nanopb是protocol buffer协议的纯C实现,没有依赖其他库,只需要几个C文件就可以了。非常适合用来做嵌入式设备的通信协议。
Nanopb基础用法
1 移植nanopb
从nanopb下载,解压到本地 在我们的工程中集成Nanopb只需要添加这几个文件就可以了。
- pb.h
- pb_common.c
- pb_common.h
- pb_decode.c
- pb_decode.h
- pb_encode.c
- pb_encode.h
2 编译.proto文件
- 安装protoc
将protoc.exe放到C:\Windows\System32目录下,确保Path已经包含了C:\Windows\System32路径,如果没有,作为程序员的我们应该知道怎么配置Path了吧。
下载protoc.exe
在命令行验证是否安装成功 2. 编写一个简单的.proto
syntax = "proto2";
message SimpleMessage {
required int32 lucky_number = 1;
required string name = 2;
}
将文件保存成为simple.proto
- 开始编译
将simple.proto放到目录nanopb-0.4.5-windows-x86\generator
通过执行命令(.\nanopb_generator.py .\simple.proto)将simple.proto编译成.c和.h
在当前目录生成了simple.pb.h和simple.pb.c文件,将这两个文件添加到自己的工程。
3. 简单demo
bool encode_string(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
{
char *str = *arg;
if (!pb_encode_tag_for_field(stream, field))
return false;
return pb_encode_string(stream, (uint8_t*)str, strlen(str));
}
bool decode_string(pb_istream_t *stream, const pb_field_t *field, void **arg)
{
int i=0;
char* tmp = *arg;
while (stream->bytes_left)
{
uint64_t value;
if (!pb_decode_varint(stream, &value))
return false;
*(tmp+i)=value;
i++;
}
return true;
}
int simple_main(void)
{
uint8_t buffer[128];
size_t message_length;
char* nameStr = "abcdefg";
bool status;
{
SimpleMessage message = SimpleMessage_init_zero;
pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
message.lucky_number = 1569875;
message.name.arg = nameStr;
message.name.funcs.encode = encode_string;
status = pb_encode(&stream, SimpleMessage_fields, &message);
message_length = stream.bytes_written;
NRF_LOG_DEBUG("message_legth: %d",message_length);
if (!status)
{
NRF_LOG_DEBUG("Encoding failed: %s", PB_GET_ERROR(&stream));
return 1;
}
}
{
SimpleMessage message = SimpleMessage_init_zero;
char nameDe[64];
memset(nameDe, 0, 64);
message.name.funcs.decode = decode_string;
message.name.arg = nameDe;
pb_istream_t stream = pb_istream_from_buffer(buffer, message_length);
status = pb_decode(&stream, SimpleMessage_fields, &message);
if (!status)
{
NRF_LOG_DEBUG("Decoding failed: %s", PB_GET_ERROR(&stream));
return 1;
}
NRF_LOG_DEBUG("Your lucky number was %d!", (int)message.lucky_number);
NRF_LOG_DEBUG("Your name: %s",&nameDe[0]);
}
return 0;
}
至此我们的MCU端就集成完毕了,可以根据自己的业务需要编写对应的序列化和反序列换的接口函数。
三 Android端集成Protocol Buffer
1 工程配置
- 在工程的build.gradle中添加protobuf-gradle-plugin
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.6'
}
- 在app的build.gradle中添加protobuf
apply plugin: 'com.google.protobuf'
protobuf {
protoc {
artifact = 'com.google.protobuf:protoc:3.0.0'
}
plugins {
javalite {
artifact = 'com.google.protobuf:protoc-gen-javalite:3.0.0'
}
}
generateProtoTasks.generatedFilesBaseDir = "$projectDir/src/main/java/com/hytto/test/protocbuf/java"
generateProtoTasks {
all().each { task ->
task.plugins {
javalite {
outputSubDir = ''
}
}
}
}
}
android {
sourceSets {
main {
java {
srcDirs 'src/main/java/com/hytto/test/protocbuf/java'
}
proto {
srcDirs 'src/main/java/com/hytto/test/protocbuf/proto'
}
}
}
}
dependencies {
implementation 'com.google.protobuf:protobuf-lite:3.0.0'
}
将simple.proto放到’src/main/java/com/hytto/test/protocbuf/proto’这个目录,然后点击编译。 编译成功之后可以看到Simple.java
2 简单的demo
Simple.SimpleMessage.Builder builder = Simple.SimpleMessage.newBuilder();
String name = "abcdefg";
builder.setLuckyNumber(1569875);
builder.setName(name);
Simple.SimpleMessage message = builder.build();
msgBytes = message.toByteArray();
String strMsg = "length " + msgBytes.length;
for (int i = 0; i < msgBytes.length; i++){
String str = String.format(" %02x", msgBytes[i]);
strMsg += str;
}
Log.d(TAG, strMsg);
try {
Simple.SimpleMessage messageDe = Simple.SimpleMessage.parseFrom(msgBytes);
String nameDe = messageDe.getName();
int luckNumDe = messageDe.getLuckyNumber();
Log.d(TAG, "decode name: " + nameDe + "; luckNumDe: " + luckNumDe);
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
}
|