和菜鳥一起學linux之DBUS基礎學習記錄

東月之神發表於2013-08-18

D-Bus三層架構

D-Bus是一個為應用程式間通訊的訊息匯流排系統, 用於程式之間的通訊。它是個3層架構的IPC 系統,包括:

1、函式庫libdbus ,用於兩個應用程式互相聯絡和互動訊息。

2、一個基於libdbus構造的訊息匯流排守護程式,可同時與多個應用程式相連,並能把來自一個應用程式的訊息路由到0或者多個其他程式。

3、基於特定應用程式框架的封裝庫或捆綁(wrapper libraries or bindings )。例如,libdbus-glib和libdbus-qt,還有繫結在其他語言,例如Python的。大多數開發者都是使用這些封裝庫的API,因為它們簡化了D-Bus程式設計細節。libdbus被有意設計成為更高層次繫結的底層後端(low-levelbackend )。大部分libdbus的 API僅僅是為了用來實現繫結。

 

匯流排

  在D-Bus中,“bus”是核心的概念,它是一個通道:不同的程式可以通過這個通道做些操作,比如方法呼叫、傳送訊號和監聽特定的訊號。在一臺機器上匯流排守護有多個例項(instance)。這些匯流排之間都是相互獨立的。

一個持久的系統匯流排(system bus):

它在引導時就會啟動。這個匯流排由作業系統和後臺程式使用,安全性非常好,以使得任意的應用程式不能欺騙系統事件。它是桌面會話和作業系統的通訊,這裡作業系統一般而言包括核心和系統守護程式。這種通道的最常用的方面就是傳送系統訊息,比如:插入一個新的儲存裝置;有新的網路連線;等等。

還將有很多會話匯流排(session buses):

這些匯流排當使用者登入後啟動,屬於那個使用者私有。它是使用者的應用程式用來通訊的一個會話匯流排。同一個桌面會話中兩個桌面應用程式的通訊,可使得桌面會話作為整體整合在一起以解決程式生命週期的相關問題。這在GNOME和KDE桌面中大量使用。

  對於一些遠端的工作站,在system bus中可能會有一些問題,例如熱插拔,是否需要通知遠端的terminal,這會使得kernel暴露一些裝置的能力,不過,我們現在關心D-Bus,是因為手持終端裝置的使用,這些將不會出現。在Internet Tablet,也包括我們的手機系統,所有的應用程式都是使用一個使用者ID執行的,所以只有一個會話通道,這一點是和Linux桌面系統是有明顯區別的。

  D-Bus是低延遲而且低開銷的,設計得小而高效,以便最小化傳送的往返時間。另外,協議是二進位制的,而不是文字的,這樣就排除了費時的序列化過程。從開發者的角度來看,D-BUS 是易於使用的。有線協議容易理解,客戶機程式庫以直觀的方式對其進行包裝。D-Bus的主要目的是提供如下的一些更高層的功能:

A、結構化的名字空間

B、獨立於架構的資料格式

C、支援訊息中的大部分通用資料元素

D、帶有異常處理的通用遠端呼叫介面

E、支援廣播型別的通訊

 

Bus daemon匯流排守護

       Bus daemon是一個特殊的程式:這個程式可以從一個程式傳遞訊息給另外一個程式。當然了,如果有很多applications連結到這個通道上,這個 daemon程式就會把訊息轉發給這些連結的所有程式。在最底層,D-Bus只支援點對點的通訊,一般使用本地套接字(AF_UNIX)在應用和bus daemon之間通訊。D-Bus的點對點是經過busdaemon抽象過的,由busdaemon來完成定址和傳送訊息,因此每個應用不必要關心要把訊息發給哪個程式。D-Bus傳送訊息通常包含如下步驟(正常情況下):

建立和傳送訊息 給後臺bus daemon程式,這個過程中會有兩個上下文的切換。

