D-Bus最主要的用途是在 Linux 桌面环境为进程提供通信,同时能将 Linux 桌面环境和 Linux 内核事件作为消息传递到进程。D-Bus(其中D原先是代表桌面“Desktop” 的意思),即:用于桌面操作系统的通信总线。
D-Bus的主要概念为总线,注册后的进程可通过总线接收或传递消息,进程也可注册后等待内核事件响应,例如等待网络状态的转变或者计算机发出关机指令。
D-Bus是为Linux系统开发的进程间通信(IPC)和远程过程调用(RPC)机制,使用统一的通信协议来代替现有的各种IPC解决方案。D-Bus允许系统级进程(如:打印机和硬件驱动服务)和普通用户进程进行通信。
2. D-Bus特性D-Bus使用一个快速的二进制消息传递协议,D-Bus协议的低延迟和低消耗特点适用于同一台机器的通信。D-Bus的规范目前由freedesktop.org项目定义,可供所有团体使用。
D-Bus不和低层的IPC直接竞争,比如sockets,shared memory或message queues。低层IPC有自己的特点,和D-Bus并不冲突。
与其他重量级的进程间通信技术不同,D-Bus是非事务的。D-Bus使用了状态以及连接的概念,比UDP等底层消息传输协议更“聪明”。但另一方面,D-Bus传送的是离散消息,与TCP协议将数据看做“流”有所不同。D-Bus支持点对点的消息传递以及广播/订阅式的通信。
总结如下:
二、D-Bus架构 1. 结构层次1、D-BUS的协议是低延迟而且低开销的,设计小巧且高效,以便最小化传送时间。从设计上避免往返交互并允许异步操作。
2、协议是二进制的,而不是文本,排除序列化过程。
3、考虑了字节序问题。
4、易用性:按照消息而不是字节流来工作,并且自动地处理了许多困难的IPC问题,并且D-Bus库以可以封装的方式来设计,开发者可以使用框架里存在的对象/类型系统,而不用学习一种新的专用于IPC的对象/类型系统。
5、请求时启动服务以及安全策略。
6、支持多语言(C/C++/Java/C#/Python/Ruby),多平台(Linux/windows/maemo)。
7、采用C语言,而不是C++。 H、由于基本上不用于internet上的IPC,因此对本地IPC进行了特别优化。
8、提供服务注册,理论上可以进行无限扩展。
9、支持广播类型的通信。
10、带有异常处理的通用远程调用接口;
D-Bus进程间通信主要有三层架构:
1.底层接口层:主要是通过libdbus这个函数库,给予系统使用DBus的能力。
2.总线层:主 要Message bus daemon这个总线守护进程提供的,在Linux系统启动时运行,负责进程间的消息路由和传递,其中包括Linux内核和Linux桌面环境的消息传递。总线守护进程可同时与多个应用程序相连,并能把来自一个应用程序的消息路由到0或者多个其他程序。
3.应用封装层:通过一系列基于特定应用程序框架将DBus的底层接口封装成友好的Wrapper库,供不同开发人员使用。比如libdbus-glib, libdbus-python.
2.系统总线和会话总线在一台机器上总线守护有多个实例(instance)。这些总线之间都是相互独立的。
-
一个持久的系统总线(system bus)
它在引导时就会启动。这个总线由操作系统和后台进程使用,安全性非常好,以使得任意的应用程序不能欺骗系统事件。 它是桌面会话和操作系统的通信,这里操作系统一般而言包括内核和系统守护进程。 这种通道的最常用的方面就是发送系统消息,比如:插入一个新的存储设备;有新的网络连接;等等。
-
还将有很多会话总线(session buses)‘
普通进程创建,可同时存在多条。会话总线属于某个进程私有,它用于进程间传递消息。
使用d-bus的应用程序既可以是server也可以是client,server监听到来的连接,client连接到 server,一旦连接建立,消息就可以流转。点对点通信时就是一个 server 和 一个 client;如果使用dbus daemon,所有的应用程序都是client,bus daemon 是server,daemon监听所有的连接,应用程序初始化连接到daemon。
dbus地址指明server将要监听的地方,client将要连接的地方,例如,地址:unix:path=/tmp/abcdef表明 server将在/tmp/abcdef路径下监听unix域的socket,client也将连接到这个socket。一个地址也可以指明是 TCP/IP的socket,或者是其他的。
当使用bus daemon时,libdbus会从环境变量中(DBUS_SESSION_BUS_ADDRESS)自动认识“会话daemon”的地址。如果是系统 daemon,它会检查指定的socket路径获得地址,也可以使用环境变量(DBUS_SESSION_BUS_ADDRESS)进行设定。
2. bus name 总线名字当一个应用连接到 bus daemon,daemon 立即会分配一个名字给这个连接,称为 Unique Connection Name, 这个唯一标识的名字以冒号 “:” 开头,例如 :1.2,这个名字在 daemon 的整个生命周期是唯一的。
但是这种名字总是临时分配,无法确定的,也难以记忆,因此应用可以要求有另外一个公共名 well-known name 来对应这个唯一标识,就像我们使用域名来映射 IP地址一样。例如可以使用 org.fmddlmyy.Test 来映射 :1.2。这样我们就可以使用公共名连接到 DBus 服务。
3. 原生对象和对象路径d-bus 的底层接口是没有这些对象的概念的,它提供的是一种叫对象路径(object path),用于让高层接口绑定到各个对象中去,允许远端应用程序指向它们。object path就像是一个文件路径,可以叫做 /org/kde/kspread/sheets/3/cells/4/5 等。
4. 接口 Interface接口是一组方法和信号,每一个对象支持一个或者多个接口,接口定义一个对象实体的类型。 D-Bus使用简单的命名空间字符串来表示接口,例如 org.freedesktop.Introspectable。
5. Methods 和 Signals每一个对象有两类成员:方法和信号:
- 方法就是一个函数,具有有输入和输出;
- 信号会被广播,感兴趣的对象可以处理这个 信号,同时信号中也可以带有相关的数据。
在 D-BUS 中有四种类型的消息:
1、方法调用(method call) # 在对象上执行一个方法
2、方法返回(method return) # 返回方法执行的结果
3、信号(signal) # 调用方法产生的异常
4、错误(error)# 通知指定的信号发生了,可以想象成“事件”。
要执行D-BUS对象的方法,您需要向对象发送一个方法调用消息。 它将完成一些处理(就是执行了对象中的Method,Method是可以带有输入参数的)并返回,返回消息或者错误消息。 信号的不同之处在于它们不返回任何内容:既没有“信号返回”消息,也没有任何类型的错误消息。
6. 总结:方法所需要的参数要在指定的对象中调用指定的方法,需要知道的参数如下:
Address -> [Bus Name] -> Path -> Interface -> Method
bus name是可选的,除非是希望把消息送到特定的应用中才需要。interface也是可选的,有一些历史原因,DCOP不需要指定接口,因为DCOP在同一个对象中禁止同名的方法。
四、D-Bus运行环境 1. 运行环境安装# 安装依赖 sudo apt-get install dbus sudo apt-get install libgtk2.0-dev sudo apt-get install libdbus-glib-1-dev # 安装 D-Feet sudo apt-get install d-feet
同时下载一个简单的 DBus 程序:链接,运行方法:
tar -zxvf hello-dbus3-0.1.tar.gz # 编译 cd hello-dbus3-0.1/ ./autogen.sh ./configure make # 运行 cd src ./example-service
运行 d-feet,打开 Session bus,找到 “org.fmddlmyy.Test” 连接名,这个链接就是我们刚刚运行的一个D-Bus程序:
在右侧展开栏我们会发现 org.fmddlmyy.Test.Basic 下有一个 Add 方法,我们点击它,输入 1,2,点击执行,可以看到给我们返回了结果:
通过上面的操作我们通过 d-feet 发起了一次 d-bus 请求。
2. dbus-send以及dbus-monitordbus提供了两个小工具:dbus-send和dbus-monitor。我们可以用dbus-send发送消息。用dbus-monitor监视总线上流动的消息。 让我们通过dbus-send发送消息来调用前面的Add方法,这时dbus-send充当了应用程序B。用dbus-monitor观察调用过程中的消息
保持上面的example-server运行,新建两个窗口一个运行dubs-monitor一个运行如下命令
dbus-send --session --type=method_call --print-reply --dest=org.fmddlmyy.Test /TestObj org.fmddlmyy.Test.Basic.Add int32:100 int32:999
-
dbus-send的详细用法可以参阅手册。调用远程方法的一般形式是
$ dbus-send [--system | --session] --type=method_call --print-reply --dest=连接名 对象路径 接口名.方法名 参数类型:参数值 参数类型:参数值
dbus-send支持的参数类型包括:string, int32, uint32, double, byte, boolea
dubs-monitor窗口返回的消息如下:
# 1、向 D-Bus Damon 发送Hello建立连接 method call time=1653831830.713656 sender=:1.97 -> destination=org.freedesktop.DBus serial=1 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=Hello method return time=1653831830.713685 sender=org.freedesktop.DBus -> destination=:1.97 serial=1 reply_serial=1 string ":1.97" # 2、广播全局,指定名称的拥有者发生了变化 signal time=1653831830.713696 sender=org.freedesktop.DBus -> destination=(null destination) serial=10 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameOwnerChanged string ":1.97" # 名字 string "" # 拥有者原始名字 string ":1.97" # 拥有者现在名字 # 3、通知应用获得了指定名称的拥有权 signal time=1653831830.713712 sender=org.freedesktop.DBus -> destination=:1.97 serial=2 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameAcquired string ":1.97" # 4、调用 Add 方法 method call time=1653831830.714160 sender=:1.97 -> destination=org.fmddlmyy.Test serial=2 path=/TestObj; interface=org.fmddlmyy.Test.Basic; member=Add int32 123 int32 456 # 5、Add 方法返回结果 method return time=1653831830.714310 sender=:1.95 -> destination=:1.97 serial=7 reply_serial=2 int32 579 # 6、通知应用失去了指定名称的拥有权 signal time=1653831830.715690 sender=org.freedesktop.DBus -> destination=:1.97 serial=6 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameLost string ":1.97" # 7、广播全局,指定名称的拥有者发生了变化 signal time=1653831830.715717 sender=org.freedesktop.DBus -> destination=(null destination) serial=11 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameOwnerChanged string ":1.97" string ":1.97" string ""
上述一次完整的 d-bus 通信的流程
3.Dbus通信流程总结 (1) 方法调用的一般流程:1.使用不同语言绑定的dbus高层接口,都提供了一些代理对象,调用其他进程里面的远端对象就像是在本地进程中的调用一样。应用调用代理上的方法,代理将构造一个方法调用消息给远端的进程。
2.在DBUS的底层接口中,应用需要自己构造方法调用消息(method call message),而不能使用代理。
3.方法调用消息里面的内容有:目的进程的bus name,方法的名字,方法的参数,目的进程的对象路径,以及可选的接口名称。
4.方法调用消息是发送到bus daemon中的。
5.bus daemon查找目标的bus name,如果找到,就把这个方法发送到该进程中,否则,daemon会产生错误消息,作为应答消息给发送进程。
6. 目标进程解开消息,在dbus底层接口中,会立即调用方法,然后发送方法的应答消息给daemon。在dbus高层接口中,会先检测对象路径,接口, 方法名称,然后把它转换成对应的对象(如GObject,QT中的QObject等)的方法,然后再将应答结果转换成应答消息发给daemon。
7.bus daemon接受到应答消息,将把应答消息直接发给发出调用消息的进程。
8.应答消息中可以包容很多返回值,也可以标识一个错误发生,当使用绑定时,应答消息将转换为代理对象的返回值,或者进入异常
1.当使用dbus底层接口时,信号需要应用自己创建和发送到daemon,使用dbus高层接口时,可以使用相关对象进行发送,如Glib里面提供的信号触发机制。
2.信号包含的内容有:信号的接口名称,信号名称,发送进程的bus name,以及其他参数。
3.任何进程都可以依据”match rules”注册相关的信号,daemon有一张注册的列表。
4.daemon检测信号,决定哪些进程对这个信号感兴趣,然后把信号发送给这些进程。
5.每个进程收到信号后,如果是使用了dbus高层接口,可以选择触发代理对象上的信号。如果是dbus底层接口,需要检查发送者名称和信号名称,然后决定怎么做。
首先,客户端必须要连接上 Dbus,一般来说,系统中会有一个 System Bus 和一个 Session Bus。其次,你需要在 Dbus 中注册一个名字,用于标识自己。为了简单起见,这里先不考虑重名的情况:
DBusError err;
DBusConnection* conn;
int ret;
// initialise the errors
dbus_error_init(&err);
// connect to the bus
conn = dbus_bus_get(DBUS_BUS_SESSION, &err);
if (dbus_error_is_set(&err)) {
fprintf(stderr, "Connection Error (%s)n", err.message);
dbus_error_free(&err);
}
if (NULL == conn) {
exit(1);
}
// request a name on the bus
ret = dbus_bus_request_name(conn, "test.method.server",
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 (DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER != ret) {
exit(1);
}
一般来说,连接上 Dbus 和注册一个名称,应该是在程序最开始运行的时候就会进行的操作。
当然,在程序结束的时候,需要关闭掉与 Dbus 的连接。使用下面的函数:
dbus_connection_close(conn);(2) 发送信号(Sending Signal)
信号是一种广播的消息,你可以简单的发出一个信号,这样,所有连接在 DBus 总线上并注册了接受对应信号的进程,都会收到这个信号。
为了发出一个信号,需要的只是创建一个 DBusMessage 对象来代表信号,然后追加上一些需要发出的参数,就可以发向总线了。
发完之后还需要释放掉 Message。如果内存不足的话,这下面不少函数都会返回 false,所以一般情况下你都需要处理这些情况的返回值。
dbus_uint32_t serial = 0; // unique number to associate replies with requests
DBusMessage* msg;
DBusMessageIter args;
// create a signal and check for errors
msg = dbus_message_new_signal("/test/signal/Object", // object name of the signal
"test.signal.Type", // interface name of the signal
"Test"); // name of the signal
if (NULL == msg)
{
fprintf(stderr, "Message Nulln");
exit(1);
}
// append arguments onto signal
dbus_message_iter_init_append(msg, &args);
if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &sigvalue)) {
fprintf(stderr, "Out Of Memory!n");
exit(1);
}
// send the message and flush the connection
if (!dbus_connection_send(conn, msg, &serial)) {
fprintf(stderr, "Out Of Memory!n");
exit(1);
}
dbus_connection_flush(conn);
// free the message
dbus_message_unref(msg);
(3) 调用方法(Call a Methond)
调用一个远程方法(remote method)与发送一个信号(sending a signal)是很类似的。需要创建一个 DBusMessage,然后通过注册在 DBus 上的名称指定发送的对象,追加相应的参数,但调用方法分为两种,一种是阻塞式的,另一种则可以异步调用。异步调用的时候会得到一个 DBusMessage* 的返回,从这个 DBusMessage 中可以获取一些返回的参数。
调用方法1DBusMessage* msg;
DBusMessageIter args;
DBusPendingCall* pending;
msg = dbus_message_new_method_call("test.method.server", // target for the method call
"/test/method/Object", // object to call on
"test.method.Type", // interface to call on
"Method"); // method name
if (NULL == msg) {
fprintf(stderr, "Message Nulln");
exit(1);
}
// append arguments
dbus_message_iter_init_append(msg, &args);
if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, ¶m)) {
fprintf(stderr, "Out Of Memory!n");
exit(1);
}
// send message and get a handle for a reply
if (!dbus_connection_send_with_reply (conn, msg, &pending, -1)) { // -1 is default timeout
fprintf(stderr, "Out Of Memory!n");
exit(1);
}
if (NULL == pending) {
fprintf(stderr, "Pending Call Nulln");
exit(1);
}
dbus_connection_flush(conn);
// free message
dbus_message_unref(msg);
调用方法2
bool stat;
dbus_uint32_t level;
// block until we receive a reply
dbus_pending_call_block(pending);
// get the reply message
msg = dbus_pending_call_steal_reply(pending);
if (NULL == msg) {
fprintf(stderr, "Reply Nulln");
exit(1);
}
// free the pending message handle
dbus_pending_call_unref(pending);
// read the parameters
if (!dbus_message_iter_init(msg, &args))
fprintf(stderr, "Message has no arguments!n");
else if (DBUS_TYPE_BOOLEAN != dbus_message_iter_get_arg_type(&args))
fprintf(stderr, "Argument is not boolean!n");
else
dbus_message_iter_get_basic(&args, &stat);
if (!dbus_message_iter_next(&args))
fprintf(stderr, "Message has too few arguments!n");
else if (DBUS_TYPE_UINT32 != dbus_message_iter_get_arg_type(&args))
fprintf(stderr, "Argument is not int!n");
else
dbus_message_iter_get_basic(&args, &level);
printf("Got Reply: %d, %dn", stat, level);
// free reply and close connection
dbus_message_unref(msg);
(4) 接收消息(Receiving a Signal)
接下来的两种操作是从总线读取消息并处理这些消息。
要接收一个消息,你首先需要告诉 DBus 你对什么样的消息感兴趣:
// add a rule for which messages we want to see
dbus_bus_add_match(conn,
"type='signal',interface='test.signal.Type'",
&err); // see signals from the given interface
dbus_connection_flush(conn);
if (dbus_error_is_set(&err)) {
fprintf(stderr, "Match Error (%s)n", err.message);
exit(1);
}
然后,进程就可以在一个循环中等待这类消息的发生了:
/ loop listening for signals being emmitted
while (true) {
// non blocking read of the next available message
dbus_connection_read_write(conn, 0);
msg = dbus_connection_pop_message(conn);
// loop again if we haven't read a message
if (NULL == msg) {
sleep(1);
continue;
}
// check if the message is a signal from the correct interface and with the correct name
if (dbus_message_is_signal(msg, "test.signal.Type", "Test")) {
// read the parameters
if (!dbus_message_iter_init(msg, &args))
fprintf(stderr, "Message has no arguments!n");
else if (DBUS_TYPE_STRING != dbus_message_iter_get_arg_type(&args))
fprintf(stderr, "Argument is not string!n");
else {
dbus_message_iter_get_basic(&args, &sigvalue);
printf("Got Signal with value %sn", sigvalue);
}
}
// free the message
dbus_message_unref(msg);
}
(5) 提供被远程调用的方法(Exposing a Method to be called)
在(3) 调用方法(Call a Methond)中,我们看到了调用一个远程方法,这节就是告诉我们怎么样提供一个方法让别的应用程序调用。用下面的程序,就可以把方法关联在那些提供给外部的方法上,并解析出相应的参数,最后构建一个消息返回给调用方法的应用程序。
远程调用的方法1// loop, testing for new messages
while (true) {
// non blocking read of the next available message
dbus_connection_read_write(conn, 0);
msg = dbus_connection_pop_message(conn);
// loop again if we haven't got a message
if (NULL == msg) {
sleep(1);
continue;
}
// check this is a method call for the right interface and method
if (dbus_message_is_method_call(msg, "test.method.Type", "Method"))
reply_to_method_call(msg, conn);
// free the message
dbus_message_unref(msg);
}
远程调用方法2:
void reply_to_method_call(DBusMessage* msg, DBusConnection* conn)
{
DBusMessage* reply;
DBusMessageIter args;
DBusConnection* conn;
bool stat = true;
dbus_uint32_t level = 21614;
dbus_uint32_t serial = 0;
char* param = "";
// read the arguments
if (!dbus_message_iter_init(msg, &args))
fprintf(stderr, "Message has no arguments!n");
else if (DBUS_TYPE_STRING != dbus_message_iter_get_arg_type(&args))
fprintf(stderr, "Argument is not string!n");
else
dbus_message_iter_get_basic(&args, ¶m);
printf("Method called with %sn", param);
// create a reply from the message
reply = dbus_message_new_method_return(msg);
// add the arguments to the reply
dbus_message_iter_init_append(reply, &args);
if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_BOOLEAN, &stat)) {
fprintf(stderr, "Out Of Memory!n");
exit(1);
}
if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &level)) {
fprintf(stderr, "Out Of Memory!n");
exit(1);
}
// send the reply && flush the connection
if (!dbus_connection_send(conn, reply, &serial)) {
fprintf(stderr, "Out Of Memory!n");
exit(1);
}
dbus_connection_flush(conn);
// free the reply
dbus_message_unref(reply);
}
2. 实例代码
(1) 客户端代码
client.c
#include(2) 服务端代码#include #include #include #include DBusConnection* init_bus() { DBusConnection *connection; DBusError err; int ret; dbus_error_init(&err); //连接到dbus,建立一个连接,称为对象 connection = dbus_bus_get(DBUS_BUS_SESSION, &err); if(dbus_error_is_set(&err)) { printf("connection error: :%s -- %sn", err.name, err.message); dbus_error_free(&err); return NULL; } //为这个对象分配一个名字 ret = dbus_bus_request_name(connection, "hello.world.client", DBUS_NAME_FLAG_REPLACE_EXISTING, &err); if(dbus_error_is_set(&err)) { printf("Name error: %s -- %sn", err.name, err.message); dbus_error_free(&err); return NULL; } if(ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) return NULL; return connection; } void send_signal(DBusConnection *connection) { DBusMessage *msg; DBusMessageIter arg; char *str = "hello world!"; //创建一个signal对象 //param1: path (这个逻辑来说,可以是任何字符串,只要符合规则即可) //param2: interface (一样) //param3: 信号方法名(必须与服务端名匹配) if((msg = dbus_message_new_signal("/hello", "aa.bb.cc", "alarm_test")) == NULL) { printf("message is NULLn"); return; } #if 0 //这个看需求添加,一般来说,信号是一种单向广播,加上这一句变单向单播 //param2: bus_name if(!dbus_message_set_destination(msg, "hello.world.service")) { printf("memory errorn"); } #endif //添加参数的一些接口 dbus_message_iter_init_append(msg, &arg); dbus_message_iter_append_basic(&arg, DBUS_TYPE_STRING, &str); //入队 dbus_connection_send(connection, msg, NULL); //发送 dbus_connection_flush(connection); //释放内存 dbus_message_unref(msg); return; } void send_method_call(DBusConnection *connection) { DBusMessage *msg; DBusMessageIter arg; DBusPendingCall *pending; int a = 100; int b = 99; int sum; msg = dbus_message_new_method_call("hello.world.service", "/hello/world","hello.world", "add"); if(msg == NULL) { printf("no memoryn"); return; } dbus_message_iter_init_append(msg, &arg); if(!dbus_message_iter_append_basic (&arg, DBUS_TYPE_INT32,&a)){ printf("no memory!"); dbus_message_unref(msg); return; } if(!dbus_message_iter_append_basic (&arg, DBUS_TYPE_INT32,&b)){ printf("no memory!"); dbus_message_unref(msg); return; } //入队message,等待回复 //param1: 连接描述符 //param2: message //param3: 相当于一个回调的一个描述符,为了获了返回的消息 //param4: 超时间. -1代表无限 if(!dbus_connection_send_with_reply (connection, msg, &pending, -1)){ printf("no memeory!"); dbus_message_unref(msg); return; } if(pending == NULL){ printf("Pending is NULL, may be disconnect...n"); dbus_message_unref(msg); return; } //send dbus_connection_flush(connection); dbus_message_unref(msg); //阻塞,直到接收到一个响应. dbus_pending_call_block (pending); msg = dbus_pending_call_steal_reply (pending); if (msg == NULL) { printf("reply is null. errorn"); return; } //释放pending内存 dbus_pending_call_unref(pending); //解析参数 if (!dbus_message_iter_init(msg, &arg)) printf("no argument, errorn"); if(dbus_message_iter_get_arg_type(&arg) != DBUS_TYPE_INT32) { printf("paramter type errorn"); } dbus_message_iter_get_basic(&arg, &sum); printf(" a(%d) + b(%d) = %dn",a, b, sum); dbus_message_unref(msg); return; } int main(int argc, char **argv) { DBusConnection *connection; connection = init_bus(); if(connection == NULL) { printf("connect to bus failed...n"); return -1; } //发送一个信号及一个方法调用 send_signal(connection); send_method_call(connection); return 0; }
service.c
#include(3)执行结果 客户端 服务端#include #include #include #include DBusConnection* init_bus() { DBusConnection *connection; DBusError err; int ret = 0; dbus_error_init(&err); //与session dbus 建立连接 //param1:bus type = {DBUS_BUS_SESSION, DBUS_BUS_SYSTEM} 一个系统dbus, 一个普通用户dbus //param2:错误信息,包括错误名与错误信息. connection = dbus_bus_get(DBUS_BUS_SESSION, &err); if(dbus_error_is_set(&err)) { printf("Connection Error: %s--%sn", err.name, err.message); dbus_error_free(&err); return NULL; } //为连接设置一个bus name: bus_name; //param 1: 连接描述符 //param 2: 请求bus要分配的bus name(逻辑上讲,bus name可以是任何字符串,只要符合命名规则) //param 3: flags ={DBUS_NAME_FLAG_REPLACE_EXISTING, // DBUS_NAME_FLAG_ALLOW_REPLACEMENT, // DBUS_NAME_FLAG_DO_NOT_QUEUE // } //param 4: err info ret = dbus_bus_request_name(connection, "hello.world.service", DBUS_NAME_FLAG_REPLACE_EXISTING, &err); if(dbus_error_is_set(&err)) { printf("Name Error: %s--%sn", err.name, err.message); dbus_error_free(&err); return NULL; } if(ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) return NULL; //注册感兴趣的signal: 来自接口dbus.test.signal.sender //param1: 连接描述符 //param2: match rule (常用的类型: sender= // interface= // type= // member= ) //param3: err info //只设置一个type = signal,表示所有信号都接受.也可以加上接口,发送者bus_name dbus_bus_add_match(connection, "type='signal'", &err); //阻塞,直到消息发送成功. dbus_connection_flush(connection); if(dbus_error_is_set(&err)) { printf("add Match Error %s--%sn", err.name, err.message); dbus_error_free(&err); return connection; } return connection; } void handle_message(DBusConnection *connection) { DBusMessage *msg; DBusMessageIter arg; char *str; while(1) { //param1: 连接描述符 //param2: 超时时间, -1无限超时时间 dbus_connection_read_write(connection, 0); //从队列中取出一条消息 msg = dbus_connection_pop_message(connection); if(msg == NULL) { sleep(1); continue; } //这里应该过滤path,暂且不做 //打印出消息对象路径 printf("path: %sn", dbus_message_get_path (msg)); //param1: message //param2: interface 这个名字必须与发送那个接口一样.才能处理 //param3: singal name 方法名也必须一样. if(dbus_message_is_signal(msg, "aa.bb.cc", "alarm_test")) { //解析message 参数,0为无参数. if(!dbus_message_iter_init(msg, &arg)) { printf("no argumentn"); } //获取第一个参数类型 if(dbus_message_iter_get_arg_type(&arg) != DBUS_TYPE_INVALID) { //获取参数的值 dbus_message_iter_get_basic(&arg,&str); printf("recv param --: %sn", str); } } else if(dbus_message_is_method_call(msg, "hello.world", "add")) {//处理 add 远程调用. DBusMessage *rp; DBusMessageIter r_arg; int a = 0; int b = 0; int sum = 0; printf("service: add functionn"); if(!dbus_message_iter_init(msg, &arg)) { printf("no argument!n"); goto out; } if(dbus_message_iter_get_arg_type(&arg) != DBUS_TYPE_INT32) { printf("argument errorn"); goto out; } dbus_message_iter_get_basic(&arg, &a); if(!dbus_message_iter_next(&arg)) { printf("too few argument!n"); goto out; } //check argument type.... dbus_message_iter_get_basic(&arg, &b); sum = a + b; out: //new 一个回应对象 rp = dbus_message_new_method_return(msg); dbus_message_iter_init_append(rp, &r_arg); if(!dbus_message_iter_append_basic(&r_arg, DBUS_TYPE_INT32, &sum)) { printf("no memory!!n"); return; } //param3: 这个跟消息序列有关 if(!dbus_connection_send(connection, rp, NULL)) { printf("no memory!!n"); return; } dbus_connection_flush(connection); dbus_message_unref(rp); } //释放空间 dbus_message_unref(msg); } //dbus_bus_remove_match(); } int main(int argc, char **argv) { int ret = 0; DBusConnection *connection; connection = init_bus(); if(connection == NULL) { printf("connect the dbus failed...n"); return -1; } handle_message(connection); return 0; }
参考博客:
D-Bus 学习
DBUS基础知识(非常全面)
DBus 入门与应用 -- DBus 的 C 编程接口
dbus 简单编程(二)



