protobuf、protobuf-c、protobuf-c-rpc在Linux(Ubuntu18.04)编译安装及交叉编译arm\aarch64版本

文章目录

protobuf、protobuf-c、protobuf-c-rpc在Linux(Ubuntu18.04)编译安装及交叉编译arm\aarch64版本一、前言二、protobuf、rpc、protobuf-c、protobuf-c-rpc介绍1、protobuf2、protobuf-c3、protobuf-c-rpc

三、Ubuntu18.04下编译安装及交叉编译1、前置准备-升级cmake2、protobuf编译安装及交叉编译2.1、正常编译、安装及错误解决方法2.2、交叉编译arm、aarch64版本及错误解决方法

3、protobuf-c编译安装及交叉编译3.1、正常编译安装3.2、交叉编译arm、aarch64版本

4、protobuf-c-rpc编译安装4.1、正常编译安装4.2、交叉编译arm、aarch64版本

四、接口调用示例及演示**1、创建proto文件test.proto****2、使用protoc-c编译proto文件生成服务和客户端代码文件****3、完成rpc客户端和服务端程序****4、编译并测试**5、关键点(重要)

五、最后

一、前言

如下介绍c语言的protobuf+rpc的开源库protobuf-c和protobuf-c-rpc,其适合于嵌入式分布式场景,利用protobuf协议的可扩展性比较方便进行协议兼容升级,利用rpc接口的网络易用性,不需要再从头到尾实现一遍socket通信、通信接口设计,只需要实现C函数接口设计和开发以及利用proto设计好交互协议即可,并且具备一定的跨编程语言交互特性,每个具备联网能力的嵌入式设备都可作为rpc客户端和服务端,可以快速进行嵌入式设备业务组网开发(此外,还有一些适合于stm32的protobuf库,这里的库个人感觉更适合Linux和嵌入式Linux)。

二、protobuf、rpc、protobuf-c、protobuf-c-rpc介绍

1、protobuf

Protocol Buffers(简称:ProtoBuf)是一种开源跨平台的序列化数据结构的协议。其对于存储资料或在网络上进行通信的程序是很有用的。这个方法包含一个接口描述语言,描述一些数据结构,并提供程序工具根据这些描述产生代码,这些代码将用来生成或解析代表这些数据结构的字节流。 起初接触protobuf是在go语言上,当时go的grpc框架对我影响很大,分布式场景的快速组网开发使得设备之间的交互扩展性极强,增加新功能只需要简单修改protobuf交互协议文档,新的服务就可以快速生成并开发,高低版本的交互协议也很容易兼容,其相较于xml和json使用简单,更接近于代码层面的数据类型以及高效的性能和兼容性,在分布式场景下应用越来越广。

2、protobuf-c

这是Google Protocol Buffers数据序列化格式的 C 实现。它包括一个实现 protobuf 编码和解码的纯 C 库,以及一个基于原始 .protobuf 文件将 Protocol Buffer 文件转换为 C 描述符代码的代码生成器。之前是包含rpc实现的,后面单独拆分出来了,更将强调了 protobuf和rpc的单独性(虽然protobuf和rpc以及grpc一起使用,但protobuf可以像json、xml等序列化协议一样可以单独去使用)。

3、protobuf-c-rpc

用于将protobuf和rpc结合使用的C语言实现库,以此类推,也有将json和rpc结合使用的库等等。

三、Ubuntu18.04下编译安装及交叉编译

1、前置准备-升级cmake

#卸载老版本

sudo apt-get autoremove cmake

#直接下载对应运行包:https://cmake.org/files/

wget https://cmake.org/files/v3.20/cmake-3.20.0-linux-x86_64.tar.gz

#解压

tar zxvf cmake-3.20.0-linux-x86_64.tar.gz

#移动到常用安装目录

mv cmake-3.20.0-linux-x86_64 /opt/cmake-3.20.0

#创建软链接

ln -sf /opt/cmake-3.20.0/bin/cmake /usr/bin/cmake

#查看版本

cmake --version

2、protobuf编译安装及交叉编译

GitHub仓库及下载地址: https://github.com/google/protobuf/releases https://github.com/protocolbuffers/protobuf/archive/refs/tags/v3.6.0.tar.gz

2.1、正常编译、安装及错误解决方法

该过程可以写入简单shell脚本一键执行:

#下载源码

wget https://github.com/protocolbuffers/protobuf/archive/refs/tags/v3.6.0.tar.gz

#解压

tar zxvf v3.6.0.tar.gz

cd protobuf-3.6.0/

#安装autogen、autoconf、libtool、m4

