D-BUS 基本使用学习 【low-level C API】


系统版本和环境配置

系统版本:ubuntu-18.04.6-live-server-amd64版本系统镜像

环境配置:

sudo apt-get update
sudo apt-get upgrade
sudo apt install build-essential

sudo apt-get install dbus
sudo apt-get install d-feet  # 用于查看 session bus 和 system bus

sudo apt-get install libdbus-1-dev
sudo apt-get install libgtk2.0-dev 
sudo apt-get install libdbus-glib-1-dev  # C和glib下的dbus api库

# 运行 dbus-send 和 dbus-monitor 命令,根据系统提示进行安装

官方文档信息:

该文档下载链接(有图片显示问题):

https://files.cnblogs.com/files/tlam/D-BUS基本使用学习【low-level_C_API】.zip

可能遇到的问题和疑问

  • 针对编译过程中可能出现的错误:

https://blog.csdn.net/zfzf294990051/article/details/6303425

  • system bus和session bus的区别:

https://www.cnblogs.com/cnland/archive/2013/04/28/3049281.html

  • 如果有安装notification-daemon进行监听的需求:

或者存在如 The name org.freedesktop.Notifications was not provided by any .service files 的问题

https://zoomadmin.com/HowToInstall/UbuntuPackage/notification-daemon

https://wiki.archlinux.org/title/Desktop_notifications#Standalone

  • dbus-monitor: unable to enable new-style monitoring 无法监控

ubuntu环境下,dbus-monitor默认禁止监视系统总线,请尝试使用sudo权限运行dbus-monitor命令。

  • 如何去理解dbus-monitor的数据结果:

https://askubuntu.com/questions/38733/how-to-read-dbus-monitor-output

  • 系统DBUS自带一些method,其官方文档:

https://dbus.freedesktop.org/doc/dbus-java/api/org/freedesktop/DBus.html

  • 编译本文档中的代码:
g++ dbus_learn2.cpp -std=c++0x $(pkg-config dbus-1 --cflags) -ldbus-1 -Werror -Wall -Wextra
  • dbus method call over tcp 相关资料,未经可行性实践:

https://stackoverflow.com/questions/10158684/connecting-to-dbus-over-tcp

https://stackoverflow.com/questions/45484655/connect-to-dbus-service-via-tcp

  • dbus 数据绑定相关资料:

https://blog.csdn.net/shanzhizi/article/details/7710692

D-BUS通信编程例子

对具体编程实例中的方法简单介绍,想要具体了解所有内容,请查阅官方文档。

信号signal发送

  • dbus_message_new_signal方法:初始化并返回一个signal类型的DbusMessage
  • dbus_message_iter_init_append方法和dbus_message_iter_append_basic方法:分别对迭代器类型DBusMessageIter进行 初始化绑定到对应的DbusMessage添加基础数据到相应的内存空间
  • dbus_connection_send方法:将对应的DbusMessage发送到消息队列,带回消息队列的serial数据
  • dbus_connection_flush方法:清空dbus消息队列,立即发送

示例程序:

    dbus_uint32_t serial = 0; // unique number to associate replies with requests
    DBusMessage* bus_msg;
    DBusMessageIter bus_args;
    void* sigvalue = (void*)("hello qiujunyin");

    //object name / interface name / name of the signal
    bus_msg = dbus_message_new_signal("/test/signal/Object","test.signal.Type","Test");
    if (bus_msg==NULL){
        cout<<"dbus signale message is null"<<endl;
        exit(1);
    }
    // 初始化参数迭代器并append进入signal信息
    dbus_message_iter_init_append(bus_msg,&bus_args);
    if (!dbus_message_iter_append_basic(&bus_args, DBUS_TYPE_STRING, &sigvalue)) {
        cout<<"dbus iter append fail : out of memory"<<endl;
        exit(1);
    }
    //发送signal serial带回其在消息队列中的位置
    if (!dbus_connection_send(bus_conn,bus_msg,&serial)){
        cout<<"dbus send signal fail : out of memory"<<endl;
        exit(1);
    } 
    cout<<"signal serial in mq :"<<serial<<endl;
    dbus_connection_flush(bus_conn);
    dbus_message_unref(bus_msg);

dbus-monitor监控:

开启dbus-monitor监控后,编译运行上述代码可以观察得到:

dbus-monitor --session --monitor "type=signal,interface=test.signal.Type"