後臺bus daemon程式會處理該訊息,並轉發給目標程式,這也會引起上下文的切換目標程式接收到訊息,然後根據訊息的種類,做不同的響應:要麼給個確認、要麼應答、還有就是忽略它。最後一種情況對於“通知”型別的訊息而言,前兩種都會引起進一步的上下文切換。

綜上原因,如果你準備在不同的程式之間傳遞大量的資料,D-Bus可能不是最有效的方法,最有效的方法是使用共享記憶體,但是對共享記憶體的管理也是相當複雜的。

 

D-Bus程式通訊簡單框架

 

D-Bus常見概念

原生物件和物件路徑

所有使用D-BUS的應用程式都包含一些物件, 當經由一個D-BUS連線收到一條訊息時,該訊息是被髮往一個物件而不是整個應用程式。在開發中程式框架定義著這樣的物件,例如JAVA,GObject,QObject等等,在D-Bus中成為native object。

對於底層的D-Bus協議,即libdbus API,並不理會這些native object,它們使用的是一個叫做object path的概念。通過object path,高層程式設計可以為物件例項進行命名,並允許遠端應用引用它們。這些名字看起來像是檔案系統路徑,例如一個物件可能叫做“/org/kde/kspread/sheets/3/cells/4/5”。易讀的路徑名是受鼓勵的做法,但也允許使用諸如“/com/mycompany/c5yo817y0c1y1c5b”等,只要它可以為你的應用程式所用。Namespacing的物件路徑以開發者所有的域名開始(如 /org/kde)以避免系統不同程式碼模組互相干擾。

簡單地說:一個應用建立物件例項進行D-Bus的通訊,這些物件例項都有一個名字,命名方式類似於路徑,例如/com/mycompany,這個名字在全域性(session或者system)是唯一的,用於訊息的路由。

 

方法和訊號Methodsand Signals

每一個物件有兩類成員:方法和訊號。方法就是JAVA中同樣概念,方法是一段函式程式碼,帶有輸入和輸出。訊號是廣播給所有興趣的其他實體,訊號可以帶有資料payload。

在 D-BUS 中有四種型別的訊息:方法呼叫(method calls)、方法返回(method returns)、訊號(signals)和錯誤(errors)。要執行 D-BUS 物件的方法,您需要向物件傳送一個方法呼叫訊息。它將完成一些處理(就是執行了物件中的Method,Method是可以帶有輸入引數的。)並返回,返回訊息或者錯誤訊息。訊號的不同之處在於它們不返回任何內容:既沒有“訊號返回”訊息,也沒有任何型別的錯誤訊息。

 

介面Interface

每一個物件支援一個或者多個介面,介面是一組方法和訊號,介面定義一個物件實體的型別。D-Bus對介面的命名方式,類似org.freedesktop.Introspectable。開發人員通常將使用程式語言類的的名字作為介面名字。

 

Proxies代理

代理物件用來表示其他的remote object。當我們觸發了proxy物件的method時,將會在D-Bus上傳送一個method_call的訊息,並等待答覆,根據答覆返回。使用非常方便,就像呼叫一個本地的物件。

 

Bus Names匯流排名字

  當一個應用連線到bus daemon,daemon立即會分配一個名字給這個連線,稱為unique connection name ,這個唯一標識的名字以冒號:開頭,例如:34-907,這個名字在daemon的整個生命週期是唯一的。但是這種名字總是臨時分配,無法確定的,也難以記憶,因此應用可以要求有另外一個名字well-known name 來對應這個唯一標識,就像我們使用域名來對應IP地址一樣。例如可以使用com.mycompany來對映:34-907。應用程式可能會要求擁有額外的周知名字(well-knownname )。例如,你可以寫一個規範來定義一個名字叫做 com.mycompany.TextEditor。你的協議可以指定自己擁有這個名字,一個應用程式應該在路徑/com/mycompany /TextFileManager下有一個支援介面org.freedesktop.FileHandler的物件。應用程式就可以傳送訊息到這個匯流排名字,物件,和介面以執行方法呼叫。

