Dbus 的编译(移植)以及双向通信使用例程

        由于之前做项目时,一个项目需要由多个app组成,各个app还需要进行通信,当时所采用的是利用socket进行通信。但是采用socket进行通信时,两个客户端无法直接进行通信,导致还需要额外再建立一个server端进行转发,如此一来,浪费了大量的cpu内存资源以及精力来处理数据转发。后来了解到Dbus这个数据总线后,果断采用dbus来处理。

        本来是不想写这篇文章的,因为网上关于dbus的资料很多,但是网上的通信案例大多是单向通信的,最多的也就是接收端收到数据后给个应答,并没有同时双向通信的例子(至少笔者是没找到的)。这里所说的双向通信,比如appA和appB,A可以主动向B发送数据,同时,B也可以主动向A发送数据。两个数据流同时进行,互不干扰。

        从网上的资料来看,dbus是一个数据总线,其内部实现依旧采用socket进行通信。详细的原理这里不做介绍,自行了解即可。接下来,将主要介绍dbus的编译以及对原有的dbus进行c++封装成dbus类,并进行双向通信测试的实现。

注意:以下内容均在pc上,ubuntu进行测试,arm端在am57xx测试。两者不同的地方会有说明。

Dbus的编译

        dbus的编译,其实与其他第三方库的编译一样,都是遵循configure,make, make install三个步骤。dbus版本:dbus-1.10.20.tar.gz。

解压源码,在dbus源码路径下进行configure配置:

./configure --prefix=/home/xurc/am57xx/dbus_pc/install                 (pc端)

./configure --prefix=/home/xurc/am57xx/dbus_pc/install CC=arm-linux-gnueabihf- --host=arm-linux-gnueabihf               (arm端)

然后进行make;make install 即可。

dbus的编译其实网上很多,这里仅简单说明。

注意:在ubuntu环境下,dbus的安装其实可以直接apt-get安装,这里之所以要进行额外编译,是因为如果没有编译源码,则编译两个测试例子时,会提示缺少dbus.h等头文件,笔者由于找不到更好的解决办法,索性把源码编译一下,然后利用-I -L指定dbus相关的库以及头文件。

dbus编译完成后,如下图: 

dbus库的c++封装

        之所以对原有的dbus进行c++封装,是因为项目采用c++实现,出于方便的考虑,决定自行封装一层接口。对外接口实现了两个,分别是发送数据DbusSend以及接收数据DbusRecv。

Dbus类,拥有一个结构体成员:

struct recvst
{
    string bus_name;
    string object_path_name;
    string interface_name;
    string method_name;
    string request_bus_name;
    
    DBusError dbus_error;                // 原来为DBusError &err = *perr;
    DBusConnection *connection;
    DBusMessage *message;

    pthread_mutex_t dbus_recv_mutex;
    uint8_t recvdata[RECV_DATA_SIZE];
    int32_t recvpos;
    int32_t recvlen;
};

还有一个静态成员函数static DBusHandlerResult ws_dbus_message_handler,该函数主要用于在后台接收数据。目前该静态成员函数允许接收字符串和uint8_t数组两种格式的数据,其他dbus允许的数据格式这里没有实现,因为笔者都将其他类型(int8,int32)的数据转成uint8_t 数组。如有需要,读者可以参考网上的其他资料,添加即可。

构造函数Dbus(string bus_name, string path_name, string interface_name, string method_name, string request_bus_name, uint8_t dbus_type)

构造函数主要是获取总线,生成Dbus。注意,以下是实现双向通信的关键点,也是其他资料没有提到的。

如果采用dbus_bus_get去获取总线,将无法进行双向通信,或者说,如果采用dbus_bus_get获取总线,则appA发送数据给appB,appB可以被动的进行应答,但是无法主动向appA发送数据。而在实际应用中,appB大多数情况下也需要主动向appA发送一些数据。因此,必须采用dbus_bus_get_private获取总线,利用这个函数获取的将是一个私有总线,可以独立的进行数据传输。另外,在构造函数的参数中,还传入了一个总线类型dbus_type,这个dbus_type用于指明总线是用于发送还是接收,即下面的两个宏:

#define DBUS_RECV       1                 //接收
#define DBUS_SEND       2                 //发送

