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