1. invoke的方式实现端对端通信

最简单的情景就是一个提供服务的server端,一个请求服务的client端,client请求server的服务。
下面的例子中,server注册了一个名为“scan_prog”的对象,该对象中提供一个“scan”方法:
ubus_invoke.h:

#ifndef __UBUS_INVOKE_H__
#define __UBUS_INVOKE_H__
#include <json/json.h>
#include <libubox/blobmsg_json.h>struct prog_attr {char name[64];int chn_id;
};
#define PROG_MAX    8#endif  /* __UBUS_INVOKE_H__ */

invoke_server.c:

#include <libubox/uloop.h>
#include <libubox/ustream.h>
#include <libubox/utils.h>
#include <libubus.h>
#include <json/json.h>
#include <libubox/blobmsg_json.h>
#include "ubus_invoke.h"static struct ubus_context * ctx = NULL;
static struct blob_buf b;
static const char * sock_path;static struct prog_attr uri_list[PROG_MAX] =
{{"program_beijing", 1},{"program_guangzhou", 2},{"program_shanghai", 3},{"", -1},
};enum
{SCAN_CHNID,SCAN_POLICY_MAX,
};static const struct blobmsg_policy scan_policy[SCAN_POLICY_MAX] = {[SCAN_CHNID] = {.name = "chnID", .type = BLOBMSG_TYPE_INT32},
};static int ubus_start_scan(struct ubus_context *ctx, struct ubus_object *obj,struct ubus_request_data *req, const char *method,struct blob_attr *msg)
{int ret = -1;void * json_list = NULL;void * json_uri = NULL;int idx;blob_buf_init(&b, 0);struct blob_attr *tb[SCAN_POLICY_MAX];blobmsg_parse(scan_policy, SCAN_POLICY_MAX, tb, blob_data(msg), blob_len(msg));/*本例子中,如果请求特定的节目号,返回节目名称。如果请求节目号是0,则返回所有节目列表。*/if (tb[SCAN_CHNID] != NULL){int chnid = blobmsg_get_u32(tb[SCAN_CHNID]);if (chnid == 0){json_uri = blobmsg_open_array(&b, "prog_list");for (idx = 0; idx < PROG_MAX; idx++){if ('\0' != uri_list[idx].name[0]){json_list = blobmsg_open_table(&b, NULL);blobmsg_add_string(&b, "name", uri_list[idx].name);blobmsg_add_u32(&b, "channel", uri_list[idx].chn_id);blobmsg_close_table(&b, json_list);}}blobmsg_close_array(&b, json_uri);ret = 0;}else{for (idx = 0; idx < PROG_MAX; idx++){if ('\0' != uri_list[idx].name[0] && uri_list[idx].chn_id == chnid){blobmsg_add_string(&b, "name", uri_list[idx].name);ret = 0;}}}}blobmsg_add_u32(&b, "result", ret);ubus_send_reply(ctx, req, b.head);return 0;
}/* 方法列表 */
static struct ubus_method scan_methods[] =
{UBUS_METHOD("scan", ubus_start_scan, scan_policy),
};/* type目前没有实际用处 */
static struct ubus_object_type scan_obj_type
= UBUS_OBJECT_TYPE("scan_prog", scan_methods);static struct ubus_object scan_obj =
{.name = "scan_prog", /* 对象的名字 */.type = &scan_obj_type,.methods = scan_methods,.n_methods = ARRAY_SIZE(scan_methods),
};static void ubus_add_fd(void)
{ubus_add_uloop(ctx);#ifdef FD_CLOEXECfcntl(ctx->sock.fd, F_SETFD,fcntl(ctx->sock.fd, F_GETFD) | FD_CLOEXEC);
#endif
}static void ubus_reconn_timer(struct uloop_timeout *timeout)
{static struct uloop_timeout retry ={.cb = ubus_reconn_timer,};int t = 2;if (ubus_reconnect(ctx, sock_path) != 0) {uloop_timeout_set(&retry, t * 1000);return;}ubus_add_fd();
}static void ubus_connection_lost(struct ubus_context *ctx)
{ubus_reconn_timer(NULL);
}static int display_ubus_init(const char *path)
{uloop_init();sock_path = path;ctx = ubus_connect(path);if (!ctx){printf("ubus connect failed\n");return -1;}printf("connected as %08x\n", ctx->local_id);ctx->connection_lost = ubus_connection_lost;ubus_add_fd();/* 向ubusd注册对象和方法,供其他ubus客户端调用。 */if (ubus_add_object(ctx, &scan_obj) != 0){printf("ubus add obj failed\n");return -1;}return 0;
}static void display_ubus_done(void)
{if (ctx)ubus_free(ctx);
}int main(int argc, char * argv[])
{char * path = NULL;if (-1 == display_ubus_init(path)){printf("ubus connect failed!\n");return -1;}uloop_run();display_ubus_done();return 0;
}

