在没有使用 RPC
之前,那就需要程序员自己来完成两个进程(芯片)之间的通信。无论是对于发射端还是接收端,其逻辑都类似如下:
发送端根据制定的传输格式传输二进制流
接收端根据接收到的二进制流,以状态机的方式依次分解二进制流的内容
最终将解析出来的命令和数据与对应的处理程序进行关联调用
人为实现这个过程比较繁琐:
发射端和接收端可能会存在大小端的问题,这需要单独处理
如果代码架构不好,那么换一个底层通信协议便会导致又需要重新实现一次这个过程
长期维护起来比较麻烦,尤其是存在多个进程(芯片)之间通信时
发射端和接收端存在同步和异步调用关系,人为实现较为麻烦
而有了 RPC
框架,便可以以函数调用的方式来实现多个进程(芯片)之间的通信,通信细节对于程序员来讲就是透明的。程序员便可以专注于上层业务逻辑,提高开发效率。
相比较于代码量庞大的 grpc ,ERPC
更适合于嵌入式通信的应用场景,我们先来体验一下它。
获取代码并打补丁
安装依赖
EPRC 的工具需要一些依赖库才可以工作,所以需要先安装依赖:
1 sudo apt install -y flex bison libboost-dev libboost-system-dev libboost-filesystem-dev
获取代码
ERPC 主要分为 Master
和 develop
两个分支,在 clone 代码以后,首先需要切换到 Master
分支:
1 2 3 $ git clone https://github.com/EmbeddedRPC/erpc.git $ cd erpc/ $ git checkout master
BUG 修复
虽然这 Master
分支是最新的 1.8.1
版本,但由于其存在重复定义 BUG,所以需要修复该问题。按照这个 Pull
request 进行修复即可。
编译并安装
编译和安装步骤倒是轻车熟路了:
1 2 $ make -j8 $ sudo make install
安装完成后,安装路径位于:
1 2 3 4 Installing headers in /usr/local/include/erpc Installing liberpc.a in /usr/local/lib Installing erpcgen in /usr/local/bin Installing erpcsniffer in /usr/local/bin
编写 demo
定义 IDL 文件
IDL 文件用于定义客户端和服务端通信的接口,下面建立一个简单的文件
erpcdemo.erpc
:
1 2 3 4 5 6 7 8 program erpcdemo interface DEMO { DemoHello (binary val) -> binary }
接下来便是生成对应的文件:
便会生成下面 4 个文件:
1 2 3 4 erpcdemo.h erpcdemo_client.cpp erpcdemo_server.cpp erpcdemo_server.h
对于客户端而言,需要包含erpcdemo.h
,erpcdemo_client.cpp
文件。
对于服务端而言,需要包含erpcdemo_server.cpp
,erpcdemo_server.h
文件。
提供 TCP 通信实例
由于 ERPC 默认编译方式并没有把创建 TCP
实例的代码包含进库中,所以我们需要将
erpc_c/setup/erpc_setup_tcp.cpp
复制到当前测试文件夹下。
实现服务端
服务端用于创建连接并实现接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #include <cstdlib> #include <iostream> #include <string> #include <erpc_server_setup.h> #include <erpc_transport_setup.h> #include "erpcdemo_server.h" static const char * ret = "Hello, this is server!\n" ;binary_t * DemoHello (const binary_t * val) { std::cout << "client message: " << (char *)val->data; int len = std::strlen (ret) + 1 ; char * buf = (char *)std::malloc (len); std::strncpy (buf, ret, len - 1 ); binary_t * message = new binary_t {(uint8_t *)buf, (uint32_t )(len - 1 )}; return message; } int main (int argc, char *argv[]) { auto transport = erpc_transport_tcp_init ("127.0.0.1" , 60901 , true ); erpc_mbf_t message_buffer_factory = erpc_mbf_dynamic_init (); erpc_server_init (transport, message_buffer_factory); erpc_add_service_to_server (create_DEMO_service ()); std::cout << "server is running!\n" ; erpc_server_run (); erpc_transport_tcp_close (); return 0 ; }
实现客户端
客户端就是与服务端连接,并调用接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 #include <iostream> #include <string> #include <erpc_client_setup.h> #include <erpc_port.h> #include <erpc_transport_setup.h> #include "erpcdemo.h" int main (int argc, char *argv[]) { auto transport = erpc_transport_tcp_init ("127.0.0.1" , 60901 , false ); auto message_buffer_factory = erpc_mbf_dynamic_init (); erpc_client_init (transport, message_buffer_factory); auto message = "Hello, this is client!\n" ; binary_t cmd{(uint8_t *)message, (uint32_t )(std::strlen (message))}; binary_t *ret = DemoHello (&cmd); std::cout << "Get message of server: " << ret->data << "\n" ; if (ret->data) { erpc_free (ret->data); } if (ret) { erpc_free (ret); } erpc_transport_tcp_close (); return 0 ; }
编写 Makefile
Makefile 比较简单,如下:
1 2 3 4 5 6 INCLUDE = /usr/local/include /erpc LIBRARY = /usr/local/lib all: g++ test_erpcdemo_client.cpp erpcdemo_client.cpp erpc_setup_tcp.cpp -I${INCLUDE} -L${LIBRARY} -lerpc -lpthread -o client g++ test_erpcdemo_server.cpp erpcdemo_server.cpp erpc_setup_tcp.cpp -I${INCLUDE} -L${LIBRARY} -lerpc -lpthread -o server
运行
服务端输出:
1 2 server is running! client message: Hello, this is client!
客户端输出:
1 Get message of server: Hello, this is server!