如果是DBUS_RECV,则还会在后台创建一个线程,用于接收数据:

    if (dbus_type == DBUS_RECV)
    {
        st.recvpos = 0;
        st.recvlen = 0;
        memset(&st.recvdata, 0x00, RECV_DATA_SIZE);
        pthread_mutex_init(&st.dbus_recv_mutex, NULL);
        pthread_create(&precv_id, NULL, precv_func, &st);
    }

 后台接收线程precv_func

        当总线是用于接收时,才会创建后台接收线程precv_func,该线程是向总线注册一个回调函数,当总线有数据需要接收时,调用回调函数将总线上的数据存入缓冲区recvdata,方便上层读取。

接收函数DbusRecv(void *buff, int32_t buff_len)

        接收函数的功能其实很简单,就是将缓冲区的数据提取出来。 

发送函数DbusSend(const void *data, int32_t data_len, int32_t data_type)

        发送函数需要指明所要发送的数据类型是字符串还是uint8_t数组,因为发送这两种不同的数据时,略有差异:发送字符串时,需要先创建一个DBusMessage,然后将DBusMessage绑定到DBusMessageIter,最后dbus_message_iter_append_basic即可;但发送uint8_t数组时,还需要一个额外的数组迭代器才能实现。具体差异如下:

以上就是Dbus类的主要功能,目前Dbus类发送和接收只支持字符串和uint8_t数组,因为实际应用中这两种可以使用最多,其他类型的数据可以自行添加实现,或者转成字符串,或者转成uint8_t数组。

测试案例appA:

         appA创建两个线程,hardware_send_dataprocess以及hardware_recv_dataprocess。hardware_send_dataprocess占有总线hardware.dataprocess,该线程向总线发送数据;

hardware_recv_dataprocess占有总线dataprocess.hardware,该线程从总线上读取数据。

 测试案例appB:

        appB同样创建两个线程:dataprocess_send_hardware以及dataprocess_recv_hardware。dataprocess_send_hardware占有总线dataprocess.hardware,该线程向总线发送数据。dataprocess_recv_hardware占有总线hardware.dataprocess,该线程从总线读取数据。

需要特别注意的是: appA的发送即是appB的接收,appA的接收即是appB的发送。发送和接收的总线名称务必注意。

测试案例的编译

编译测试案例时,需要注意头文件和库的链接地址。如果是arm端,注意修改编译器。

测试效果

  

 整个dbus的使用过程,其实没什么太难的地方,主要还是在于双向通信的实现。这里笔者仅仅是对原有的资料进行了一层c++封装,便于使用。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#include <ctype.h>
#include <dbus/dbus.h>
#include <iostream>
#include <string>

using namespace std;

#define DBUS_RECV       1                 //接收
#define DBUS_SEND       2                 //发送
#define RECV_DATA_SIZE  (512 * 20)


//DBusError dbus_error;	

struct recvst
{
    string bus_name;
	string object_path_name;
	string interface_name;
	string method_name;
	string request_bus_name;
    
	DBusError dbus_error;				//xurc 原来为DBusError &err = *perr;
	DBusConnection *connection;
    DBusMessage *message;

    pthread_mutex_t dbus_recv_mutex;
    uint8_t recvdata[RECV_DATA_SIZE];
    int32_t recvpos;
    int32_t recvlen;
};

class Dbus
{
	public:
        pthread_t precv_id;

        struct recvst st;
		
		Dbus(string bus_name, string path_namae, string interface_name, string method_name, string request_bus_name, uint8_t dbus_type);
		
		int32_t DbusSend(const void *data, int32_t data_len, int32_t data_type);
		int32_t DbusRecv(void *buff, int32_t buff_len);

