轉載請注明出處 作者: 唐風
通信數據的設置和獲取
前篇主要是有講一些相對高層的概念,比如 object,interface,method 之類的,對於這些“C 本來沒有的東西”,如何在 DBus 中表現的確實很讓我迷惑了一陣。但通信數據的發送可能比前面那些名稱好理解得多。因為這些概念都是很本來就是底層的,很 C 的。
DBus 提供了一個 DBusMessageIter 的類型,使用這個類型的變量,我們就可以向 DBusMessage 中很容易地加入數據,也可以很容易地從中取出數據。
- DBusMessage* msg;
- DBusMessageIter args;
- // msg...
- dbus_message_iter_init_append(msg, &args);
- dbus_uint32_t my_data = 10;
- if(!dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &my_data)) {
- printf("Out of memory\n");
- return RES_FAILED;
- }
- dbus_connection_flush(conn);
- dbus_message_unref(msg);
第 2 行的代碼聲明了一個 DBusMessageIter 的對象 args,第 6 行的代碼處,對 args 進行初始化,這可以讓一個 DBusMessageIter 對象與對應的 DBusMessage 關聯起來,后面再對 DBusMessageIter的時候(設置或者取得數據),就是對相應的 DBusMessage 進行處理。然后使用第8行的函數,將一個 uint32 的數據 my_data 追加到 msg 中了。如果還要追加新的參數的話,只需要繼續調用該函數,並傳入適當的參數就可以了。
- dbus_bool_t dbus_message_iter_append_basic(DBusMessageIter * iter,
- inttype,
- const void * value
- )
這個函數可以用來向 DBusMessageIter 中追加一些“基本類型”(basic)的數據,所謂基本類型的數據,在 DBus 中是這么定義的:
Conventional Name |
Encoding |
Alignment |
BYTE |
A single 8-bit byte. |
1 |
BOOLEAN |
As for |
4 |
INT16 |
16-bit signed integer in the message's byte order. |
2 |
UINT16 |
16-bit unsigned integer in the message's byte order. |
2 |
INT32 |
32-bit signed integer in the message's byte order. |
4 |
UINT32 |
32-bit unsigned integer in the message's byte order. |
4 |
INT64 |
64-bit signed integer in the message's byte order. |
8 |
UINT64 |
64-bit unsigned integer in the message's byte order. |
8 |
DOUBLE |
64-bit IEEE 754 double in the message's byte order. |
8 |
STRING |
A |
4 (for the length) |
OBJECT_PATH | Exactly the same as STRING except the content must be a valid object path (see below). |
4 (for the length) |
(參考 DBus 的在線 API 的 Marshaling (Wire Format) 一節)
有 basic type,當然也就有更復雜的不是 basic 的類型,但這和基本概念的關系不大,在這篇文章中就不多介紹了。(請參考我其它的 DBus 博文)
到現在為止,我已經知道如何把數據入到一個 DBusMessage 中了,那么,如何從一個 DBusMessage 中取出數據呢?比如,我在 A 進程使用上面的代碼把 my_data 加到 DBusMessage 中了,現在 B 進程取到了 DBusMessage,如何把數據取出來呢?
- DBusMessage* msg;
- DBusMessageIter args;
- // get a DBusMessage from process A
- if(!dbus_message_iter_init(msg, &args)) {
- printf("dbus_message_iter_init error, msg has no arguments!\n");
- }
- else if (DBUS_TYPE_UINT32 != dbus_message_iter_get_arg_type(&args)){
- printf("not a uint 32 type !\n");
- }
- else {
- dbus_uint32_t my_age = 0;
- dbus_message_iter_get_basic(&args, &my_age);
- printf("Got signal with value %d\n", my_age);
- }
很簡單,一樣的先使用 dbus_messge_iter_init 先把 DBusMessage 對象和從 DBus 總線中取到的 msg 關聯起來。這樣,使用第 9 行的函數先取得第一個通信數據中第一個參數的類型,如果類型無誤的話可以進而使用第 14 行的函數取得參數值本身。
這樣,一個簡單的數據如何入到 DBusMessage 中,又如何從 DBusMessage 中取出來就明白了。那么如何將 DBusMessage 在進程之間傳遞呢?
消息的發送和獲取
消息的發送其實比較簡單,當進程 A 准備申請好一個 DBusMessage對象,設置好它的“類型”(就是各種名字),放好需要通信的數據,之后,使用下面的代碼就可以將數據發送到總線上:
- dbus_uint32_t serial = 0;
- if(!dbus_connection_send(conn, msg, &serial)) {
- printf("Out of memory");
- return RES_FAILED;
- }
- dbus_connection_flush(conn);
這很簡單,只是 dbus_connection_flush 這個函數有點突兀,它的作用是“Blocks until the outgoing message queue is empty.”,可以簡單地理解為調用這個函數可以使用得發送進程一直等消息發送完了才能繼續運行。
接受方的代碼也很簡單:
- dbus_connection_read_write(conn, 0);
- msg = dbus_connection_pop_message(conn);
- if(NULL == msg) {
- sleep(1);
- continue;
- }
使用 1 和 2 行的代碼就可以取出發送到本進程的消息,之后就可以使用 msg (如果 msg 不是 NULL 的話)來獲取通信數據了。
到這里,基本概念就有了。后面,應該對 DBus 的細節再深入的探索。
Sample 代碼:
發送方進程(my_client):
- #include <stdio.h>
- #include <stdlib.h>
- #include <dbus/dbus.h>
- #include <unistd.h>
- const int RES_SUCCESS = -1;
- const int RES_FAILED = 0;
- int my_dbus_initialization(char const * _bus_name, DBusConnection ** _conn) {
- DBusError err;
- int ret;
- dbus_error_init(&err);
- *_conn = dbus_bus_get(DBUS_BUS_SESSION, &err);
- if(dbus_error_is_set(&err)) {
- printf("Connection Error\n");
- dbus_error_free(&err);
- return RES_FAILED;
- }
- ret = dbus_bus_request_name(*_conn, _bus_name, DBUS_NAME_FLAG_REPLACE_EXISTING, &err);
- if(dbus_error_is_set(&err)){
- printf("Requece name error \n");
- dbus_error_free(&err);
- return RES_FAILED;
- }
- if(DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER != ret) {
- return RES_FAILED;
- }
- return RES_SUCCESS;
- }
- int my_dbus_send_sigal(DBusConnection * conn) {
- dbus_uint32_t serial = 0;
- DBusMessage* msg;
- DBusMessageIter args;
- char sigvalue[20] = "liyiwen";
- msg = dbus_message_new_signal("/test/signal/Object", // object name
- "test.signal.Type", // interface name
- "Test"); // name of signal
- if (NULL == msg) {
- printf("Message Null");
- return RES_FAILED;
- }
- dbus_message_iter_init_append(msg, &args);
- printf("%s\n", sigvalue);
- dbus_uint32_t my_age = 10;
- if(!dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &my_age)) {
- printf("Out of memory\n");
- return RES_FAILED;
- }
- if(!dbus_connection_send(conn, msg, &serial)) {
- printf("Out of memory");
- return RES_FAILED;
- }
- dbus_connection_flush(conn);
- dbus_message_unref(msg);
- return RES_SUCCESS;
- }
- int main(int agrc, char** argv)
- {
- DBusConnection * conn;
- printf("Start\n");
- if (RES_FAILED == my_dbus_initialization("test.method.client", &conn)) {
- exit(1);
- }
- my_dbus_send_sigal(conn);
- while(1){sleep(10);}
- return 0;
- }
接收方進程(my_server.cpp)
- #include <stdio.h>
- #include <stdlib.h>
- #include <dbus/dbus.h>
- #include <unistd.h>
- const int RES_SUCCESS = -1;
- const int RES_FAILED = 0;
- int my_dbus_initialization(char const * _bus_name, DBusConnection **_conn) {
- DBusError err;
- int ret;
- dbus_error_init(&err);
- *_conn = dbus_bus_get(DBUS_BUS_SESSION, &err);
- if(dbus_error_is_set(&err)) {
- printf("Connection Error(%s) \n", err.message);
- dbus_error_free(&err);
- return RES_FAILED;
- }
- ret = dbus_bus_request_name(*_conn, _bus_name, DBUS_NAME_FLAG_REPLACE_EXISTING, &err);
- if(dbus_error_is_set(&err)){
- printf("Requece name error(%s) \n", err.message);
- dbus_error_free(&err);
- return RES_FAILED;
- }
- if(DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER != ret) {
- return RES_FAILED;
- }
- return RES_SUCCESS;
- }
- int main(int agrc, char** argv)
- {
- DBusError err;
- DBusMessage* msg;
- DBusMessageIter args;
- dbus_error_init(&err);
- DBusConnection *conn;
- if (RES_FAILED == my_dbus_initialization("test.method.server", &conn)) {
- exit(1);
- }
- dbus_bus_add_match(conn, "type='signal', interface='test.signal.Type'", &err);
- dbus_connection_flush(conn);
- if(dbus_error_is_set(&err)) {
- printf("dbus_bus_add_match err (%s)", err.message);
- return RES_FAILED;
- }
- while(1) {
- dbus_connection_read_write(conn, 0);
- msg = dbus_connection_pop_message(conn);
- if(NULL == msg) {
- sleep(1);
- continue;
- }
- if(dbus_message_is_signal(msg, "test.signal.Type", "Test")) {
- if(!dbus_message_iter_init(msg, &args)) {
- printf("dbus_message_iter_init error, msg has no arguments!\n");
- }
- else if (DBUS_TYPE_UINT32 != dbus_message_iter_get_arg_type(&args)){
- printf("not a uint 32 type !\n");
- }
- else {
- dbus_uint32_t my_age = 0;
- dbus_message_iter_get_basic(&args, &my_age);
- printf("Got signal with value %d\n", my_age);
- }
- }
- dbus_message_unref(msg);
- }
- return 0;
- }
參考資料:
- http://dbus.freedesktop.org/doc/dbus-specification.html 這當然是最權威最重要的資料,但我覺得不是一個很好的入門資料。
- http://dbus.freedesktop.org/doc/dbus-tutorial.html 這里面有一些不錯的例子,對Names 的解釋也很好,但用的是 glib 的 binding,不能探究更底層的動作一度還是讓我雲里霧里。
- http://dbus.freedesktop.org/doc/api/html/group__DBusMessage.html DBus 的 C 編程接口的在線文檔,非常棒也非常有用
- http://dbus.freedesktop.org/doc/dbus/libdbus-tutorial.html 如何用 C API 層面的 DBus ,相見恨晚。