UBUS 通信接口的使用——实现订阅与通知( ubus Subscribe)

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端的编       写,关心其他进程发送的通知。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值