apt-get install autogen

apt-get install autoconf

apt-get install libtool

apt-get install m4

#configure编译并设置安装路径

./autogen.sh

apt-get install g++

./configure --prefix=/usr/local/protobuf

#make编译时间稍微长一些

make

make install

#bin目录可以加入/etc/profile中,之后就可以执行protoc命令将.proto文件生成代码了

export PATH=/usr/local/protobuf/bin/:$PATH

source /etc/profile

#查看版本,安装成功的话可以看到类似:libprotoc 3.6.0

protoc --version

#PKG_CONFIG_PATH最好不要添加,交叉编译时路径需要修改,所以每次编译前执行一下该命令即可,如果不需要交叉编译则可以添加,避免找不到库

export PKG_CONFIG_PATH=/usr/local/protobuf/lib/pkgconfig/

错误1:

# ./autogen.sh

+ autoreconf -f -i -Wall,no-obsolete

./autogen.sh: 32: ./autogen.sh: autoreconf: not found

解决:安装autogen、autoconf

错误2:

# ./autogen.sh

+ autoreconf -f -i -Wall,no-obsolete

configure.ac:104: error: possibly undefined macro: AC_PROG_LIBTOOL

If this token and others are legitimate, please use m4_pattern_allow.

See the Autoconf documentation.

autoreconf: /usr/bin/autoconf failed with exit status: 1

解决:安装libtool、m4

错误3:

configure: error: in `/home/plc/protobuf-3.6.0':

configure: error: C++ preprocessor "/lib/cpp" fails sanity check

See `config.log' for more details

解决:安装g++

2.2、交叉编译arm、aarch64版本及错误解决方法

首先需要确认你自己的交叉编译工具链,这主要取决于你的开发板,这里以linaro的gnu工具链为例。 下载地址:https://www.linaro.org/downloads/#gnu_and_llvm https://snapshots.linaro.org/gnu-toolchain/ 这里以目前最新的的14版本为例下载x86_64的arm和aarch的arm交叉编译工具链(这取决于你的PC机的芯片)。 这里注意不要下载版本太高的交叉编译工具链,否则其依赖的libc版本太高的话会导致无法使用。

#wget直接下载,会比较慢,建议使用其它方法下载好传到虚拟机上

wget https://snapshots.linaro.org/gnu-toolchain/11.3-2022.06-1/aarch64-linux-gnu/gcc-linaro-11.3.1-2022.06-x86_64_aarch64-linux-gnu.tar.xz

wget https://snapshots.linaro.org/gnu-toolchain/11.3-2022.06-1/arm-linux-gnueabihf/gcc-linaro-11.3.1-2022.06-x86_64_arm-linux-gnueabihf.tar.xz

#解压

tar -xvf gcc-linaro-11.3.1-2022.06-x86_64_arm-linux-gnueabihf.tar.xz

tar -xvf gcc-linaro-11.3.1-2022.06-x86_64_aarch64-linux-gnu.tar.xz

#移动到常用位置

mv gcc-linaro-11.3.1-2022.06-x86_64_arm-linux-gnueabihf/ /usr/local/

mv gcc-linaro-11.3.1-2022.06-x86_64_aarch64-linux-gnu/ /usr/local/

#进行配置、编译、安装

CC=/usr/local/gcc-linaro-11.3.1-2022.06-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc CXX=/usr/local/gcc-linaro-11.3.1-2022.06-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-g++ ./configure --prefix=/usr/local/protobuf/arm --host=arm-linux

make clean && make && make install

#aarch版本

CC=/usr/local/gcc-linaro-11.3.1-2022.06-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc CXX=/usr/local/gcc-linaro-11.3.1-2022.06-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-g++ ./configure --prefix=/usr/local/protobuf/aarch64 --host=aarch64-linux

make clean && make && make install

错误1:

#configure报错

checking for arm-linux-gcc... /usr/local/gcc-linaro-14.0.0-2023.06-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc

checking whether the C compiler works... no

#然后使用/usr/local/gcc-linaro-14.0.0-2023.06-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc发现libc版本不够

/lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.34' not found (required by /usr/local/gcc-linaro-14.0.0-2023.06-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc

strings /lib/x86_64-linux-gnu/libc.so.6 |grep GLIBC_

GLIBC_2.2.5

GLIBC_2.2.6

GLIBC_2.3

GLIBC_2.3.2

GLIBC_2.3.3

GLIBC_2.3.4

GLIBC_2.4

GLIBC_2.5

GLIBC_2.6

GLIBC_2.7

GLIBC_2.8