image-20211120224955622

同步method_call

  • dbus_message_new_method_call方法:初始化并返回一个method_call类型的DbusMessage
  • dbus_connection_send_with_reply_and_block方法:阻塞方式发送DbusMessage,等待返回数据
  • dbus_message_get_args方法:对DbusMessage中的对应参数数据解析获取
  • dbus_message_unref方法:释放DbusMessage资源,减少引用技术

示例代码:

    //5. 同步method_call  dbus_connection_send_with_reply_and_block
    DBusMessage * dbus_reply = nullptr;
    const char * dbus_result = nullptr;
    

    bus_msg = dbus_message_new_method_call("org.freedesktop.DBus","/","org.freedesktop.DBus.Introspectable","Introspect");
    if (bus_msg == NULL){
        cout<<"dbus method call is null"<<endl;
        exit(1);
    }
    // 增加入参:a. dbusMessage绑定迭代器原始操作接口 b. dbus_message_append_args

    // const char *v_STRING = "input args test";
    // if (!dbus_message_append_args(bus_msg,DBUS_TYPE_STRING,&v_STRING,DBUS_TYPE_INVALID)){
    //     cout<<"dbus method call append args failed"<<endl;
    //     exit(1);
    // }

    dbus_reply = ::dbus_connection_send_with_reply_and_block(bus_conn, bus_msg, DBUS_TIMEOUT_USE_DEFAULT, &bus_err); //无限时长阻塞
    if (dbus_reply == NULL){
        //如果有入参,预期此处调用失败,此method call为系统dbus提供返回,无需入参。报错多余入参
        cout<<"dbus method call return fail : "<<bus_err.name<<" "<<bus_err.message<<endl;
        exit(1);
    }
    if (!dbus_message_get_args(dbus_reply,&bus_err,DBUS_TYPE_STRING,&dbus_result,DBUS_TYPE_INVALID)){
        cout<<"dbus method call return parser failed : "<<bus_err.name<<" "<<bus_err.message<<endl;
        exit(1);
    }else{
        cout<<"dbue method call result : "<<dbus_result<<endl;
    }
    dbus_message_unref(bus_msg);
    dbus_message_unref(dbus_reply);

此段代码同步调用了系统dbus的相关方法:org.freedesktop.DBus.Introspectable下的Introspect方法,返回官方文档数据字符串。

image-20211120225241760

异步method_call

  • dbus_connection_send_with_reply方法:非阻塞方式下发送DbusMessage,函数调用结果仅代表是否发送成功,具体数据将会被存储在DBusPendingCall类型的数据下。
  • dbus_pending_call_block方法:阻塞等待DBusPendingCall数据回调
  • dbus_pending_call_steal_reply方法:将DBusPendingCall数据转换为DbusMessage数据用于后续使用

示例程序:

    DBusPendingCall* bus_pending;
    DBusMessageIter bus_args2;
    void *param = (void*)("");
    void *stat = new int;

    bus_msg = dbus_message_new_method_call("test.method.server","/test/method/Object","test.method.Type","Method");
    if (bus_msg == NULL){
        cout<<"dbus method call is null"<<endl;\
        exit(1);
    }
    dbus_message_iter_init_append(bus_msg,&bus_args2);
    if (!dbus_message_iter_append_basic(&bus_args2, DBUS_TYPE_STRING, &param)) {
        cout<<"dbus iter append fail : out of memory"<<endl;
        exit(1);
    }
    if (!dbus_connection_send_with_reply(bus_conn, bus_msg, &bus_pending, 1000)) { // -1 is default timeout
        cout<<"dbus send method call : out of memory"<<endl;
        exit(1);
    }
    if (bus_pending==NULL){
        cout<<"dbus method call' Pending is NULL"<<endl;
        exit(1);
    }else{
        cout<<"dbus method call' Pending address : "<<bus_pending<<endl;
    }
    dbus_connection_flush(bus_conn);
    dbus_message_unref(bus_msg);

    dbus_pending_call_block(bus_pending);   //在上述时间限制内,阻塞等待返回数据,去除则完全异步
    bus_msg = dbus_pending_call_steal_reply(bus_pending);   //返回数据转换
    if (bus_msg == NULL) {
        cout<<"dbus method call reply NULL"<<endl;
        exit(1);
    }
    dbus_pending_call_unref(bus_pending);   //释放返回数据的句柄

    if(!dbus_message_iter_init(bus_msg,&bus_args)){
        //获取转换后数据的迭代器
        cout<<"dbus method call reply message has no argument"<<endl;
        exit(1);
    }
    else{
        dbus_message_iter_get_basic(&bus_args, stat);
    }
    cout<<"dbus method call reply value : "<<*((int*)stat)<<endl;
    dbus_message_unref(bus_msg);