	//private:
        static DBusHandlerResult ws_dbus_message_handler(DBusConnection *connection, DBusMessage *message, void *user_data)
        {
            DBusMessageIter recvIter;
	        DBusMessageIter arrayIter;
            int ret = 0;
	        dbus_int32_t len = 0;
            struct recvst *st = (struct recvst *)user_data;
            //DBusMessage *message = st->message;

            DBusMessage *reply = NULL;
            const char * path =dbus_message_get_path(message);
            if(NULL==path)
            {
                return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
            }

            if (strcmp(path, st->object_path_name.c_str()) == 0) 
            {
        		if (dbus_message_is_method_call(message, st->interface_name.c_str(), st->method_name.c_str())) 
                {
                    dbus_message_iter_init(message, &recvIter);
                    do 
	                {
	                	ret = dbus_message_iter_get_arg_type(&recvIter);
	                	if (DBUS_TYPE_STRING == ret)
	                	{
	                		char *pdata = NULL;
                            dbus_message_iter_get_basic(&recvIter, &pdata);
	                		printf("%s\n", pdata);
                            pthread_mutex_lock(&st->dbus_recv_mutex);
                            memcpy((char *)st->recvdata + st->recvpos, pdata, strlen(pdata));
                            st->recvlen = strlen(pdata);
                            st->recvpos += st->recvlen;
                            pthread_mutex_unlock(&st->dbus_recv_mutex);
	                	}
	                	else if (DBUS_TYPE_ARRAY == ret)
	                	{
	                		uint8_t *pdata = NULL;
	                		dbus_message_iter_recurse(&recvIter, &arrayIter);
                            dbus_message_iter_get_fixed_array(&arrayIter, &pdata, &len);
	                		if (len > 0)
	                		{
                                pthread_mutex_lock(&st->dbus_recv_mutex);
	                			memcpy(st->recvdata + st->recvpos, pdata, len);
                                st->recvlen = len;
                                st->recvpos += len;
                                pthread_mutex_unlock(&st->dbus_recv_mutex);
	                		}
	                	}
	                } while (dbus_message_iter_next(&recvIter));

        			char answer [40];
        			sprintf (answer, "I have get your message!");
        			if ((reply = dbus_message_new_method_return (message)) == NULL) 
                    {
        				fprintf (stderr, "Error in dbus_message_new_method_return\n");
        				exit (1);
        			}

        			DBusMessageIter iter;
        			dbus_message_iter_init_append (reply, &iter);
        			char *ptr = answer;
        			if (!dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &ptr)) 
                    {
        				fprintf (stderr, "Error in dbus_message_iter_append_basic\n");
        				exit (1);
        			}
        		}
        	}

        	if(reply)
            {
        	    // send the reply && flush the connection
        	    if (!dbus_connection_send(connection, reply, NULL)) 
                {
        			return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
        		}
        		dbus_connection_flush(connection);
        		// free the reply
        		dbus_message_unref(reply);

        		return DBUS_HANDLER_RESULT_HANDLED ;
        	}
            else
            {
        		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
        	}	
        }
};

static void *precv_func(void *arg)
{
    struct recvst *st = (struct recvst *)arg;

    dbus_error_init(&st->dbus_error);
    if (!dbus_connection_add_filter(st->connection, Dbus::ws_dbus_message_handler, st, NULL)) 
    { 
        fprintf(stderr, "dbus_connection_add_filter\n");
        exit(1); 
    }
	while (dbus_connection_read_write_dispatch (st->connection, -1))
    {
        if (st->recvlen > 0)
        {
            //printf("555555555st->len = %d\n", st->recvlen);
            //printf("555555555st->recvdata = %s\n", st->recvdata);
            //
            //memset(&st->recvdata, 0x00, sizeof(st->recvdata));
            //st->recvlen = 0;
            printf("get new %d bytes\n", st->recvlen);
            st->recvlen = 0;
        }
        //sleep(1);
   }
}


Dbus::Dbus(string bus_name, string path_name, string interface_name, string method_name, string request_bus_name, uint8_t dbus_type)
{
    int32_t ret = 0;

    st.bus_name = bus_name;
    st.object_path_name = path_name;
    st.interface_name = interface_name;
    st.method_name = method_name;
    st.request_bus_name = request_bus_name;
    
    //if (st.connection == NULL)
    {
        dbus_error_init(&st.dbus_error);
        //st.connection = dbus_bus_get (DBUS_BUS_SESSION, &st.dbus_error);          //获取公共的connection
        st.connection = dbus_bus_get_private(DBUS_BUS_SESSION, &st.dbus_error);     //用dbus_bus_get_private获取独立的connection
    }

    if (dbus_error_is_set (&st.dbus_error))
        printf("dbus_bus_get\n");
    if (!st.connection) 
        exit (1);
    
    ret = dbus_bus_request_name (st.connection, st.request_bus_name.c_str(), DBUS_NAME_FLAG_DO_NOT_QUEUE, &st.dbus_error);
    if (dbus_error_is_set (&st.dbus_error))
        printf("dbus_bus_get\n");
    if (ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) 
    {
        fprintf (stderr, "Dbus: not primary owner, ret = %d\n", ret);
        exit (1);
    }
    //dbus_error_init(&dbus_error);
    //if (!dbus_connection_add_filter (connection, ws_dbus_message_handler, NULL, NULL)) { 
    //    fprintf(stderr, "dbus_connection_add_filter\n");
    //    exit(1); 
    //} 
	//while (dbus_connection_read_write_dispatch (connection, -1));
    if (dbus_type == DBUS_RECV)
    {
        st.recvpos = 0;
        st.recvlen = 0;
        memset(&st.recvdata, 0x00, RECV_DATA_SIZE);
        pthread_mutex_init(&st.dbus_recv_mutex, NULL);
        pthread_create(&precv_id, NULL, precv_func, &st);
    }
}

