DBus API的使用(講的很清晰)

轉載請註明出處               做者: 唐風 html

DBus是用來進行進程間通訊的。下面這張圖展現了一些DBus的大部分東西,可是它太複雜了: 編程

 
image

DBus 自己是構建在 Socket 機制之上。真正的通訊仍是由 Socket 來完成的。DBus 則是在這之上,制定了一些通訊的協議,並提供了更高一層的接口,以更方便應用程序之間進行數據的交互。 windows

在DBus的體系中,有一個常駐的進程 Daemon,全部進程間的交互都經過它來進行分發和管理。全部但願使用 DBus 進行通訊的進程,都必須事先連上 Daemon,並將本身的名字註冊到 Daemon 上,以後,Daemon會根據須要把消息以及數據發到相應的進程中。 api

首先使用 數據結構

1 conn = dbus_bus_get(DBUS_BUS_SESSION, &err);

讓應用程序和 DBus 之間取得鏈接。以後,使用函數 app

1 ret = dbus_bus_request_name(conn, "test.method.server", 2  DBUS_NAME_FLAG_REPLACE_EXISTING 3 , &err);

將本身的進程名字註冊到 Daemon 上。(參考前篇的[共通用代碼])。這樣通訊就有了基礎了。 框架

DBus 提供的最簡單的一種通訊方式是信號(Signal),應用程序能夠發送一個信號到 Daemon 上,以後,Daemon 會根據信號的種類和誰但願獲得信號等信息,把相應的數據發給每一個但願獲得信號的進程。也就是 Signal 具備廣播的功能。信號具備兩個基本的屬性,一個是名稱,用來標識各個不一樣的信號,一個是數據,信號是能夠帶必定的數據的。Signal 的通訊過程能夠用下面的圖大概展現出來: 異步

 
image
 

若是一個進程(好比 B )想接收接口名爲 test.signal.Type 的信號,那麼可使用下面的函數向 Daemon 添加匹配信號,讓 Daemon 知道本身對這種信號感興趣。 函數

1 // add a rule for which messages we want to see 2 dbus_bus_add_match(conn, 3 "type='signal',interface='test.signal.Type'", 4 &err); // see signals from the given interface

 

而後, 進程 B 可使用下面的函數來進行等待: ui

1 dbus_connection_read_write(conn, 0); 2 msg = dbus_connection_pop_message(conn);

一旦有消息發送過來,進程 B 就能夠經過 msg 取到相應的數據了。(參考前篇代碼段[接收消息一、2] )

如今有一個進程 A,

複製代碼
 1 dbus_uint32_t serial = 0; // unique number to associate replies with requests  2 DBusMessage* msg;  3  4 // create a signal and check for errors  5 msg = dbus_message_new_signal("/test/signal/Object", // object name of the signal  6 "test.signal.Type", // interface name of the signal  7 "Test"); // name of the signal  8  9 // append arguments onto signal 10 dbus_message_iter_init_append(msg, &args); 11 if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &sigvalue)) { 12 fprintf(stderr, "Out Of Memory!\n"); 13 exit(1); 14 } 15 16 // send the message and flush the connection 17 if (!dbus_connection_send(conn, msg, &serial)) { 18 fprintf(stderr, "Out Of Memory!\n"); 19 exit(1); 20 }
複製代碼

到這裏爲止,已經涉及到了幾個基本而又核心的概念,搞清楚它們,幾乎就大概知道了 DBus 的使用方法了。

DBusMessage 是 DBus 中的核心數據結構。能夠這樣理解:DBus中傳遞消息數據的時候,就是經過它來傳遞的。對於使用者來講,DBusMessage 中存儲了兩種重要的信息,一種是爲通訊機制服務的各類 Name,一種是通訊的數據自己。

 

各類名字(Name)

在前面用到的不少接口中都還有名稱/路徑字符串做爲參數。

DBus Name: 在 DBus 中最爲重要的名字是「Bus Name」,Bus Name 是一個每一個應用程序(或是通訊對象)用來標識本身用的。幾乎能夠當成是「IP」地址來理解。Bus Name有兩種,一種是「Unique Connection Name」,是以冒號開頭的,是全局惟一但「人類不友好」的命名,一種是「Well-know Name」,人類友好的。Bus Name 的命名規則是:

1 Bus name 就像網址同樣,由「.」號分割的多個子字符串組成,每一個子字符串都必須至少有一個以上的字符。 2 每一個子字符串都只能由「[A-Z][a-z][0-9]_-」這些 ASCII 字符組成,只有 Unique Name 的子串能夠以數字開頭。 3 每一個 Bus name 至少要有一個「.」,和兩個子字符串,不能以「.」開頭 4 Bus name 不能超過 255 個字符