接受signal信号

  • dbus_bus_add_match方法:dbus将接受特定来源的信号数据,此方法为其添加匹配规则,默认会接受来源系统默认dbus的信号。
  • dbus_connection_read_write方法:非阻塞方式去查看该连接是否可读写
  • dbus_message_is_signal方法:判断DbusMessage数据的类型是否为signal

示例程序:

    dbus_error_init(&bus_err);
    dbus_bus_add_match(bus_conn,"type='signal',interface='com.example'",&bus_err); //默认对session dbus也监听了
    dbus_connection_flush(bus_conn);
    if (dbus_error_is_set(&bus_err)){
        cout<<"dbus add match fail : "<<bus_err.message<<endl;
        exit(1);
    }
    while(1){
        //非阻塞监听信号
        dbus_connection_read_write(bus_conn, 0);
        bus_msg = dbus_connection_pop_message(bus_conn);
        if(bus_msg==NULL){
            sleep(1);
            continue;
        }

        //再次过滤
        if(dbus_message_is_signal(bus_msg,"com.example","signal_name")){
            if (!dbus_message_get_args(bus_msg,&bus_err,DBUS_TYPE_STRING,&dbus_result,DBUS_TYPE_INVALID)){
                cout<<"dbus method call return parser failed : "<<bus_err.name<<" "<<bus_err.message<<endl;
                exit(1);
            }
            cout<<"dbus get signal message : "<<dbus_result<<endl;
            break;
        }else{
            // cout<<"dbus message interface : "<<dbus_message_get_interface(bus_msg)<<endl;
            // cout<<"dbus message member : "<<dbus_message_get_member(bus_msg)<<endl;
            cout<<"dbus check signal message and pass this signal"<<endl;
        }
        dbus_message_unref(bus_msg);
    }

dbus-send命令模拟发送信号:

dbus-send --session --type=signal / com.example.signal_name string:"hello"

在上述程序编译运行后,运行dbus-send命令:

image-20211120230310268

提供被远程调用的方法

  • dbus_message_is_method_call方法:判断DbusMessage数据的类型是否为method_call

示例程序:

    while(1){
        //非阻塞监听信号
        dbus_connection_read_write(bus_conn, 0);
        bus_msg = dbus_connection_pop_message(bus_conn);
        if(bus_msg==NULL){
            sleep(1);
            continue;
        }

        if(dbus_message_is_method_call(bus_msg,"test.method.Type","Method")){
            reply_to_method_call(bus_conn,bus_msg);
            break;
        }

        dbus_message_unref(bus_msg);
    }

dbus-send命令进行调用:

dbus-send --session --type=method_call --print-reply --dest=test.method.server / test.method.Type.Method string:"hello"

获得我们程序中写入的返回数据

image-20211120230540011

需要注意的事情

完整源代码

#include <dbus/dbus.h>
#include <cstddef>
#include <cstdio>
#include <iostream>
#include <unistd.h>

using namespace std;

void reply_to_method_call(DBusConnection* bus_conn,DBusMessage* bus_msg);