int32_t Dbus::DbusRecv(void *buff, int32_t buff_len)
{
    int32_t len = 0;

    if ((st.recvpos > 0) && (buff_len >= st.recvpos))
    {
        pthread_mutex_lock(&st.dbus_recv_mutex);
        memcpy((uint8_t *)buff, st.recvdata, st.recvpos);
        len = st.recvpos;
        st.recvpos = 0;
        pthread_mutex_unlock(&st.dbus_recv_mutex);
    }

    return len;
}

int32_t Dbus::DbusSend(const void *data, int32_t data_len, int32_t data_type)
{
    DBusMessage *msg = NULL;
    DBusPendingCall *pending;
    DBusMessageIter sendIter;
	DBusMessageIter arrayIter;
	char buff[2];
	char *ptype = buff;
	dbus_bool_t ret = 0;

    //针对目的地地址,请参考图,创建一个method call消息。Constructs a new message to invoke a method on a remote object.
	msg = dbus_message_new_method_call(st.bus_name.c_str(), st.object_path_name.c_str(), st.interface_name.c_str(), st.method_name.c_str());
	if(msg == NULL){
		fprintf(stderr, "MessageNULL");
		return 0;
	}

    //为消息添加参数。Appendarguments
	dbus_message_iter_init_append(msg, &sendIter);
	if (data_type == DBUS_TYPE_STRING)
	{
		if(!dbus_message_iter_append_basic(&sendIter, DBUS_TYPE_STRING, &data)){
			fprintf(stderr, "Out of Memory!");
			exit(1);
		}
	}
	else if (data_type == DBUS_TYPE_BYTE)
	{	
		buff[0] = DBUS_TYPE_BYTE;
		buff[1] = '\0';
		dbus_message_iter_open_container(&sendIter, DBUS_TYPE_ARRAY, ptype, &arrayIter);
		ret = dbus_message_iter_append_fixed_array(&arrayIter, DBUS_TYPE_BYTE, &data, data_len);
		dbus_message_iter_close_container(&sendIter, &arrayIter);
	}

    //dbus_connection_send(st.connection, msg, NULL);
    //dbus_connection_flush(st.connection);
	//dbus_message_unref(msg);

#if 1
	//发送消息并获得reply的handle。Queues amessage to send, as withdbus_connection_send() , but also returns aDBusPendingCall used to receive a reply to the message.
	if(!dbus_connection_send_with_reply(st.connection, msg, &pending, -1)){
		fprintf(stderr, "Out of Memory!");
		exit(1);
	}
	
	if(pending == NULL){
		fprintf(stderr, "Pending CallNULL: connection is disconnected ");
		dbus_message_unref(msg);
		return 0;
	}
	
	dbus_connection_flush(st.connection);
	dbus_message_unref(msg);
    dbus_pending_call_block(pending);

    //接收返回数据
    char *s;
	DBusMessage *reply;
	reply= dbus_pending_call_steal_reply(pending);
	if (reply== NULL)
	{
		fprintf (stderr,"call steal reply failed\n");
		dbus_message_unref(reply);
		exit(1);;
	}
    dbus_pending_call_unref(pending);
    
	//从服务器端返回一个简单的字符串
    if (dbus_message_get_args (reply, &st.dbus_error, DBUS_TYPE_STRING, &s, DBUS_TYPE_INVALID)) {
        printf ("Receive from server:  %s\n", s);
    }else{
        fprintf (stderr, "Did not get arguments in reply\n");
        exit (1);
    }
    dbus_message_unref (reply);	
	/****************逐个解析获取信息***********
	dbus_error_init(&err);
	dbus_message_iter_init(msg, &iter);//成功返回1	
	if(DBUS_TYPE_INT32 == dbus_message_iter_get_arg_type(&iter)){
		dbus_message_iter_get_basic(&iter, &status);//获取DBUS_TYPE_INT32类型数据存放到status
	}
	dbus_message_iter_next(&iter);//获取下一数据
	if(DBUS_TYPE_INT32 == dbus_message_iter_get_arg_type(&iter)){
		dbus_message_iter_get_basic(&iter, &count);
	}
	//解析获取的数组数据
	DBusMessageIter sub_iter;
	if(dbus_message_iter_next(&iter){
		if(DBUS_TYPE_ARRAY == dbus_message_iter_get_arg_type(&iter)){
			dbus_message_iter_recurse(&iter, &sub_iter);
			dbus_message_iter_get_fixed_array(&sub_iter, &mac, &mac_len);
		}
	}
	*******************************************/
#endif

   	if (dbus_bus_release_name (st.connection, st.request_bus_name.c_str(), &st.dbus_error) == -1) {
        fprintf (stderr, "Error in dbus_bus_release_name\n");
        exit (1);
    }
	return 0;
}