客户端代码invoke_client.c去调用server端的"scan_prog"对象的"scan"方法:

#include <libubox/uloop.h>
#include <libubox/ustream.h>
#include <libubox/utils.h>
#include <libubus.h>
#include <json/json.h>
#include <libubox/blobmsg_json.h>
#include "ubus_invoke.h"static struct ubus_context * ctx = NULL;
static struct blob_buf b;
static const char * cli_path;enum
{SCAN_CHNID,SCAN_POLICY_MAX,
};static const struct blobmsg_policy scan_policy[SCAN_POLICY_MAX] = {[SCAN_CHNID] = {.name = "chnID", .type = BLOBMSG_TYPE_INT32},
};static int timeout = 30;
static bool simple_output = false;static void scanreq_prog_cb(struct ubus_request *req, int type, struct blob_attr *msg)
{char *str;if (!msg)return;/* 在这里处理返回的消息。本例子只是将返回的消息打印出来。*/str = blobmsg_format_json_indent(msg, true, simple_output ? -1 : 0);printf("%s\n", str);free(str);
}static int client_ubus_call()
{unsigned int id;int ret;blob_buf_init(&b, 0);/* 需要传递的参数 */blobmsg_add_u32(&b, scan_policy[SCAN_CHNID].name, 0);/*向ubusd查询是否存在"scan_prog"这个对象,如果存在,返回其id*/ret = ubus_lookup_id(ctx, "scan_prog", &id);if (ret != UBUS_STATUS_OK) {printf("lookup scan_prog failed\n");return ret;}else {printf("lookup scan_prog successs\n");}/* 调用"scan_prog"对象的"scan"方法 */return ubus_invoke(ctx, id, "scan", b.head, scanreq_prog_cb, NULL, timeout * 1000);
}/*
ubus_invoke()的声明如下:
int ubus_invoke(struct ubus_context *ctx, uint32_t obj, const char *method,struct blob_attr *msg, ubus_data_handler_t cb, void *priv, int timeout);其中cb参数是消息回调函数,其函数类型定义为:
typedef void (*ubus_data_handler_t)(struct ubus_request *req,int type, struct blob_attr *msg);
参数type是请求消息的类型,如UBUS_MSG_INVOKE,UBUS_MSG_DATA等。
参数msg存放从服务端得到的回复消息,struct blob_attr类型,同样用blobmsg_parse()来解析。
参数req保存了请求方的消息属性,其中req->priv即ubus_invoke()中的priv参数,
用这个参数可以零活的传递一些额外的数据。
*/static int client_ubus_init(const char *path)
{uloop_init();cli_path = path;ctx = ubus_connect(path);if (!ctx){printf("ubus connect failed\n");return -1;}printf("connected as %08x\n", ctx->local_id);return 0;
}static void client_ubus_done(void)
{if (ctx)ubus_free(ctx);
}int main(int argc, char * argv[])
{/* ubusd创建的unix socket,默认值为"/var/run/ubus.sock" */char * path = NULL;/* 连接ubusd */if (UBUS_STATUS_OK != client_ubus_init(path)){printf("ubus connect failed!\n");return -1;}/* 调用某个ubus方法并处理返回结果 */client_ubus_call();client_ubus_done();return 0;
}