int main(){
    DBusError bus_err;
    DBusConnection* bus_conn;

    //1. DBusError初始化
    dbus_error_init(&bus_err);

    //2. 连接DBUS
    bus_conn = dbus_bus_get(DBUS_BUS_SESSION,&bus_err);
    if (dbus_error_is_set(&bus_err)){
        cout<<"Connection Error : "<<bus_err.message<<endl;
        dbus_error_free(&bus_err);
        exit(1);
    }else if(bus_conn == NULL){
        cout<<"bus_conn is null"<<endl;
        exit(1);
    }

    //3. 设置DBUS名称  or 注册一个BUS NAME
    int ret = dbus_bus_request_name(bus_conn,"test.method.server",DBUS_NAME_FLAG_REPLACE_EXISTING,&bus_err);
    if (dbus_error_is_set(&bus_err)){
        cout<<"Connection Error : "<<bus_err.message<<endl;
        dbus_error_free(&bus_err);
        exit(1);
    }else if (ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER){
        cout<<"request dbus name return fail"<<endl;
        exit(1);
    }


    //4. 信号发送
    dbus_uint32_t serial = 0; // unique number to associate replies with requests
    DBusMessage* bus_msg;
    DBusMessageIter bus_args;
    void* sigvalue = (void*)("hello qiujunyin");

    //object name / interface name / name of the signal
    bus_msg = dbus_message_new_signal("/test/signal/Object","test.signal.Type","Test");
    if (bus_msg==NULL){
        cout<<"dbus signale message is null"<<endl;
        exit(1);
    }
    // 初始化参数迭代器并append进入signal信息
    dbus_message_iter_init_append(bus_msg,&bus_args);
    if (!dbus_message_iter_append_basic(&bus_args, DBUS_TYPE_STRING, &sigvalue)) {
        cout<<"dbus iter append fail : out of memory"<<endl;
        exit(1);
    }
    //发送signal serial带回其在消息队列中的位置
    if (!dbus_connection_send(bus_conn,bus_msg,&serial)){
        cout<<"dbus send signal fail : out of memory"<<endl;
        exit(1);
    } 
    cout<<"signal serial in mq :"<<serial<<endl;
    dbus_connection_flush(bus_conn);
    dbus_message_unref(bus_msg);


    //5. 同步method_call  dbus_connection_send_with_reply_and_block
    DBusMessage * dbus_reply = nullptr;
    const char * dbus_result = nullptr;
    

    bus_msg = dbus_message_new_method_call("org.freedesktop.DBus","/","org.freedesktop.DBus.Introspectable","Introspect");
    if (bus_msg == NULL){
        cout<<"dbus method call is null"<<endl;
        exit(1);
    }
    // 增加入参:a. dbusMessage绑定迭代器原始操作接口 b. dbus_message_append_args

    // const char *v_STRING = "input args test";
    // if (!dbus_message_append_args(bus_msg,DBUS_TYPE_STRING,&v_STRING,DBUS_TYPE_INVALID)){
    //     cout<<"dbus method call append args failed"<<endl;
    //     exit(1);
    // }

    dbus_reply = ::dbus_connection_send_with_reply_and_block(bus_conn, bus_msg, DBUS_TIMEOUT_USE_DEFAULT, &bus_err); //无限时长阻塞
    if (dbus_reply == NULL){
        //如果有入参,预期此处调用失败,此method call为系统dbus提供返回,无需入参。报错多余入参
        cout<<"dbus method call return fail : "<<bus_err.name<<" "<<bus_err.message<<endl;
        exit(1);
    }
    if (!dbus_message_get_args(dbus_reply,&bus_err,DBUS_TYPE_STRING,&dbus_result,DBUS_TYPE_INVALID)){
        cout<<"dbus method call return parser failed : "<<bus_err.name<<" "<<bus_err.message<<endl;
        exit(1);
    }else{
        cout<<"dbue method call result : "<<dbus_result<<endl;
    }
    dbus_message_unref(bus_msg);
    dbus_message_unref(dbus_reply);

    //6. 异步method_call  : 通过迭代器处理输入输出数据
    // 存在问题:异步下如何通过DBusPendingCall捕获错误信息
    DBusPendingCall* bus_pending;
    DBusMessageIter bus_args2;
    void *param = (void*)("");
    void *stat = new int;

    bus_msg = dbus_message_new_method_call("test.method.server","/test/method/Object","test.method.Type","Method");
    if (bus_msg == NULL){
        cout<<"dbus method call is null"<<endl;\
        exit(1);
    }
    dbus_message_iter_init_append(bus_msg,&bus_args2);
    if (!dbus_message_iter_append_basic(&bus_args2, DBUS_TYPE_STRING, &param)) {
        cout<<"dbus iter append fail : out of memory"<<endl;
        exit(1);
    }
    if (!dbus_connection_send_with_reply(bus_conn, bus_msg, &bus_pending, 1000)) { // -1 is default timeout
        cout<<"dbus send method call : out of memory"<<endl;
        exit(1);
    }
    if (bus_pending==NULL){
        cout<<"dbus method call' Pending is NULL"<<endl;
        exit(1);
    }else{
        cout<<"dbus method call' Pending address : "<<bus_pending<<endl;
    }
    dbus_connection_flush(bus_conn);
    dbus_message_unref(bus_msg);

    dbus_pending_call_block(bus_pending);   //在上述时间限制内,阻塞等待返回数据,去除则完全异步
    bus_msg = dbus_pending_call_steal_reply(bus_pending);   //返回数据转换
    if (bus_msg == NULL) {
        cout<<"dbus method call reply NULL"<<endl;
        exit(1);
    }
    dbus_pending_call_unref(bus_pending);   //释放返回数据的句柄

    if(!dbus_message_iter_init(bus_msg,&bus_args)){
        //获取转换后数据的迭代器
        cout<<"dbus method call reply message has no argument"<<endl;
        exit(1);
    }
    else{
        dbus_message_iter_get_basic(&bus_args, stat);
    }
    cout<<"dbus method call reply value : "<<*((int*)stat)<<endl;
    dbus_message_unref(bus_msg);

    //7. 接受信号
    dbus_error_init(&bus_err);
    dbus_bus_add_match(bus_conn,"type='signal',interface='com.example'",&bus_err); //默认对session dbus也监听了
    dbus_connection_flush(bus_conn);
    if (dbus_error_is_set(&bus_err)){
        cout<<"dbus add match fail : "<<bus_err.message<<endl;
        exit(1);
    }
    while(1){
        //非阻塞监听信号
        dbus_connection_read_write(bus_conn, 0);
        bus_msg = dbus_connection_pop_message(bus_conn);
        if(bus_msg==NULL){
            sleep(1);
            continue;
        }

        //再次过滤
        if(dbus_message_is_signal(bus_msg,"com.example","signal_name")){
            if (!dbus_message_get_args(bus_msg,&bus_err,DBUS_TYPE_STRING,&dbus_result,DBUS_TYPE_INVALID)){
                cout<<"dbus method call return parser failed : "<<bus_err.name<<" "<<bus_err.message<<endl;
                exit(1);
            }
            cout<<"dbus get signal message : "<<dbus_result<<endl;
            break;
        }else{
            // cout<<"dbus message interface : "<<dbus_message_get_interface(bus_msg)<<endl;
            // cout<<"dbus message member : "<<dbus_message_get_member(bus_msg)<<endl;
            cout<<"dbus check signal message and pass this signal"<<endl;
        }
        dbus_message_unref(bus_msg);
    }

    //8. 提供被远程调用的方法
    while(1){
        //非阻塞监听信号
        dbus_connection_read_write(bus_conn, 0);
        bus_msg = dbus_connection_pop_message(bus_conn);
        if(bus_msg==NULL){
            sleep(1);
            continue;
        }

        if(dbus_message_is_method_call(bus_msg,"test.method.Type","Method")){
            reply_to_method_call(bus_conn,bus_msg);
            break;
        }

        dbus_message_unref(bus_msg);
    }

    //9. 通过网络的DBUS方式


    // 释放DBUS连接 (和dbus_connection_close 的区别)
    dbus_connection_unref(bus_conn);
    delete[] (char*)stat;
    cout<<"All succeed"<<endl;
    return 0;
}

void reply_to_method_call(DBusConnection* bus_conn,DBusMessage* bus_msg){
    DBusMessage* reply;
    DBusMessageIter args;
    char* param = nullptr;
    dbus_uint32_t level = 21614;
    dbus_uint32_t serial = 0;

    //读取参数
    if (!dbus_message_iter_init(bus_msg, &args)){
        cout<<"dbus method call :Message has no arguments"<<endl;
        return;
    }
    else if (DBUS_TYPE_STRING != dbus_message_iter_get_arg_type(&args)){
        cout<<"dbus method call :Argument is not string"<<endl;
        return;
    }else{
        dbus_message_iter_get_basic(&args, &param);
        cout<<"dbus method call get argument : "<<param<<endl;
    }

    //创建返回
    reply = dbus_message_new_method_return(bus_msg);

    //添加返回数据
    dbus_message_iter_init_append(reply, &args);

    if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &level)) {
        cout<<"dbus method call append data:Out Of Memory"<<endl;
        return;
    }

    //发送返回
    if (!dbus_connection_send(bus_conn, reply, &serial)) {
        cout<<"dbus method call send :Out Of Memory"<<endl;
        return;
    }
    dbus_connection_flush(bus_conn);
    dbus_message_unref(reply);
}


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM