同步操作将从 极简美/ERPC-doc 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
整个ERPC框架由一个环境变量(ERPC_PROFILE_PATH)、两个配置文件(部署配置文件、日志配置文件)和11个函数接口构成,其中11个函数接口分别分拆到3个不同的头文件中。
本文核心讲解环境变量和11个函数接口,有关配置文件,详见第4章:配置文件。
整个ERPC的文件构成结构如下所示(这里只展示核心文件):
framework/
├── bin
│ └── zlog-chk-conf
├── include
│ ├── efsm_conf.h
│ ├── efsm.h
│ ├── efsmt.h
│ ├── erpc.h
│ ├── mt_timer.h
│ ├── rpc_core.h
│ ├── rpc_logger.h
│ ├── rpc_observer.h
│ ├── rpc_service.h
│ ├── rpc_util.h
└── lib
└── librpc.so
如上文件为ERPC相关的核心文件。在framework目录中,还有很多其他的头文件和库,为ERPC实现的基石,或者扩展功能,可在外围功能的文档中进行学习。
其中多远定时器(mult_timer)和事件驱动型(EFSM)为通用功能,可独立于ERPC框架运行。
为了便于使用,用户可只包含erpc.h一个头文件即可使用核心框架的所有功能,该头文件将ERPC的核心头文件全部包含在一起:
#ifndef __ERPC_H__
#define __ERPC_H__
#ifdef __cplusplus
extern "C" {
#endif
#include "rpc_core.h"
#include "rpc_service.h"
#include "rpc_observer.h"
#include "rpc_util.h"
#include "rpc_logger.h"
#ifdef __cplusplus
}
#endif
#endif // __ERPC_H__
框架的正确使用流程是:
要想正确的使用ERPC,必须在系统中通过export设置ERPC_PROFILE_PATH环境变量,该环境变量指向ERPC的配置文件,比如:
export ERPC_PROFILE_PATH=/etc/erpc.json
若系统没有配置该环境变量,则ERPC框架会寻找默认路径:"/etc/erpc.json",当寻找配置文件失败时,会提示如下错误:
[INFO] [./business/rpc_core.c][erpc_framework_init():146]:RPC : Environment ERPC_PROFILE_PATH has not set in system!
[INFO] [./business/rpc_core.c][erpc_framework_init():147]:RPC : ERPC will load default profile: /etc/erpc.json
[ERROR] [./configuration/rpc_profile_json.c][rpc_profile_parse():167]:RPCConfig : open /etc/erpc.json config-file failed!
有关配置文件的详细介绍,详见:配置文件。
与框架控制相关的只有三个接口:框架初始化、框架主循环和框架退出,它们定义在rpc_core.h头文件中。
框架初始化必须在其它所有框架类接口之前调用,函数原型如下:
extern int erpc_framework_init(char *process);
参数process为运行ERPC框架的进程名称:若传入进程名,则框架将以传入的名称为准;若process为NULL,则ERPC将会从系统自动识别当前运行进程的名称(从PID获取名称)。
框架初始化成功返回0,失败返回-1,并输出对应的错误信息。可能出错的情况有:
框架初始化函数,只需要在整个程序开始处调用一次,多次调用无效并将获得如下信息:
RPC : xxxx have init RPC framework before!
其中xxxx为当前进程名。
当注册服务、创建观察者、注册观察者业务都完成之后,且业务线程也已经启动完成,最后主线程调用框架主循环进入监控状态:
typedef enum {
ERPC_LOOP_EXIT,
ERPC_LOOP_DEFAULT,
ERPC_LOOP_ONCE,
ERPC_LOOP_NOWAIT
}erpc_loop_t;
extern int erpc_framework_loop(erpc_loop_t way);
参数way为进入监控状态的方式,可选的值为:
该函数执行成功不返回,出错返回-1,并打印对应的错误信息。可能出错的情况有:
当业务运行出错,或者应业务需求,需要退出框架结束运行时,可以调用下面函数:
extern void erpc_framework_break(void);
该函数将结束框架主循环,并释放所有与框架相关的资源。
与远程调用相关的有3个接口:注册服务、注销服务和远程调用,它们定义在rpc_service.h头文件中。
当系统启动初始化框架完成后,可调用该接口向ERPC系统注册服务:
typedef cJSON *(*erpc_service_callback_t)(cJSON *params);
extern int erpc_service_register(const char *module, const char *service, erpc_service_callback_t pointer);
该函数执行成功返回0,出错返回-1。共有三个参数:
typedef cJSON *(*erpc_service_callback_t)(cJSON *params);
服务函数接收一个cJSON对象参数,返回值也是一个cJSON的对象。也即:服务的具体参数由服务编写者决定,当服务执行完毕后,返回的数据类型也由服务编写者决定;且整个服务的编写过程与在本地实现一个功能函数类似:处理参数,返回结果!
当不再提供某项服务的时候,可以调用下面的接口从ERPC系统中注销该服务:
extern int erpc_service_unregister(const char *module, const char *service);
注销后的服务,再执行远程调用将会失败。
对于嵌入式设备来讲,在系统启动的时候,启动各个后台进程,并注册服务,直到设备关机,一般不会存在注销某个服务的情况。
当应用者需要使用某个服务时,可以使用远程调用接口调用远端的服务:
extern int erpc_service_proxy_call(const char *module, const char *service, cJSON *send, cJSON **recv, struct timeval *tv);
该接口调用成功返回0,失败返回-1,可能出错的情况有:
其参数详细说明如下:
特殊说明:若服务无数据返回,则返回是NULL,对应recv参数也为NULL,但该接口正确收到服务的返回则照样返回0。
对于服务接口的使用,建议编写服务的人员不仅实现服务的开发,并且还要完成接口的开发。这与面向对象开发语言不谋而合:开发过程分为实体实现和接口实现。因为:
与观察者相关的有5个接口:创建观察者对象、销毁观察者对象、注册观察者、注销观察者和消息广播,它们定义在rpc_observer.h头文件中。
在服务侧,若有状态供其他模块观察,则需要在服务侧创建观察者对象,调用下面接口:
extern int erpc_observed_create(const char *module, const char *observed);
该函数创建成功返回0;失败返回-1,并打印错误信息。
参数module是观察者所属模块名,observed是观察者对象的名称。
当服务侧不再需要观察者对象时,可以调用下面接口销毁一个观察者对象,销毁的同时会清空以注册的观察者:
extern int erpc_observed_destroy(const char *module, const char *observed);
参数module是观察者所属模块名,observed是观察者对象的名称。
对于应用者,若关注某个模块的状态,且该模块已经创建了观察者对象,可以通过下面接口注册观察者:
extern int erpc_observer_register(const char *module, const char *observed, erpc_observer_callback_t action, struct timeval *tv);
该函数执行成功返回0,出错返回-1,并打印出错信息。可能出错的情况有:
其参数详细说明如下:
typedef void (*erpc_observer_callback_t)(cJSON *params);
与远程调用的服务相比,观察者回调是不需要返回值的。
当我们不再关注模块的状态时,可以使用下面函数注销对该观察者对象的观察者:
extern int erpc_observer_unregister(const char *module, const char *observed, struct timeval *tv);
该函数执行成功返回0,出错返回-1,并打印出错信息。可能出错的情况有:
其参数详细说明如下:
在服务侧,当观察者对象的状态发生改变,则可通过下面接口广播状态:
extern int erpc_observer_invoke(const char *module, const char *observed, cJSON *params);
该函数执行成功返回0,失败返回-1,并打印出错信息。
参数module是观察者对象所述模块,observed是观察者对象的名称,params是状态数据,也可为NULL,对应的观察者回调也会接收到NULL参数。
注意:观察者对象的状态广播时,所有消息的ID均为同一个。
除了模块或硬件的状态变更时可以使用观察者接口来实现外,对于数据准备需要比较长的时间时也可以使用观察者接口和服务来配合实现:
从交互过程就知道,第一种数据是采用广播方式,不能保证正确性,第二种方法虽然繁琐一点,但是数据是有保障的。具体根据应用场景而定。
除了ERPC核心框架的11个接口外,我们还提供了与框架相关的其他功能函数,比如获取版本信息、框架运行状态,设置周期任务,设置数据加密算法等等。
通过下面接口可以获取当前ERPC框架的版本信息:
extern char *erpc_version(void);
该函数返回版本字符串,比如:"ERPC-V1.6"
当我们的应用运行状态依赖于ERPC框架的运行状态时,可以使用下面接口获取:
extern int erpc_is_running(void);
函数返回0代表框架正在运行,返回-1代表框架还未开始运行,或已经退出。
有时我们的cJSON参数或对象需要放在多个地方处理,为了防止多次cJSON_Delete()而导致内存异常,可以使用下面函数复制新的一份相同的cJSON对象:
extern int erpc_duplicate_params(cJSON *params, cJSON **object);
复制成功返回0,复制失败返回-1。若复制成功,后续params和*object对象都需要分别进行cJSON_Delete(),否则会导致内存泄漏。
ERPC框架提供了一个内部周期任务,周期最小单位为S,通过下面两个接口,你可以自定义周期时间和周期任务:
extern void erpc_timer_period_set(struct timeval tv);
extern void erpc_timer_handler_set(void (*handler)(void));
这两个函数始终都会成功。erpc_timer_period_set()设置周期时间,erpc_timer_handler_set()设置周期回调函数,回调函数是不带参数、无返回值的函数。
当我们对数据安全性有要求时,可以为通信的数据增加加密/解密算法:
typedef void (*rpc_message_handler_t)(int sockfd, char *data, size_t len);
typedef int (*rpc_protocol_encrypt_t)(char *data, size_t len, int sockfd, rpc_message_handler_t handler);
typedef int (*rpc_protocol_decrypt_t)(char *data, size_t len, int sockfd, rpc_message_handler_t handler);
extern void erpc_information_security(rpc_protocol_encrypt_t encrypt, rpc_protocol_decrypt_t decrypt);
该函数始终会执行成功,用于设置加密函数回调encrypt和解密函数decrypt,两个函数必须成对注册,否则会导致收发双方信息不对称。
加密/解密回调函数类型是一样的:
typedef int (*rpc_protocol_encrypt_t)(char *data, size_t len, int sockfd, rpc_message_handler_t handler);
typedef int (*rpc_protocol_decrypt_t)(char *data, size_t len, int sockfd, rpc_message_handler_t handler);
它们接收二进制数据和长度,在内部进行数据处理(加密/解密),并将处理后的送到数据handler指针指向的函数中,其中sockfd为handler的第一个参数:
typedef void (*rpc_message_handler_t)(int sockfd, char *data, size_t len);
也即加密/解密函数的执行流程为(下面为伪代码):
int encrypt_handler(char *data, size_t len, int sockfd, rpc_message_handler_t handler)
{
size_t en_len = 0;
char *en_data = NULL;
/* encrypt data */
en_len = encrypt_function(data, len, en_data);
/* transmit data */
handler(sockfd, en_data, en_len);
/* free data */
free(en_data);
}
int decrypt_handler(char *data, size_t len, int sockfd, rpc_message_handler_t handler)
{
size_t de_len = 0;
char *de_data = NULL;
/* decrypt data */
de_len = decrypt_function(data, len, de_data);
/* transmit data */
handler(sockfd, de_data, de_len);
/* free data */
free(de_data);
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。