當一個應用結束或者崩潰是,OS kernel會關閉它的匯流排連線。匯流排傳送notification訊息告訴其他應用,這個應用的名字已經失去他的owner。當檢測到這類notification時,應用可以知道其他應用的生命週期。這種方式也可用於只有一個例項的應用,即不開啟同樣的兩個應用的情況。

 

地址

連線建立有server和client,對於bus daemon,應用就是client,daemon是server。一個D-Bus的地址是指server用於監聽,client用於連線的地方,例如unix:path=/tmp/abcedf標識server將在路徑/tmp/abcedf的UNIX domain socket監聽。地址可以是指定的TCP/IP socket或者其他在或者將在D-Bus協議中定義的傳輸方式。

  如果使用bus daemon,libdbus將通過讀取環境變數自動獲取session bus damon的地址,通過檢查一個指定的UNIX domain socket路徑獲取system bus的地址。如果使用D-bus,但不是daemon,需要定義那個應用是server,那個是client,並定義一套機制是他們認可server的地址,這不是通常的做法。

  通過上面的描述,我們可以獲得下面的檢視:

Address –> [BusName] –> Path –> Interface –> Method

bus name不是必要的,它只在daemon的情況下用於路由,點對點的直接連線是不需要的。

簡單地說:Address是D-Bus中server用來監聽client的地址,當一個client連線上D-Bus,通常是Daemo的方式,這個client就有了一個Bus Name。其他應用可以根據訊息中所帶的Bus Name,來判斷和哪個應用相關。訊息在匯流排中傳遞的時候,傳遞到應用中,再根據objectpath,送至應用中具體的物件例項中,也就是是應用中根據Interface建立的物件。這些Interface有method和singal兩種,用來傳送、接收、響應訊息。

 

一個例子的示意圖:

 

D-Bus 訊息

訊息通過D-Bus在程式間傳遞。有四類訊息:

一、Method call訊息:將觸發物件的一個method 

二、Method return訊息:觸發的方法返回的結果

       三、Error訊息:觸發的方法返回一個異常 

四、Signal訊息:通知,可以看作為事件訊息。

 

一個訊息有訊息頭header,裡面有field,有一個訊息體body,裡面有引數arguments。訊息頭包含訊息體的路由資訊,訊息體就是淨荷payload。頭欄位可能包括髮送者的bus名,目的地的bus名,方法或者signal名等等,其中一個頭欄位是用於描述body中的引數的型別,例如“i”標識32位整數,"ii”表示淨荷為2個32為整數。

 

傳送Method call訊息的場景

  一個method call訊息從程式A到程式B,B將應答一個method return訊息或者error訊息。在每個call訊息帶有一個序列號,應答訊息也包含同樣的號碼,使之可以對應起來。他們的處理過程如下:

如果提供proxy,通過觸發本地一個物件的方法從而觸發另一個程式的遠端物件的方法。應用呼叫proxy的一個方法,proxy構造一個method call訊息傳送到遠端程式。

對於底層的API,不使用proxy,應用需要自己構造method call訊息。

一個method call訊息包含:遠端程式的bus name,方法名字,方法的引數,遠端程式中object path,可選的介面名字。

method call訊息傳送到bus daemon

bus daemon檢視目的地的bus name。如果一個程式對應這個名字,bus daemon將method call訊息傳送到該程式中。如果沒有發現匹配,bus daemon建立一個error訊息作為應答返回。

程式接收後將method call訊息分拆。對於簡單的底層API情況,將立即執行方法,併傳送一個method reply訊息給bus daemon。對於高層的API,將檢查物件path,interface和method,觸發一個native object的方法,並將返回值封裝在一個method reply訊息中。

bus daemon收到method reply訊息,將其轉發到原來的程式中程式檢視method reply訊息,獲取返回值。這個響應也可以標識一個error的殘生。當使用高階的捆綁,method reply訊息將轉換為proxy方法的返回值或者一個exception。