GLIBC_2.9

GLIBC_2.10

GLIBC_2.11

GLIBC_2.12

GLIBC_2.13

GLIBC_2.14

GLIBC_2.15

GLIBC_2.16

GLIBC_2.17

GLIBC_2.18

GLIBC_2.22

GLIBC_2.23

GLIBC_2.24

GLIBC_2.25

GLIBC_2.26

GLIBC_2.27

GLIBC_PRIVATE

解决方法:下载低版本的交叉编译工具链或者升级glibc,升级glibc会导致一系列系统问题,不太建议。

3、protobuf-c编译安装及交叉编译

https://github.com/protobuf-c/protobuf-c https://github.com/protobuf-c/protobuf-c/releases/download/v1.4.1/protobuf-c-1.4.1.tar.gz 目前安装的1.4.1,protobuf-c和protobuf-c-rpc已经分离成两个项目了。 这里注意正常编译时不需要配置disable-protoc,用于生成bin、lib等内容,而交叉编译时则可以不需要

3.1、正常编译安装

wget https://github.com/protobuf-c/protobuf-c/releases/download/v1.4.1/protobuf-c-1.4.1.tar.gz

tar zxvf protobuf-c-1.4.1.tar.gz

cd protobuf-c-1.4.1/

apt-get install pkg-config

#注意设置PKG_CONFIG_PATH,否则会找不到protobuf的头文件等

export PKG_CONFIG_PATH=/usr/local/protobuf/lib/pkgconfig/

./configure --prefix=/usr/local/protobuf-c CFLAGS="-fPIC"

make clean && make && make install

export PATH=/usr/local/protobuf-c/bin/:$PATH

错误1:

checking pkg-config is at least version 0.9.0... ./configure: line 12928: /usr/local/protobuf/lib/pkgconfig/: Is a directory

no

configure: error: pkg-config is required!

解决方法:安装pkg-config 如果确认你安装了pkg-config但还是一直报类似错误:

configure: error: in `/home/plc/protobuf-c-1.4.1':

configure: error: The pkg-config script could not be found or is too old. Make sure it

is in your PATH or set the PKG_CONFIG environment variable to the full

path to pkg-config.

Alternatively, you may set the environment variables protobuf_CFLAGS

and protobuf_LIBS to avoid the need to call pkg-config.

See the pkg-config man page for more details.

To get pkg-config, see .

See `config.log' for more details

那么可能是环境变量污染导致的,export看一下PKG_CONFIG是不是被设置了,如果是应该就是这个问题,reboot重启一下设备就好了。

3.2、交叉编译arm、aarch64版本

#交叉编译时使用的bin下的程序还是Linux正常编译安装的,只改变PKG_CONFIG_PATH链接对应的库和头文件

export PKG_CONFIG_PATH=/usr/local/protobuf/arm/lib/pkgconfig/

#增加--disable-protoc,只生成库和头文件

CC=/usr/local/gcc-linaro-11.3.1-2022.06-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc ./configure --prefix=/usr/local/protobuf-c/arm --host=arm-linux --disable-protoc CFLAGS="-fPIC"

make clean && make && make install

#aarch64版本

CC=/usr/local/gcc-linaro-11.3.1-2022.06-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc ./configure --prefix=/usr/local/protobuf-c/aarch64 --host=aarch64-linux --disable-protoc CFLAGS="-fPIC"

make clean && make && make install

错误1:

/bin/bash: /usr/local/protobuf/arm/bin/protoc: cannot execute binary file: Exec format error

Makefile:2707: recipe for target 'protobuf-c/protobuf-c.pb.cc' failed

make: *** [protobuf-c/protobuf-c.pb.cc] Error 126

protoc该程序只是用于编译proto文件,可以不需要交叉编译的protoc程序,当然如果交叉编译环境要使用该程序,则修改环境变量让其使用上面我们交叉编译protobuf时生成的protoc即可,我这里直接disable方便一些。

4、protobuf-c-rpc编译安装

https://github.com/protobuf-c/protobuf-c-rpc

4.1、正常编译安装

git clone https://github.com/protobuf-c/protobuf-c-rpc.git

cd protobuf-c-rpc/

export PKG_CONFIG_PATH=/usr/local/protobuf-c/lib/pkgconfig/

./autogen.sh

./configure --prefix=/usr/local/protobuf-c-rpc CFLAGS="-fPIC"

export C_INCLUDE_PATH=/usr/local/protobuf-c/include/

make clean && make && make install

错误1:

In file included from protobuf-c-rpc/protobuf-c-rpc-data-buffer.c:37:0:

