使用dbus进行进程通信

送分小仙女□ 提交于 2019-11-26 04:58:30

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

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!