幾個例子是:Unique Name   :392-2.33 org.freedesktop.DBus 等等

DBus Name 是用來給應用程序進行標識本身的,因此每當程序連上 DBus Daemon 後,就會分配到一個 Unique Name,同時應用程序還能夠要求本身分配另外一個 Well-know name (經過 dbus_bus_request_name 函數)。

Interface name: DBus 也有 interface 這個概念,主要是用來爲更高一層的框架使用方面而設定的。在 C API 這一層,你幾乎能夠無視這個概念,只須要知道這個一個「字符串」,並在消息匹配是被 DBus 使用到,會隨着消息在不一樣的進程以前傳遞,從進程 A 發送一個消息或是數據到進程 B 時,其中一定會帶有一個部分就是這個字符串,至於 B 進程怎麼用(或是無視它)均可以。它的命名規則與 DBus Name 幾乎是同樣的,只有一點要注意,interface name 中不能帶有「-」字符。

Object path:DBus 中的 object path,與 interface 同樣,也只是個概念在更高一層的框架(QT Dbus)中才比較有用,在 C API 這一層,幾乎能夠無視這個概念,把它當成一個普通的字符串,根據通訊的須要,用來作一種標識和區分。Object path 的命名規則是:/com/example/MusicPlayer1

1 object path 能夠是任意長度的 2'/'開頭,並以以'/'分隔的若干子字符串組成 3 每一個子串必須由「[A-Z][a-z][0-9]_」中的字符組成 4 不能有空子串(也就是不能連續兩個'/'符) 5 除了「root path」('/')以外,不能再有 object path 是以 '/' 結尾的了。

一個 object path 的例子:/com/example/MusicPlayer1

Member name:Member 包含兩種類型,一種是 Signal,一種是 Method。在大多數方面,他們幾乎是同樣的,除了兩點:1. Signal是在總線中進行廣播的,而Method是指定發給某個進程的。2. Signal 不會有返回,而 Method 必定會有返回(同步的或是異步的)。Member name的命名規則是這樣的:

1 只能包含"[A-Z][a-z][0-9]_"這些字符,且不能以數字開頭。不能包含「.」。 2 不能超過255個字符

以 C API 的層面來看,Member name最大的做用就是在兩個進程間共享「發出的消息的類型信息」。DBus 只能以 Signal / Method 來進行消息通訊,這兩種方式都容許在消息發出以前,在消息中 append 各類類型的數據,當通訊的對方收到消息後,它就能夠經過 Signal / Method 的名稱知道如何把各類數據再解析出來。

到如今爲止,已經介紹了三種最爲重要的 Name,若是與的熟悉 windows 消息機制對比的話,我大概以爲,DBus Name 就是進程的 ID,有了這個你才能把消息發給指定的進程,object path ,interface等概念在「(QT等)高層框架中才有意義」,C API 級別的使用的話,能夠無視它的概念,當成消息甄別用的信息就行了。 Member name 至關於 Message type,有了它你才知道怎麼去解析發過來的數據。

下篇將會記錄 DBusMessage 另外一個主要的組成部分:通訊數據。

 
 
通訊數據的設置和獲取
 
DBus 提供了一個 DBusMessageIter 的類型,使用這個類型的變量,咱們就能夠向 DBusMessage 中很容易地加入數據,也能夠很容易地從中取出數據。
 
1、向 DBusMessage 中加入數據
分析一個例子:
複製代碼
 1 DBusMessage* msg;  2 DBusMessageIter args;  3  4 // msg...  5  6 dbus_message_iter_init_append(msg, &args);  7 dbus_uint32_t my_data = 10;  8 if(!dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &my_data)) {  9 printf("Out of memory\n"); 10 return RES_FAILED; 11 } 12 13 dbus_connection_flush(conn); 14 dbus_message_unref(msg);
複製代碼
第 2 行的代碼聲明瞭一個 DBusMessageIter 的對象 args;
第 6 行的函數,對 args 進行初始化,這可讓一個 DBusMessageIter 對象與對應的 DBusMessage 關聯起來,後面再對 DBusMessageIter的時候(設置或者取得數據),就是對相應的 DBusMessage 進行處理。
第 8 行的函數,將一個 uint32 的數據 my_data 追加到 msg 中了。若是還要追加新的參數的話,只須要繼續調用該函數,並傳入適當的參數就能夠了。
 
 
2、從 DBusMessage 中取出數據
分析一個例子:
複製代碼
 1 DBusMessage* msg;  2 DBusMessageIter args;  3  4 // get a DBusMessage from process A  5  6 if(!dbus_message_iter_init(msg, &args)) {  7 printf("dbus_message_iter_init error, msg has no arguments!\n");  8 }  9 else if (DBUS_TYPE_UINT32 != dbus_message_iter_get_arg_type(&args)){ 10 printf("not a uint 32 type !\n"); 11 } 12 else { 13 dbus_uint32_t my_age = 0; 14 dbus_message_iter_get_basic(&args, &my_age); 15 printf("Got signal with value %d\n", my_age); 16 }