protobuf-c-rpc/protobuf-c-rpc.h:32:10: fatal error: protobuf-c/protobuf-c.h: No such file or directory

#include

^~~~~~~~~~~~~~~~~~~~~~~~~

compilation terminated.

Makefile:853: recipe for target 'protobuf-c-rpc/protobuf-c-rpc-data-buffer.lo' failed

解决方法:设置环境变量C_INCLUDE_PATH指定头文件位置即可(export C_INCLUDE_PATH=/usr/local/protobuf-c/include/),交叉编译时也是如此。

4.2、交叉编译arm、aarch64版本

#交叉编译时使用的bin下的程序还是Linux正常编译安装的,只改变PKG_CONFIG_PATH链接对应的库和头文件

export PKG_CONFIG_PATH=/usr/local/protobuf-c/arm/lib/pkgconfig/

CC=/usr/local/gcc-linaro-11.3.1-2022.06-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc ./configure --prefix=/usr/local/protobuf-c-rpc/arm --host=arm-linux CFLAGS="-fPIC"

export C_INCLUDE_PATH=/usr/local/protobuf-c/arm/include

make clean && make && make install

#aarch64版本

export PKG_CONFIG_PATH=/usr/local/protobuf-c/arm/lib/pkgconfig/

CC=/usr/local/gcc-linaro-11.3.1-2022.06-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc ./configure --prefix=/usr/local/protobuf-c-rpc/aarch64 --host=aarch64-linux CFLAGS="-fPIC"

export C_INCLUDE_PATH=/usr/local/protobuf-c/aarch64/include/

make clean && make && make install

四、接口调用示例及演示

protobuf-c-rpc示例主要参考这里:https://github.com/protobuf-c/protobuf-c-rpc/blob/master/t/test-rpc.c

域socket方式指定socket文件的路径即可,比如“./socket”;tcp方式服务端指定端口即可,传递的name是字符串类型的端口如“8888”;而客户端需要指定ip和端口如“127.0.0.1:8888”;

这里以获取一个版本号为例写一个简单的rpc客户端服务端示例,客户端去调用rpc接口获取版本信息:

1、创建proto文件test.proto

syntax = "proto3";

service Test {

rpc GetVersion(VersionReq) returns (VersionRes) {}

}

message VersionReq {

string dev = 1; //请求版本的设备标识

}

message VersionRes {

int32 err_code = 1; //错误码,为0是正常,非0错误

string message = 2; //回复错误信息

string version = 3; //版本信息

}

2、使用protoc-c编译proto文件生成服务和客户端代码文件

/usr/local/protobuf-c/bin/protoc-c --c_out=. test.proto

之后会生成test.pb-c.c和test.pb-c.h,每次更新.proto文件重新生成一下即可。

3、完成rpc客户端和服务端程序

//server.c

#include

#include

#include

#include

#include

#include

#include "protobuf-c/protobuf-c.h"

#include "protobuf-c/protobuf-c-rpc.h"

#include "test.pb-c.h"

static int g_run_flag = 1;

void signal_handler(int signum) {

printf("Interrupt signal:%d received.\n", signum);

g_run_flag = 0;

exit(signum);

}

void this__get_version(Test_Service *service,

const VersionReq *input,

VersionRes_Closure closure,

void *closure_data) {

VersionRes result = VERSION_RES__INIT;

printf("ser get version\n");

(void) service;

if (input->dev == NULL || !strlen(input->dev))

{

result.err_code = -1;

result.message = "dev info is null";

closure(&result, closure_data);

return;

}

printf("req dev info:%s\n", input->dev);

result.err_code = 0;

result.version = "1";

result.message = "success";

closure (&result, closure_data);

}

static Test_Service test_service = TEST__INIT(this__);

void *pthread_rpc_server(void *arg) {

ProtobufC_RPC_Server *rpc_server;

ProtobufCService *local_service = (ProtobufCService *)&test_service;

//域socket方式

// protobuf_c_rpc_server_new(PROTOBUF_C_RPC_ADDRESS_LOCAL, "test.socket",

// local_service, NULL);

//tcp方式

protobuf_c_rpc_server_new(PROTOBUF_C_RPC_ADDRESS_TCP, "12345",

local_service, NULL);

while (g_run_flag) {

protobuf_c_rpc_dispatch_run(protobuf_c_rpc_dispatch_default());

sleep(1);

}

return NULL;

}

