转载请注明出处 作者: 唐风
通信数据的设置和获取
前篇主要是有讲一些相对高层的概念,比如 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 ,相见恨晚。