複製代碼

很簡單,同樣的先使用 dbus_messge_iter_init 先把 DBusMessage 對象和從 DBus 總線中取到的 msg 關聯起來。

這樣,使用第 9 行的函數先取得第一個通訊數據中第一個參數的類型,若是類型無誤的話能夠進而使用第 14 行的函數取得參數值自己。

這樣,一個簡單的數據如何入到 DBusMessage 中,又如何從 DBusMessage 中取出來就明白了。那麼如何將 DBusMessage 在進程之間傳遞呢?

 
 

消息的發送和獲取
 
消息的發送其實比較簡單,當進程 A 準備申請好一個 DBusMessage對象,設置好它的「類型」(就是各類名字),放好須要通訊的數據,以後,使用下面的代碼就能夠將數據發送到總線上:
複製代碼
1 dbus_uint32_t serial = 0; 2 3 if(!dbus_connection_send(conn, msg, &serial)) { 4 printf("Out of memory"); 5 return RES_FAILED; 6 } 7 dbus_connection_flush(conn);
複製代碼

這很簡單,只是 dbus_connection_flush 這個函數有點突兀,它的做用是「Blocks until the outgoing message queue is empty.」,能夠簡單地理解爲調用這個函數可使用得發送進程一直等消息發送完了才能繼續運行。

接受方的代碼也很簡單:
複製代碼
1 dbus_connection_read_write(conn, 0); 2 msg = dbus_connection_pop_message(conn); 3 4 if(NULL == msg) { 5 sleep(1); 6 continue; 7 }
複製代碼

使用 1 和 2 行的代碼就能夠取出發送到本進程的消息,以後就可使用 msg (若是 msg 不是 NULL 的話)來獲取通訊數據了。

到這裏,基本概念就有了。後面,應該對 DBus 的細節再深刻的探索。

Sample 代碼:

發送方進程(my_client):

My_Client.cpp
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <dbus/dbus.h>
  4. #include <unistd.h>
  5.  
  6. const int RES_SUCCESS = -1;
  7. const int RES_FAILED  = 0;
  8.  
  9. int my_dbus_initialization(char const * _bus_name, DBusConnection ** _conn) {
  10.     DBusError err;
  11.  
  12.     int ret;
  13.  
  14.     dbus_error_init(&err);
  15.  
  16.     *_conn = dbus_bus_get(DBUS_BUS_SESSION, &err);
  17.     if(dbus_error_is_set(&err)) {
  18.         printf("Connection Error\n");
  19.         dbus_error_free(&err);
  20.         return RES_FAILED;
  21.     }
  22.  
  23.     ret = dbus_bus_request_name(*_conn, _bus_name, DBUS_NAME_FLAG_REPLACE_EXISTING, &err);
  24.     if(dbus_error_is_set(&err)){
  25.         printf("Requece name error \n");
  26.         dbus_error_free(&err);
  27.         return RES_FAILED;
  28.     }
  29.  
  30.     if(DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER != ret) {
  31.         return RES_FAILED;
  32.     }
  33.  
  34.     return RES_SUCCESS;
  35. }
  36.  
  37. int my_dbus_send_sigal(DBusConnection * conn) {
  38.     dbus_uint32_t serial = 0;
  39.     DBusMessage* msg;
  40.     DBusMessageIter args;
  41.     char sigvalue[20] = "liyiwen";
  42.  
  43.     msg = dbus_message_new_signal("/test/signal/Object",  // object name
  44.         "test.signal.Type",     // interface name
  45.         "Test");                // name of signal
  46.  
  47.     if (NULL == msg) {
  48.         printf("Message Null");
  49.         return RES_FAILED;
  50.     }
  51.  
  52.     dbus_message_iter_init_append(msg, &args);
  53.  
  54.     printf("%s\n", sigvalue);
  55.     dbus_uint32_t my_age = 10;
  56.     if(!dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &my_age)) {
  57.         printf("Out of memory\n");
  58.         return RES_FAILED;
  59.     }
  60.  
  61.     if(!dbus_connection_send(conn, msg, &serial)) {
  62.         printf("Out of memory");
  63.         return RES_FAILED;
  64.     }
  65.     dbus_connection_flush(conn);
  66.  
  67.     dbus_message_unref(msg);
  68.  
  69.     return RES_SUCCESS;
  70.  
  71. }
  72.  
  73. int main(int agrc, char** argv)
  74. {
  75.     DBusConnection * conn;
  76.  
  77.     printf("Start\n");
  78.     if (RES_FAILED == my_dbus_initialization("test.method.client", &conn)) {
  79.         exit(1);
  80.     }
  81.     my_dbus_send_sigal(conn);
  82.  
  83.     while(1){sleep(10);}
  84.  
  85.     return 0;
  86. }

