1,引入
本文通过Ubus提供的接口,实现了服务端发送通知,客户端订阅并接收通知。
2,服务端
int get_sub_ser_event(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct blob_buf b = {};
blob_buf_init(&b, 0);
blobmsg_add_string(&b, "message", "No need to get 'event'");
ubus_send_reply(ctx, req, b.head);
blob_buf_free(&b);
return 0;
}
static const struct ubus_method sub_methods[] = {
//UBUS_METHOD("event", get_sub_event, notify_policy), // 通知事件
UBUS_METHOD_NOARG("event", get_sub_ser_event), // 通知事件
};
static struct ubus_object_type sub_obj_type =
UBUS_OBJECT_TYPE("sub.service", sub_methods);
int sub_service()
{
int counter = 0;
int ret = 0;
int id = 0;
struct ubus_context *ser_ctx = NULL;
struct ubus_object sub_object;
uloop_init();
ser_ctx = ubus_connect(NULL);
if (!ser_ctx) {
printf("ser ubus_connect failed.");
return ERROR;
}
// 添加 ser_ctx 到 uloop
ubus_add_uloop(ser_ctx);
memset( &sub_object, 0, sizeof(sub_object));
sub_object.name = "sub.service";
sub_object.type = &sub_obj_type;
sub_object.methods = sub_methods;
sub_object.n_methods = ARRAY_SIZE(sub_methods);
ret = ubus_add_object(ser_ctx, &sub_object);
if(ret){
fprintf(stderr, "Failed to add object: %s\n", ubus_strerror(ret));
return ERROR;
}
char msg[64];
while(1)
{
struct blob_buf b = {};
snprintf(msg, sizeof(msg), "Notification #%d", ++counter);
blob_buf_init(&b, 0);
blobmsg_add_string(&b, "message", msg);
// 发送通知给所有订阅者
ubus_notify(ser_ctx, &sub_object, "event", b.head, -1);
blob_buf_free(&b);
sleep(3);
}
uloop_run();
ubus_free(ser_ctx);
uloop_done();
return 0;
}
这里先添加了一个sub.service的object,然后通过ubus_notify发送通知:Notification #xx
使用ubus订阅: ubus subscribe sub.service
可发现成功订阅sub.service发送的消息
注:
通过Ubus -v list | grep sub,可查看对象注册了event,但无法使用call命令通信,因为代码死循环,没有跑到uloop_run.
2,客户端的编写
enum {
EVENT_MSG,
__EVENT_MAX
};
static const struct blobmsg_policy event_policy[] = {
[EVENT_MSG] = { .name = "message", .type = BLOBMSG_TYPE_STRING },
};
static int event_cb(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct blob_attr *tb[__EVENT_MAX];
blobmsg_parse(event_policy, ARRAY_SIZE(event_policy), tb, blob_data(msg), blob_len(msg));
if (tb[EVENT_MSG]) {
printf("--->Received event: %s<---\n", blobmsg_get_string(tb[EVENT_MSG]));
}
return 0;
}
int sub_client()
{
int id = 0 , i = 0;
int ret = 0;
struct ubus_context *cli_ctx = NULL;
struct ubus_subscriber Sub_obj;
uloop_init();
cli_ctx = ubus_connect(NULL);
if (!cli_ctx) {
printf("client ubus_connect failed.");
return ERROR;
}
ubus_add_uloop(cli_ctx);
memset( &Sub_obj, 0, sizeof(Sub_obj));
//填 sub 对象
Sub_obj.cb = event_cb;
Sub_obj.remove_cb = NULL;
ret = ubus_register_subscriber(cli_ctx, &Sub_obj);
if (ret) {
printf("client ubus_register_subscriber [sub.service] is '%s' failed.\n", ubus_strerror(ret));
return ERROR;
}
ret = ubus_lookup_id(cli_ctx, "sub.service", &id);
if(ret) {
fprintf(stderr, "Failed to lookup sub object: %s\n", ubus_strerror(ret));
return ERROR;
}
ret = ubus_subscribe(cli_ctx, &Sub_obj, id);
if (ret) {
printf("client subscribe [mytest.service] is '%s' failed.\n", ubus_strerror(ret));
return ERROR;
}
uloop_run();
ubus_free(cli_ctx);
uloop_done();
}
int main()
{
//sub_service();
sub_client();
}
测试客户端结果:
注: 需要服务端先跑起来,不然look_up_id会返回错误
4,总结
在客户端中,我们使用到ubus_register_subscriber 和ubus_subscriber,来完成一个完整的消息订阅,代码中未实现remove_cb函数。
不管是Call/Sub,我们都需要注册一个object节点,在实际的工程中,我们可能只关心client端的编 写,关心其他进程发送的通知。