#if 0
int32_t main(int32_t argc, char *argv[])
{
    struct recvst rcst;
    const string recv_bus_name = "in.softprayog.add_server_recv";
    const string recv_object_path_name = "/in/softprayog/adder";
    const string recv_interface_name = "in.softprayog.dbus_example";
    const string recv_methoe_name = "test_dbus_damon";
    const string recv_request_name = "in.softprayog.add_server_recv";

    const string send_bus_name = "in.softprayog.add_server_send";
    const string send_object_path_name = "/in/softprayog/adder";
    const string send_interface_name = "in.softprayog.dbus_example";
    const string send_methoe_name = "test_dbus_damon";
    const string send_request_name = "in.softprayog.add_server_send";


    uint8_t recvbuff[1024];
    int32_t recvlen = 0;

    Dbus recv(recv_bus_name, recv_object_path_name, recv_interface_name, recv_methoe_name, recv_request_name, DBUS_RECV);
    Dbus send(send_bus_name, send_object_path_name, send_interface_name, send_methoe_name, send_request_name, DBUS_SEND);


    while (1)
    {
        send.DbusSend("hello", strlen("hello"), DBUS_TYPE_STRING);

        recvlen = recv.DbusRecv(recvbuff, sizeof(recvbuff));
        for (int i = 0; i < recvlen; i++)
            printf("%x ", recvbuff[i]);

        printf("\n");
        sleep(3);
    }
}
#endif

#if 1
static void *hardware_send_dataprocess(void *arg)
{
	string send_BusName = "hardware.dataprocess";
	string send_Path = "/hardware_dataprocess/method/Object";
	string send_InterFace = "hardware_dataprocess.method.Type";
	string send_Method = "Method";
	string send_requestName = "hardware.dataprocess.source";

	uint8_t senddata[512];
	uint8_t i = 0;

	Dbus dbus_hardware_send_dataprocess(send_BusName, send_Path, send_InterFace, send_Method, send_requestName, DBUS_SEND);
	while (1)
	{
		memset(senddata, i, sizeof(senddata));
		dbus_hardware_send_dataprocess.DbusSend(senddata, sizeof(senddata), DBUS_TYPE_BYTE);
		i++;

        //sleep(1);
		usleep(1000 * 50);
	}
}

static void *hardware_recv_dataprocess(void *arg)
{
	string recv_BusName = "dataprocess.hardware";
	string recv_Path = "/dataprocess_hardware/method/Object";
	string recv_InterFace = "dataprocess_hardware.method.Type";
	string recv_Method = "Method";
	string recv_requestName = recv_BusName;

	uint8_t recvdata[8200];
	int32_t recvlen = 0;

	Dbus dbus_hardware_recv_dataprocess(recv_BusName, recv_Path, recv_InterFace, recv_Method, recv_requestName, DBUS_RECV);
	while (1)
	{
		memset(recvdata, 0x00, sizeof(recvdata));
		recvlen = dbus_hardware_recv_dataprocess.DbusRecv(recvdata, sizeof(recvdata));
		if (recvlen > 0)
		{
			printf("tellen = %d\n", recvlen);
			for (int i = 0; i < 8; i++)
			{
				printf("%x ", recvdata[i]);
			}
		}
		sleep(1);
	}
}

int main(int argc, char *argv[])
{
    pthread_t hardware_send_dataprocess_id;
	pthread_t hardware_recv_dataprocess_id;

	pthread_create(&hardware_send_dataprocess_id, NULL, hardware_send_dataprocess, NULL);
	pthread_create(&hardware_recv_dataprocess_id, NULL, hardware_recv_dataprocess, NULL);

    while (1)
    {
        sleep(1);
    }
}
#endif

完整的源码见链接:https://download.csdn.net/download/qq_36731830/60418661 

猜你喜欢

转载自blog.csdn.net/qq_36731830/article/details/121865510