Bus daemon保證message的順序,不會亂序。例如我們傳送兩個method call訊息到同一個接受方,他們將按順序接受。接收方並不要求一定按順序回覆。訊息有一個序列號了匹配收發訊息。

 

傳送Signal的場景

  signal是個廣播的訊息,不需要響應,接收方向daemon註冊匹配的條件,包括髮送方和訊號名,bus守護只將訊號傳送給希望接受的程式。處理流程如下:

一個signal訊息傳送到bus daemon。

signal訊息包含釋出該訊號的interface名字,signal的名字,程式的bus名字,以及引數。

任何程式都可以註冊的匹配條件(match rules)表明它所感興趣的signal。匯流排有個註冊match rules列表。

bus daemon檢查那些程式對該訊號有興趣,將訊號訊息傳送到這些程式中。

收到訊號的程式決定如何處理。如果使用高層的捆綁,一個porxy物件將會十分一個native的訊號。如果使用底層的API,程式需要檢查訊號的傳送發和訊號的名字決定如果進行處理。

 

Introspection

  D-Bus物件可能支援一個介面org.freedesktop.DBus.Introspectable。該介面有一個方法Introspect,不帶引數,將返回一個XML string。這個XML字串描述介面,方法,訊號。

D-Bus提供兩個命令dbus-monitor,可以檢視bus,dbus-send命令,可以傳送訊息。

 

Signal的收發小例子

從底層,即libdbus學習如何傳送signal,以及如何監聽signal。signal在D-Bus的Daemon中廣播,為了提高效率,只傳送給向daemon註冊要求該singal的物件。

 

對於程式,第一步需要將應用和D-Bus後臺建立連線,也就是和System D-Busdaemon或者Session D-Bus daemon建立連線。一旦建立,daemon會給這條連線分配一個名字,這個名字在system或者session的生命週期是唯一的,即unique connectionname,為了方便記憶,可以為這條連線分配一個便於記憶的well-known name。對於訊號方式,分配這個名字不是必須的(在method_call中是需要的),因為在訊號的監聽中秩序給出Interface的名字和訊號名稱,在下面的例子中,可以將相關的程式碼遮蔽掉,不影響執行,但是通常我們都這樣處理,尤其在複雜的程式中。在我們的例子中,定義這個BUS name為test.singal.source。當然一個好的名字,為了避免於其他應用重複,應當使用com.mycompany.myfunction之類的名字。 ,而interface的名字,一般前面和connection的BUS name一致。

 

傳送方的小程式

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dbus/dbus-glib.h>
#include <dbus/dbus.h>
#include <unistd.h>