先执行server程序,再执行client程序,可以看到client发出请求后,server返回了相应的节目号,在client打印出了接收到的消息。
也可以通过shell命令来请求server的服务,例如:
ubus call scan_prog scan '{"chnID": 0}'
这条命令和执行client程序的作用相同。

2. subscribe/notify的方式实现订阅

ubus支持以订阅的方式进行进程间通信,通信方式如下图:

被订阅者(server)又称为notifier,订阅者(client)又称为subscriber。这样一来,可以同时有多个client订阅server的某个服务,当server通过该服务广播消息时,所有client都会被通知,并执行各自的处理。

主要过程为:

server进程:

  1. 连接ubusd。
  2. 注册一个object,用于client订阅。
  3. server可以向订阅者广播消息。
  4. 等待消息。

client进程:

  1. 连接ubusd。
  2. 向server订阅object,并定义收到server的消息时的处理函数。
  3. 等待消息。

代码示例:下面这个例子中,server注册了一个名为“test”的对象,并定期广播消息。而client去订阅这个对象,并对server发出的消息做处理。

notify_server.c:

#include <unistd.h>
#include <libubox/blobmsg_json.h>
#include <libubox/uloop.h>
#include <libubus.h>static struct ubus_context *ctx;static void test_client_subscribe_cb(struct ubus_context *ctx, struct ubus_object *obj)
{fprintf(stderr, "Subscribers active: %d\n", obj->has_subscribers);
}/* 这个空的method列表,只是为了让object有个名字,这样client可以通过object name来订阅。 */
static struct ubus_method test_methods[] = {};static struct ubus_object_type test_obj_type = UBUS_OBJECT_TYPE("test", test_methods);static struct ubus_object test_object = {.name = "test", /* object的名字 */.type = &test_obj_type,.subscribe_cb = test_client_subscribe_cb,
};static void notifier_main(void)
{int ret;/* 注册一个object,client可以订阅这个object */ret = ubus_add_object(ctx, &test_object);if (ret) {fprintf(stderr, "Failed to add object: %s\n", ubus_strerror(ret));return;}/* 在需要的时候,向所有客户端发送notify消息 *//* step1: 如果需要传递参数,则保存到struct blob_attr类型的结构体中。 *//* int ubus_notify(struct ubus_context *ctx, struct ubus_object *obj, const char *type, struct blob_attr *msg, int timeout);type是一个字符串,自定义的。msg是需要携带的参数。如果需要等待回复,timeout需设置为>=0。*/while (1) {sleep(2);/* step2: 广播notification消息。 */ubus_notify(ctx,  &test_object, "say Hi!", NULL, -1);}
}int main(int argc, char **argv)
{const char *ubus_socket = NULL;uloop_init();ctx = ubus_connect(ubus_socket);if (!ctx) {fprintf(stderr, "Failed to connect to ubus\n");return -1;}ubus_add_uloop(ctx);notifier_main();uloop_run();ubus_free(ctx);uloop_done();return 0;
}

notify_client.c:客户端订阅“test”对象,在收到3次消息后,随即取消对“test”对象的订阅。

