1. 介绍:
DBUS是一种高级的进程间通信机制。DBUS支持进程间一对一和多对多的对等通信,在多对多的通讯时,需要后台进程的角色去分转消息,当一个进程发消息给另外一个进程时,先发消息到后台进程,再通过后台进程将信息转发到目的进程。DBUS后台进程充当着一个路由器的角色。
DBUS中主要概念为总线,连接到总线的进程可通过总线接收或传递消息,总线收到消息时,根据不同的消息类型进行不同的处理。DBUS中消息分为四类:
1. Methodcall消息:将触发一个函数调用 ;
2. Methodreturn消息:触发函数调用返回的结果;
3. Error消息:触发的函数调用返回一个异常 ;
4. Signal消息:通知,可以看作为事件消息。
2. 应用场景:
根据DBUS消息类型可知,DBUS提供一种高效的进程间通信机制,主要用于进程间函数调用以及进程间信号广播。
1 . 函数调用
DBUS可以实现进程间函数调用,进程A发送函数调用的请求(Methodcall消息),经过总线转发至进程B。进程B将应答函数返回值(Method return消息)或者错误消息(Error消息)。
2 . 消息广播
进程间消息广播(Signal消息)不需要响应,接收方需要向总线注册感兴趣的消息类型,当总线接收到“Signal消息”类型的消息时,会将消息转发至希望接收的进程。
3. 安装准备:
sudo apt install libdbus-glib-1-dev libdbus-1-dev
4. 代码:
处理signal和method_call两种方式
service.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dbus/dbus.h>
#include <unistd.h>
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--%s\n", 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--%s\n", 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--%s\n", 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: %s\n", 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 argument\n");
}
//获取第一个参数类型
if(dbus_message_iter_get_arg_type(&arg) != DBUS_TYPE_INVALID)
{
//获取参数的值
dbus_message_iter_get_basic(&arg,&str);
printf("recv param --: %s\n", 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 function\n");
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 error\n");
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;
}
client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dbus/dbus.h>
DBusConnection* init_bus()
{
DBusConnection *connection;
DBusError err;
int ret;
dbus_error_init(&err);
connection = dbus_bus_get(DBUS_BUS_SESSION, &err);
if(dbus_error_is_set(&err))
{
printf("connection error: :%s -- %s\n", 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 -- %s\n", 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 NULL\n");
return;
}
#if 0
//这个看需求添加,一般来说,信号是一种单向广播,加上这一句变单向单播
//param2: bus_name
if(!dbus_message_set_destination(msg, "hello.world.service"))
{
printf("memory error\n");
}
#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 memory\n");
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. error\n");
return;
}
//释放pending内存
dbus_pending_call_unref(pending);
//解析参数
if (!dbus_message_iter_init(msg, &arg))
printf("no argument, error\n");
if(dbus_message_iter_get_arg_type(&arg) != DBUS_TYPE_INT32)
{
printf("paramter type error\n");
}
dbus_message_iter_get_basic(&arg, &sum);
printf(" a(%d) + b(%d) = %d\n",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;
}
5. 编译:
gcc client.c -ldbus-1 -I/usr/include/dbus-1.0 -o client
gcc service.c -ldbus-1 -I/usr/include/dbus-1.0 -o service
6. 运行:
./service & ./client
7. 可能的问题:
有可能会提示找不dbus-arch-deps.h头文件,在系统中搜一下,然后拷贝到/usr/include/dbus-1.0/dbus目录
附 : 接口说明及代码文件
参考:
https://blog.csdn.net/mr_wangning/article/details/60324291
https://download.csdn.net/download/u012385733/10521079
来源:CSDN
作者:kong1337
链接:https://blog.csdn.net/qq_36236684/article/details/90207896