int send_a_signal( char * sigvalue)
{
    DBusError err;
    DBusConnection * connection;
    DBusMessage * msg;
    DBusMessageIter arg;
    dbus_uint32_t  serial =0;
    int ret;

    //步驟1:建立與D-Bus後臺的連線
    
    dbus_error_init(&err);
     
    connection =dbus_bus_get(DBUS_BUS_SESSION ,&err );
    if(dbus_error_is_set(&err)){
        fprintf(stderr,"ConnectionErr : %s\n",err.message);
        dbus_error_free(&err);
    }
    if(connection == NULL)
        return -1;

    //步驟2:給連線名分配一個well-known的名字作為Bus name,這個步驟不是必須的,可以用if 0來註釋著一段程式碼,我們可以用這個名字來檢查,是否已經開啟了這個應用的另外的程式。
#if 1
    ret =dbus_bus_request_name(connection,"test.singal.source",DBUS_NAME_FLAG_REPLACE_EXISTING,&err);
    if(dbus_error_is_set(&err)){
        fprintf(stderr,"Name Err :%s\n",err.message);
        dbus_error_free(&err);
    }
    if(ret !=DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
        return -1;
#endif

    //步驟3:傳送一個訊號
    //根據圖,我們給出這個訊號的路徑(即可以指向物件),介面,以及訊號名,建立一個Message
    if((msg =dbus_message_new_signal("/test/signal/Object","test.signal.Type","Test"))== NULL){
        fprintf(stderr,"MessageNULL\n");
        return -1;
    }
    //給這個訊號(messge)具體的內容
    dbus_message_iter_init_append(msg,&arg);
   if(!dbus_message_iter_append_basic(&arg,DBUS_TYPE_STRING,&sigvalue)){
        fprintf(stderr,"Out OfMemory!\n");
        return -1;
    }

    //步驟4: 將訊號從連線中傳送
    if( !dbus_connection_send(connection,msg,&serial)){
        fprintf(stderr,"Out of Memory!\n");
        return -1;
    }
    dbus_connection_flush(connection);
    printf("Signal Send\n");

   //步驟5: 釋放相關的分配的記憶體。
    dbus_message_unref(msg );
    return 0;
}
int main( int argc , char ** argv){
   send_a_signal("Hello,world!");
    return 0;
}


 

希望接收該訊號的的小程式例子

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dbus/dbus-glib.h>
#include <dbus/dbus.h>
#include <unistd.h>

void listen_signal()
{
    DBusMessage * msg;
    DBusMessageIter arg;
    DBusConnection * connection;
    DBusError err;
    int ret;
    char * sigvalue;

     //步驟1:建立與D-Bus後臺的連線
    dbus_error_init(&err);
    connection =dbus_bus_get(DBUS_BUS_SESSION, &err);
    if(dbus_error_is_set(&err)){
        fprintf(stderr,"ConnectionError %s\n",err.message);
        dbus_error_free(&err);
    }
    if(connection == NULL)
        return;

   //步驟2:給連線名分配一個可記憶名字test.singal.dest作為Bus name,這個步驟不是必須的,但推薦這樣處理
    ret =dbus_bus_request_name(connection,"test.singal.dest",DBUS_NAME_FLAG_REPLACE_EXISTING,&err);
    if(dbus_error_is_set(&err)){
        fprintf(stderr,"Name Error%s\n",err.message);
        dbus_error_free(&err);
    }
    if(ret !=DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
        return;

    //步驟3:通知D-Bus daemon,希望監聽來行介面test.signal.Type的訊號
    dbus_bus_add_match(connection,"type='signal',interface='test.signal.Type'",&err);
    //實際需要傳送東西給daemon來通知希望監聽的內容,所以需要flush
    dbus_connection_flush(connection);
    if(dbus_error_is_set(&err)){
        fprintf(stderr,"Match Error%s\n",err.message);
        dbus_error_free(&err);
    }
   
    //步驟4:在迴圈中監聽,每隔開1秒,就去試圖自己的連線中獲取這個訊號。這裡給出的是中連線中獲取任何訊息的方式,所以獲取後去檢查一下這個訊息是否我們期望的訊號,並獲取內容。我們也可以通過這個方式來獲取method call訊息。
    while(1){
        dbus_connection_read_write(connection,0);
        msg =dbus_connection_pop_message (connection);
        if(msg == NULL){
            sleep(1);
            continue;
        }
   
        if(dbus_message_is_signal(msg,"test.signal.Type","Test")){
            if(!dbus_message_iter_init(msg,&arg))
                fprintf(stderr,"MessageHas no Param");
            else if(dbus_message_iter_get_arg_type(&arg)!= DBUS_TYPE_STRING)
                g_printerr("Param isnot string");
            else
                dbus_message_iter_get_basic(&arg,&sigvalue);
            printf("Got Singal withvalue : %s\n",sigvalue);
        }
        dbus_message_unref(msg);
    }//End of while
       
}

int main( int argc , char ** argv){
    listen_signal();
    return 0;
}


 

Method的收發小例子

監聽method和監聽signal的方式非常相似。在給出例子之前,我希望和上次學習一樣給出一個示意圖,更好地瞭解D-Bus的各個概念。

 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dbus/dbus-glib.h>
#include <dbus/dbus.h>
#include <unistd.h>

 
void reply_to_method_call(DBusMessage * msg, DBusConnection * conn){
    DBusMessage * reply;
    DBusMessageIter arg;
    char * param = NULL;
    dbus_bool_t stat = TRUE;
    dbus_uint32_t level = 2010;
    dbus_uint32_t serial = 0;
   
    //從msg中讀取引數,這個在上一次學習中學過
   if(!dbus_message_iter_init(msg,&arg))
        printf("Message has noargs\n");
    else if(dbus_message_iter_get_arg_type(&arg)!= DBUS_TYPE_STRING)
        printf("Arg is notstring!\n");
    else
       dbus_message_iter_get_basic(&arg,& param);
    if(param == NULL) return;


    //建立返回訊息reply
    reply = dbus_message_new_method_return(msg);
    //在返回訊息中填入兩個引數,和訊號加入引數的方式是一樣的。這次我們將加入兩個引數。
    dbus_message_iter_init_append(reply,&arg);
    if(!dbus_message_iter_append_basic(&arg,DBUS_TYPE_BOOLEAN,&stat)){
        printf("Out ofMemory!\n");
        exit(1);
    }
    if(!dbus_message_iter_append_basic(&arg,DBUS_TYPE_UINT32,&level)){
        printf("Out ofMemory!\n");
        exit(1);
    }
  //傳送返回訊息
      if( !dbus_connection_send(conn, reply,&serial)){
        printf("Out of Memory\n");
        exit(1);
    }
    dbus_connection_flush (conn);
    dbus_message_unref (reply);
}

 
void listen_dbus()
{
    DBusMessage * msg;
    DBusMessageIter arg;
    DBusConnection * connection;
    DBusError err;
    int ret;
    char * sigvalue;

    dbus_error_init(&err);
    //建立於session D-Bus的連線
    connection =dbus_bus_get(DBUS_BUS_SESSION, &err);
    if(dbus_error_is_set(&err)){
        fprintf(stderr,"ConnectionError %s\n",err.message);
        dbus_error_free(&err);
    }
    if(connection == NULL)
        return;
    //設定一個BUS name:test.wei.dest
    ret =dbus_bus_request_name(connection,"test.wei.dest",DBUS_NAME_FLAG_REPLACE_EXISTING,&err);
    if(dbus_error_is_set(&err)){
        fprintf(stderr,"Name Error%s\n",err.message);
        dbus_error_free(&err);
    }
    if(ret !=DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
        return;

    //要求監聽某個singal:來自介面test.signal.Type的訊號
   dbus_bus_add_match(connection,"type='signal',interface='test.signal.Type'",&err);
    dbus_connection_flush(connection);
    if(dbus_error_is_set(&err)){
        fprintf(stderr,"Match Error%s\n",err.message);
        dbus_error_free(&err);
    }

    while(true){
        dbus_connection_read_write(connection,0);
        msg =dbus_connection_pop_message (connection);

        if(msg == NULL){
            sleep(1);
            continue;
        }

        if(dbus_message_is_signal(msg,"test.signal.Type","Test")){
            if(!dbus_message_iter_init(msg,&arg))
               fprintf(stderr,"Message Has no Param");
            elseif(dbus_message_iter_get_arg_type(&arg) != DBUS_TYPE_STRING)
                g_printerr("Param isnot string");
            else
               dbus_message_iter_get_basic(&arg,&sigvalue);
        }else if(dbus_message_is_method_call(msg,"test.method.Type","Method")){
            //我們這裡面先比較了介面名字和方法名字,實際上應當現比較路徑
            if(strcmp(dbus_message_get_path(msg),"/test/method/Object") == NULL)
               reply_to_method_call(msg,connection);
        }
        dbus_message_unref(msg);
    }
   
   
}
int main( int argc , char ** argv){
    listen_dbus();
    return 0;
}


 

 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dbus/dbus-glib.h>
#include <dbus/dbus.h>
#include <unistd.h>
//建立與session D-Bus daemo的連線,並設定連線的名字,相關的程式碼已經多次使用過了
DBusConnection * connect_dbus(){
    DBusError err;
    DBusConnection * connection;
    int ret;

    //Step 1: connecting session bus
     
    dbus_error_init(&err);
     
    connection =dbus_bus_get(DBUS_BUS_SESSION, &err);
    if(dbus_error_is_set(&err)){
        fprintf(stderr,"ConnectionErr : %s\n",err.message);
        dbus_error_free(&err);
    }
    if(connection == NULL)
        return NULL;

    //step 2: 設定BUS name,也即連線的名字。
    ret =dbus_bus_request_name(connection,"test.wei.source",DBUS_NAME_FLAG_REPLACE_EXISTING,&err);
    if(dbus_error_is_set(&err)){
        fprintf(stderr,"Name Err :%s\n",err.message);
        dbus_error_free(&err);
    }
    if(ret !=DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
        return NULL;

    return connection;   
}

void send_a_method_call(DBusConnection * connection,char * param)
{
   DBusError err;
    DBusMessage * msg;
    DBusMessageIter    arg;
    DBusPendingCall * pending;
    dbus_bool_t * stat;
    dbus_uint32_t * level;   
   
    dbus_error_init(&err);

    //針對目的地地址,請參考圖,建立一個method call訊息。Constructs a new message to invoke a method on a remote object.
    msg =dbus_message_new_method_call ("test.wei.dest","/test/method/Object","test.method.Type","Method");
   if(msg == NULL){
        g_printerr("MessageNULL");
        return;
    }

    //為訊息新增引數。Appendarguments
    dbus_message_iter_init_append(msg, &arg);
    if(!dbus_message_iter_append_basic(&arg, DBUS_TYPE_STRING,¶m)){
       g_printerr("Out of Memory!");
        exit(1);
    }

    //傳送訊息並獲得reply的handle。Queues amessage to send, as withdbus_connection_send() , but also returns aDBusPendingCall used to receive a reply to the message.
    if(!dbus_connection_send_with_reply (connection, msg,&pending, -1)){
       g_printerr("Out of Memory!");
        exit(1);
    }     

    if(pending == NULL){
        g_printerr("Pending CallNULL: connection is disconnected ");
        dbus_message_unref(msg);
        return;
    }

    dbus_connection_flush(connection);
    dbus_message_unref(msg);
 
   //waiting a reply,在傳送的時候,已經獲取了methodreply的handle,型別為DBusPendingCall。
    // block until we recieve a reply, Block until the pendingcall is completed.
   dbus_pending_call_block (pending);
    //get the reply message,Gets thereply, or returns NULL if none has been received yet.
    msg =dbus_pending_call_steal_reply (pending);
    if (msg == NULL) {
        fprintf(stderr, "ReplyNull\n");
         exit(1);
    }
     // free the pendingmessage handle
     dbus_pending_call_unref(pending);
    // read the parameters
    if(!dbus_message_iter_init(msg, &arg))
        fprintf(stderr, "Message hasno arguments!\n");
    else if (dbus_message_iter_get_arg_type(&arg) != DBUS_TYPE_BOOLEAN)
        fprintf(stderr, "Argument isnot boolean!\n");
    else
        dbus_message_iter_get_basic(&arg, &stat);
 
    if (!dbus_message_iter_next(&arg))
        fprintf(stderr, "Message hastoo few arguments!\n");
    else if (dbus_message_iter_get_arg_type(&arg) != DBUS_TYPE_UINT32 )
        fprintf(stderr, "Argument isnot int!\n");
    else
        dbus_message_iter_get_basic(&arg, &level);

    printf("Got Reply: %d,%d\n", stat, level);
    dbus_message_unref(msg);
}

int main( int argc , char ** argv){
   DBusConnection * connection;
    connection = connect_dbus();
    if(connection == NULL)
        return -1;

   send_a_method_call(connection,"Hello, D-Bus");
    return 0;
}

 


相關文章