本文章轉載自:原博客鏈接
1. invoke的方式實現端對端通信
- #ifndef __UBUS_INVOKE_H__
- #define __UBUS_INVOKE_H__
- #include <json/json.h>
- #include <libubox/blobmsg_json.h>
- struct prog_attr {
- <span style="white-space:pre"> </span>char name[64];
- <span style="white-space:pre"> </span>int chn_id;
- };
- #define PROG_MAX<span style="white-space:pre"> </span>8
- #endif /* __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_CLOEXEC
- fcntl(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;
- }
#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_CLOEXEC
fcntl(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;
- }
#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打印出了接收到的消息。這條命令和執行client程序的作用相同。
2. subscribe/notify的方式實現訂閱
ubus支持以訂閱的方式進行進程間通信,通信方式如下圖:
被訂閱者(server)又稱爲notifier,訂閱者(client)又稱爲subscriber。這樣一來,可以同時有多個client訂閱server的某個服務,當server通過該服務廣播消息時,所有client都會被通知,並執行各自的處理。
主要過程爲:
server進程:
- 連接ubusd。
- 註冊一個object,用於client訂閱。
- server可以向訂閱者廣播消息。
- 等待消息。
client進程:
- 連接ubusd。
- 向server訂閱object,並定義收到server的消息時的處理函數。
- 等待消息。
代碼示例:下面這個例子中,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;
- }
#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;
- }
#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端的消息。
event的方式實現事件通知
event機制從一對一的進程之間通信來講,和invoke機制類似。不過event機制中,發送方不需要知道誰要接收這個消息,實際上就是一個廣播消息。這類似於U盤的熱插拔:當插上或拔出U盤時,內核會廣播一個NETLINK事件,之後內核繼續做自己的事情,而不關心誰會去監聽和處理這個事件。
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_CLOEXEC
- fcntl(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;
- }
#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_CLOEXEC
fcntl(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;
- }
#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" } }
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" } }
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是爲發送消息而設計的,不合適傳輸大量數據。