#include <unistd.h>
#include <libubox/blobmsg_json.h>
#include <libubox/uloop.h>
#include <libubus.h>static struct ubus_context *ctx;static int counter = 0;
static uint32_t obj_id;
static struct ubus_subscriber test_event;static int test_notify(struct ubus_context *ctx, struct ubus_object *obj,struct ubus_request_data *req,const char *method, struct blob_attr *msg)
{printf("notify handler...\n");counter++;if (counter > 3)ubus_unsubscribe(ctx, &test_event, obj_id); /* 取消订阅 */return 0;
}static void test_handle_remove(struct ubus_context *ctx,struct ubus_subscriber *obj, uint32_t id)
{printf("remove handler...\n");
}static void subscriber_main(void)
{int ret;/* 通知到来时的处理函数。 */test_event.cb = test_notify;test_event.remove_cb = test_handle_remove; //server主动发起删除该client的订阅的cb函数(如server退出的时候)/* 注册test_event */ret = ubus_register_subscriber(ctx, &test_event);if (ret)fprintf(stderr, "Failed to add watch handler: %s\n", ubus_strerror(ret));/* 得到要订阅的object的id */ret = ubus_lookup_id(ctx, "test", &obj_id);if (ret)fprintf(stderr, "Failed to lookup object: %s\n", ubus_strerror(ret));/* 订阅object */ret = ubus_subscribe(ctx, &test_event, obj_id);if (ret)fprintf(stderr, "Failed to subscribe: %s\n", ubus_strerror(ret));
}int main(int argc, char **argv)
{const char *ubus_socket = NULL;uloop_init();ctx = ubus_connect(ubus_socket);if (!ctx) {fprintf(stderr, "Failed to connect to ubus\n");return -1;}ubus_add_uloop(ctx);subscriber_main();uloop_run();ubus_free(ctx);uloop_done();return 0;
}

先运行server&,注册可订阅的对象“test”,并随即每2秒向外广播通知消息。这时还没有client订阅这个对象。
运行多个client程序,由于每个client都订阅了“test”对象,则所有client都会收到server发出的消息。当client取消订阅后,则不再收到server端的消息。

3.event的方式实现事件通知

event机制从一对一的进程之间通信来讲,和invoke机制类似。不过event机制中,发送方不需要知道谁要接收这个消息,实际上就是一个广播消息。这类似于U盘的热插拔:当插上或拔出U盘时,内核会广播一个NETLINK事件,之后内核继续做自己的事情,而不关心谁会去监听和处理这个事件。
下面的例子中,client端同时监听了“add_device”和“rm_device”两个事件,而server端会触发“add_device”事件并携带device的一些信息发送出去。

event_client.c:

#include <libubox/uloop.h>
#include <libubox/ustream.h>
#include <libubox/utils.h>
#include <libubus.h>
#include <json/json.h>
#include <libubox/blobmsg_json.h>static struct ubus_context * ctx = NULL;
static const char * cli_path;#define    UBUS_EVENT_ADD_DEVICE   "add_device"
#define UBUS_EVENT_REMOVE_DEVICE    "rm_device"static void ubus_probe_device_event(struct ubus_context *ctx, struct ubus_event_handler *ev,const char *type, struct blob_attr *msg)
{char *str;if (!msg)return;/* 在这里实现收到事件后的动作。event也可以传递消息,放在msg中。本例子只是将返回的消息打印出来。*/str = blobmsg_format_json(msg, true);printf("{ \"%s\": %s }\n", type, str);free(str);
}static int client_ubus_register_events()
{static struct ubus_event_handler listener;int ret = 0;/* 注册特定event的listener。多个event可以使用同一个listener */memset(&listener, 0, sizeof(listener));listener.cb = ubus_probe_device_event;ret = ubus_register_event_handler(ctx, &listener, UBUS_EVENT_ADD_DEVICE);if (ret){fprintf(stderr, "register event failed.\n");return -1;}ret = ubus_register_event_handler(ctx, &listener, UBUS_EVENT_REMOVE_DEVICE);if (ret){ubus_unregister_event_handler(ctx, &listener);fprintf(stderr, "register event failed.\n");return -1;}return 0;
}static void ubus_add_fd(void)
{ubus_add_uloop(ctx);#ifdef FD_CLOEXECfcntl(ctx->sock.fd, F_SETFD,fcntl(ctx->sock.fd, F_GETFD) | FD_CLOEXEC);
#endif
}static void ubus_reconn_timer(struct uloop_timeout *timeout)
{static struct uloop_timeout retry ={.cb = ubus_reconn_timer,};int t = 2;if (ubus_reconnect(ctx, cli_path) != 0) {uloop_timeout_set(&retry, t * 1000);return;}ubus_add_fd();
}static void ubus_connection_lost(struct ubus_context *ctx)
{ubus_reconn_timer(NULL);
}static int client_ubus_init(const char *path)
{uloop_init();cli_path = path;ctx = ubus_connect(path);if (!ctx){printf("ubus connect failed\n");return -1;}printf("connected as %08x\n", ctx->local_id);ctx->connection_lost = ubus_connection_lost;ubus_add_fd();return 0;
}static void client_ubus_done(void)
{if (ctx)ubus_free(ctx);
}int main(int argc, char * argv[])
{char * path = NULL;/* 连接ubusd */if (UBUS_STATUS_OK != client_ubus_init(path)){printf("ubus connect failed!\n");return -1;}/* 注册某个事件的处理函数 */client_ubus_register_events();uloop_run();client_ubus_done();return 0;
}