接收方進程(my_server.cpp)

My_Server.cpp
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <dbus/dbus.h>
  4. #include <unistd.h>
  5.  
  6. const int RES_SUCCESS = -1;
  7. const int RES_FAILED  = 0;
  8.  
  9. int my_dbus_initialization(char const * _bus_name, DBusConnection **_conn) {
  10.     DBusError err;
  11.     int ret;
  12.  
  13.     dbus_error_init(&err);
  14.  
  15.     *_conn = dbus_bus_get(DBUS_BUS_SESSION, &err);
  16.     if(dbus_error_is_set(&err)) {
  17.         printf("Connection Error(%s) \n", err.message);
  18.         dbus_error_free(&err);
  19.         return RES_FAILED;
  20.     }
  21.  
  22.     ret = dbus_bus_request_name(*_conn, _bus_name, DBUS_NAME_FLAG_REPLACE_EXISTING, &err);
  23.     if(dbus_error_is_set(&err)){
  24.         printf("Requece name error(%s) \n", err.message);
  25.         dbus_error_free(&err);
  26.         return RES_FAILED;
  27.     }
  28.     if(DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER != ret) {
  29.         return RES_FAILED;
  30.     }
  31.     return RES_SUCCESS;
  32. }
  33.  
  34. int main(int agrc, char** argv)
  35. {
  36.  
  37.     DBusError err;
  38.     DBusMessage* msg;
  39.     DBusMessageIter args;
  40.  
  41.     dbus_error_init(&err);
  42.     DBusConnection *conn;
  43.     if (RES_FAILED == my_dbus_initialization("test.method.server", &conn)) {
  44.         exit(1);
  45.     }
  46.  
  47.     dbus_bus_add_match(conn, "type='signal', interface='test.signal.Type'", &err);
  48.  
  49.     dbus_connection_flush(conn);
  50.     if(dbus_error_is_set(&err)) {
  51.         printf("dbus_bus_add_match err (%s)", err.message);
  52.         return RES_FAILED;
  53.     }
  54.  
  55.     while(1) {
  56.         dbus_connection_read_write(conn, 0);
  57.         msg = dbus_connection_pop_message(conn);
  58.  
  59.         if(NULL == msg) {
  60.             sleep(1);
  61.             continue;
  62.         }
  63.  
  64.         if(dbus_message_is_signal(msg, "test.signal.Type""Test")) {
  65.             if(!dbus_message_iter_init(msg, &args)) {
  66.                 printf("dbus_message_iter_init error, msg has no arguments!\n");
  67.             }
  68.             else if (DBUS_TYPE_UINT32 != dbus_message_iter_get_arg_type(&args)){
  69.                 printf("not a uint 32 type !\n");
  70.             }
  71.             else {
  72.                 dbus_uint32_t my_age = 0;
  73.                 dbus_message_iter_get_basic(&args, &my_age);
  74.                 printf("Got signal with value %d\n", my_age);
  75.             }
  76.         }
  77.  
  78.         dbus_message_unref(msg);
  79.  
  80.     }
  81.  
  82.     return 0;
  83. }

參考資料:

  1. http://dbus.freedesktop.org/doc/dbus-specification.html  這固然是最權威最重要的資料,但我以爲不是一個很好的入門資料。
  2. http://dbus.freedesktop.org/doc/dbus-tutorial.html 這裏面有一些不錯的例子,對Names 的解釋也很好,但用的是 glib 的 binding,不能探究更底層的動做一度仍是讓我雲裏霧裏。
  3. http://dbus.freedesktop.org/doc/api/html/group__DBusMessage.html  DBus 的 C 編程接口的在線文檔,很是棒也很是有用
  4. http://dbus.freedesktop.org/doc/dbus/libdbus-tutorial.html 如何用 C API 層面的 DBus ,相見恨晚。
相關文章
相關標籤/搜索