int main(int argc, char *argv[]) {

pthread_t pid_rpc_server;

signal(SIGINT, signal_handler);

printf("fun rpc run.\n");

pthread_create(&pid_rpc_server, NULL, (void *(*)(void *)) &pthread_rpc_server, NULL);

while (g_run_flag) {

}

}

//client.c

#include

#include

#include

#include

#include

#include

#include "protobuf-c/protobuf-c-rpc.h"

#include "test.pb-c.h"

static int g_run_flag = 1;

void signal_handler(int signum) {

printf("Interrupt signal:%d received.\n", signum);

g_run_flag = 0;

exit(signum);

}

static void

handle_query_response(const VersionRes *result,

void *closure_data) {

if (result == NULL)

printf("Error processing request.\n");

else if (result->message == NULL)

printf("Not found.\n");

else {

printf("resp message:%s\n", result->message);

printf("resp ver:%s\n", result->version);

}

*(protobuf_c_boolean *) closure_data = 1;

}

static void

do_nothing (ProtobufCRPCDispatch *dispatch, void *unused)

{

}

static void

run_main_loop_without_blocking (ProtobufCRPCDispatch *dispatch)

{

protobuf_c_rpc_dispatch_add_idle (dispatch, do_nothing, NULL);

protobuf_c_rpc_dispatch_run (dispatch);

}

int main(int argc, char *argv[]) {

ProtobufCService *service;

ProtobufC_RPC_Client *client;

VersionRes resp = VERSION_RES__INIT;

signal(SIGINT, signal_handler);

//域socket方式

// service = protobuf_c_rpc_client_new(PROTOBUF_C_RPC_ADDRESS_LOCAL, "test.socket", &test__descriptor, NULL);

//tcp方式

service = protobuf_c_rpc_client_new(PROTOBUF_C_RPC_ADDRESS_TCP, "127.0.0.1:12345", &test__descriptor, NULL);

if (service == NULL) {

printf("error creating client\n");

exit(-1);

}

client = (ProtobufC_RPC_Client *) service;

//设置自动重连时间

protobuf_c_rpc_client_set_autoreconnect_period(client, 3000);

while (!protobuf_c_rpc_client_is_connected(client))

protobuf_c_rpc_dispatch_run(protobuf_c_rpc_dispatch_default());

fprintf(stderr, "done.\n");

while (g_run_flag) {

protobuf_c_boolean is_done = 0;

VersionReq req = VERSION_REQ__INIT;

req.dev = "12345678";

run_main_loop_without_blocking (protobuf_c_rpc_dispatch_default ());

test__get_version(service, &req, handle_query_response, &is_done);

while (!is_done)

protobuf_c_rpc_dispatch_run (protobuf_c_rpc_dispatch_default ());

printf("req get version:\n");

sleep(1);

}

}

4、编译并测试

gcc server.c test.pb-c.c -o server -L /usr/local/protobuf-c/lib -L /usr/local/prtobuf-c-rpc/lib -lprotobuf-c-rpc -lprotobuf-c -lpthread

gcc client.c test.pb-c.c -o client -L /usr/local/protobuf-c/lib -L /usr/local/protobuf-c-rpc/lib -lprotobuf-c-rpc -lprotobuf-c -lpthread

# ./server

fun rpc run.

ser get version

req dev info:12345678

ser get version

req dev info:12345678

ser get version

req dev info:12345678

ser get version

req dev info:12345678

ser get version

req dev info:12345678

ser get version

req dev info:12345678

...

# ./client

done.

resp message:success

resp ver:1

req get version:

resp message:success

resp ver:1

req get version:

resp message:success

resp ver:1

req get version:

resp message:success

resp ver:1

req get version:

resp message:success

resp ver:1

req get version:

^CInterrupt signal:2 received.

5、关键点(重要)

TCP方式时服务端protobuf_c_rpc_server_new的name只需要设置端口,不需要绑定ip,但是客户端这里的name为ip:port形式;域socket方式时需要创建为name的文件用来做域socket通信;客户端要调用的接口方法是固定的,但是服务端需要重写函数指针对应的函数并且函数名是初始化service时传入的前缀名称加上方法名称:

//比如这里初始化时设置前缀为this__,那么这里重写的回调函数名称就是:this_get_version,这个函数指针被注册到服务之中了,等到客户端请求时就会调用该注册的函数

static Test_Service test_service = TEST__INIT(this__);

void this__get_version(Test_Service *service,

const VersionReq *input,

VersionRes_Closure closure,

void *closure_data) {

...

}

五、最后

protobuf这种序列化方式的数据交互方式虽然相较json等上手门槛稍微高一些,但是相较于json、xml还是有不少优势的,比较推荐。

文章来源

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。