event_server.c:

#include <libubox/uloop.h>
#include <libubox/ustream.h>
#include <libubox/utils.h>
#include <libubus.h>
#include <json/json.h>
#include <libubox/blobmsg_json.h>static struct ubus_context * ctx = NULL;
static struct blob_buf b;
static const char * sock_path;static int server_ubus_send_event(void)
{blob_buf_init(&b, 0);/* 需要传递的参数 */blobmsg_add_u32(&b, "major", 3);blobmsg_add_u32(&b, "minor", 56);blobmsg_add_string(&b, "name", "mmc01");/* 广播名为"add_device"的事件 */return ubus_send_event(ctx, "add_device", b.head);
}static int display_ubus_init(const char *path)
{uloop_init();sock_path = path;ctx = ubus_connect(path);if (!ctx){printf("ubus connect failed\n");return -1;}printf("connected as %08x\n", ctx->local_id);return 0;
}static void display_ubus_done(void)
{if (ctx)ubus_free(ctx);
}int main(int argc, char * argv[])
{char * path = NULL;if (-1 == display_ubus_init(path)){printf("ubus connect failed!\n");return -1;}server_ubus_send_event();display_ubus_done();return 0;
}

先运行client &注册事件。我们同时启动两个client程序。
再执行server主动触发"add_device"事件,则凡是注册了这个事件的client都会收到该事件并执行各自的处理。

root@NVR:~# ./server
connected as fdecbdc1
{ "add_device": { "major": 3, "minor": 56, "name": "mmc01" } }
{ "add_device": { "major": 3, "minor": 56, "name": "mmc01" } }

也可以通过命令行的ubus send命令触发事件:

root@NVR:~# ubus send "rm_device" '{ "major": 3, "minor": 56, "name": "mmc01" }'
{ "rm_device": { "major": 3, "minor": 56, "name": "mmc01" } }
{ "rm_device": { "major": 3, "minor": 56, "name": "mmc01" } }

在使用ubus时,可根据实际的场景来选择用哪种方式进行进程间通信。如之前所说,ubus是为发送消息而设计的,不合适传输大量数据。
————————————————
版权声明:本文为CSDN博主「落尘纷扰」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/jasonchen_gbd/article/details/46055885

[openwrt] ubus实现进程间通信举例相关推荐

  1. [openwrt] 使用ubus实现

    [openwrt] 使用ubus实现 ubus为openwrt平台开发中的进程间通信提供了一个通用的框架.它让进程间通信的实现变得非常简单,并且ubus具有很强的可移植性,可以很方便的移植到其他lin ...

  2. openwrt之使能WPA3加密方式

    openwrt 19.07主要版本带来了对WPA3的初始支持. 但是,默认情况下未启用WPA3 透过ubus查看支持的模式 root@OpenWrt:/# ubus call luci getFeat ...

  3. openwrt web升级功能介绍

    本文简述了openwrt sdk的升级功能流程,从页面传入升级文件到升级文件检测,再到调用升级脚本进行升级,升级完成后,进行系统重启.最后简述了如何添加升级文件标识,标识包括自己定义的字段,以及升级软 ...

  4. openwrt 网页sysupgrade刷固件流程

    一次偶然的机会,阅读了openwrt网页升级的实现细节,以实际操作流程,结合网络资料,整理了这篇流程. 本文按照网页升级固件时涉及到的各个模块的先后顺序进行介绍, openwrt 固件的升级功能流程, ...

  5. 【路由器】OpenWrt 配置使用

    文章目录 Web 界面 汉化 root 密码 ssh 升级 LuCI 美化 锐捷认证 MentoHUST MiniEAP 防火墙 开放端口 端口转发 IPv6 USB 安装 USB 驱动 自动挂载 E ...

  6. 网络编程+Python

    一.网络编程(模块:socket,from socket import *): 1. 网络层的IP地址可以唯一标识网络中的主机,传输层的"协议+端口"则可以唯一标识主机中应用程序( ...

  7. 微信公众平台开发(110) 微信连Wi-Fi

    微信连Wi-Fi是为商家的线下场所提供一套完整和便捷的微信连Wi-Fi的方案.商家接入微信连Wi-Fi后,顾客无需输入繁琐的Wi-Fi密码,通过微信扫二维码等方式即可快速上网.微信连Wi-Fi还帮助商 ...

  8. LinkIt Smart 7688 问题汇总

    一点一滴分析LinkIt Smart 7688 问题汇总 2016年03月19日 12:39:06 阅读数:24737 定义 $ - 指定Ubuntu系统下命令 # - Openwrt下命令 1. 系 ...

  9. 一点一滴分析LinkIt Smart 7688 问题汇总

    目录(?)[+] 定义 $ - 指定Ubuntu系统下命令 # - Openwrt下命令 1. 系统编译 1.1 .config文件 openwrt中,make menuconfig生成.config ...

  10. 2.凤凰架构:构建可靠的大型分布式系统 --- 访问远程服务

    第2章 访问远程服务远程服务将计算机的工作范围从单机扩展至网络,从本地延伸至远程,是构建分布式系统的首要基础.2.1 远程服务调用2.1.1 进程间通信举例,一个正常的本地调用需要完成以下几个工作:1 ...

最新文章

  1. 中国科学家Cell重要评述文章: 宏基因组学成为病毒分类新方法
  2. 在Android中使用OpenGL ES开发第(五)节:GLSL基础语法
  3. sqlservcer行列互转
  4. 练习div出现的小问题
  5. 【Spring】1、Spring 中的监听器 Listener
  6. FISCO BCOS 确定性多合约并行(DMC)
  7. Docker 取代 VM !是什么让 Docker 比 VM 或裸机更安全?
  8. Linux故障之内核反向路由检测
  9. 数据库SQL Server 如何将数据库表名等前缀转换成dbo
  10. vue单页应用首屏加载速度慢如何解决
  11. java 四舍五入保留小数点后两位
  12. 为何谷歌围棋AI AlphaGo可能会把李世石击溃
  13. case when then else end用法
  14. 2022年全球市场汽车租赁行业发展前景分析及市场需求调研报告
  15. (转)利用python、tensorflow、opencv实现人脸识别(包会)!
  16. 会声会影X5(专业视频编辑软件) v15.0 官方简体中文版
  17. Django的form组件提交时错误不显示或无法提交
  18. 一个程序员对另一个程序员的忠告
  19. php 下载doc文档
  20. Anroid手机apk安装

热门文章

  1. hmm 流程图_算法:HMM模型+维特比算法详解
  2. R语言:SVD分解求解线性方程组AX=b
  3. 【科创人独家】云风:从创业到招安,自由的游戏玩家+务实的程序员
  4. MapReduce----电信数据清洗
  5. 网格计算, 云计算, 集群计算, 分布式计算, 超级计算
  6. U-GAT-IT 论文翻译
  7. 谈谈百度竞价的一些思路
  8. Dubbo Monitor 分析
  9. GlassFish安装
  10. Watching the English:英国社会阶层攀爬指南?