DBus glib 各数据类型接收与发送详解—C语言(3)

DBus glib 各数据类型接收与发送详解—C语言(3)

动机

说到 DBus 用过的人大概都能明白其工作的流程。典型的使用流程是,向 DBus 服务进程发送数据,然后接收其返回的数据。简单的说,就像调用函数一样,向服务进程发送数据就相当于函数的参数,其返回的数据就相当于函数返回的结果。虽然明白了流程,但想要使用 C语言 通过已有的 DBus 服务进行操作,仍然是一项不太容易的工作(对像我这样的菜鸟^_^),因为数据的类型真是太多了, 使用 Python 会简单一点。简单点的有 Boolean, Byte, Int32, Int64, String, ObjectPath, Signature 等; 复杂一点的有 Array, Struct, Dict 等。如果不能弄清楚它们之间的联系,那么将是一件非常头痛的事。为了使我研究的结果不被淡忘,于是有了这篇文章。

前置知识

  • 能够熟练使用 C语言;
  • 了解 DBus 各数据类型的表示, 参考 D-Bus Specification
  • 对 DBus-glib 有基本的了解,能够与 DBus 服务进程进行简单的交互。
  • 简单使用 d-feet, 参考 D-Bus 实例讲解
  • 大概对 Python 有些了解(只是为了说明我的分析思路,如果你只想找 C 的解决方法,那完全可以不了解);
  • 简单了解 python dbus

正文

上一篇讨论了高级数据类型的传递,这次我们就讨论更难一点的, 复杂数据类型 的传递。为什么说复杂呢?因为它是高级数据类型的杂揉,本来高级数据类型就挺难的了,再杂揉一下,不用活了。

同样先给出 Python 编写的服务与测试

Python DBus 服务进程

 

more_advanced_data_deliver_service.py


#!/usr/bin/env python

import gobject

import dbus
import dbus.service
import dbus.mainloop.glib

class AdvancedData(dbus.service.Object):
    def __init__(self, bus, object_path):
        dbus.service.Object.__init__(self, bus, object_path)
        self._last_input = None

    @dbus.service.method('airead.fan.MoreAdvancedDataType', in_signature='a(si)', out_signature='a(si)')
    def StructArrayPrint(self, struct_array):
        print "receive struct array:"
        for st in struct_array:
            for value in st:
                print value, ",",
            print '\n' + '-' * 28
        print '=' * 33
        ret = [('li', 21), ('wen', 22), ('feng', 23)]
        return ret

    @dbus.service.method('airead.fan.MoreAdvancedDataType', in_signature='a{sv}', out_signature='a{sv}')
    def DictDictPrint(self, dictdict):
        print "receive dict{sv}:"
        for subdict in dictdict:
            print "subdict:", subdict
            for key in dictdict[subdict]:
                print "    ", key, ":", dictdict[subdict][key]
            print '-' * 33

        print '=' * 33
        ret = {};
        ret['fanrenhao'] = {'name':'renhao', 'age':'24', 'gender': 'male'}
        ret['liwenfeng'] = {'name':'wenfeng', 'age':'22', 'gender': 'female'}
        return ret

    @dbus.service.method('airead.fan.MoreAdvancedDataType', in_signature='a(oa{sv})', out_signature='a(oa{sv})')
    def ObjectPathDictStructArrayPrint(self, complex_array):
        print "receive a(oa{sv}):"
        for struct in complex_array:
            for mem in struct:
                if type(mem) == dbus.Dictionary:
                    for key in mem:
                        print key, ":", mem[key]
                else:
                    print mem
            print '-' * 33
        print '=' * 33
        # o for objectpath
        o1 = dbus.ObjectPath("/path1")
        o2 = dbus.ObjectPath("/path2")
        # d for dictionary
        d1 = {'name':'renhao', 'age':24, 'gender': 'male'}
        d2 = {'name':'wenfeng', 'age':22, 'gender': 'female'}
        # s for struct
        s1 = (o1, d1)
        s2 = (o2, d2)
        ret = [s1, s2]
        return ret


if __name__ == '__main__':
    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)

    session_bus = dbus.SessionBus()
    name = dbus.service.BusName("airead.fan.MoreAdvancedDataType", session_bus)
    object = AdvancedData(session_bus, '/airead/fan/MoreAdvancedDataType')

    mainloop = gobject.MainLoop()
    print "Running example service."
    mainloop.run()



Python 测试服务

more_advanced_data_deliver_test_py.py


#!/usr/bin/python

import sys
import dbus
from traceback import print_exc

def main():

    bus = dbus.SessionBus()

    try:
        remote_object = bus.get_object("airead.fan.MoreAdvancedDataType", "/airead/fan/MoreAdvancedDataType")
        dbus_interface = dbus.Interface(remote_object, "airead.fan.MoreAdvancedDataType")

        #test a(si)
        astruct = [('apple', 1), ('banana', 2), ('cherry', 5)]
        ret = dbus_interface.StructArrayPrint(astruct)
        print "receive struct array:"
        for struct in ret:
            for value in struct:
                print value
            print '-' * 28
        print '=' * 33 + "\n"

        #test a{sv}
        dic = {}
        dic['fanrenhao'] = {'name':'renhao', 'age':'24', 'gender': 'male'}
        dic['liwenfeng'] = {'name':'wenfeng', 'age':'22', 'gender': 'female'}
        ret = dbus_interface.DictDictPrint(dic)
        print "receive dict{sv}:"
        for subdict in ret:
            print "subdict:", subdict
            for key in ret[subdict]:
                print "    ", key, ":", ret[subdict][key]
            print '-' * 33
        print '=' * 33 + "\n"

        #test a(oa{sv})
        # o for objectpath
        o1 = dbus.ObjectPath("/p1")
        o2 = dbus.ObjectPath("/p2")
        # d for dictionary
        d1 = {'a':'apple', 'b': 'banana'}
        d2 = {'c': 'cherry', 'd': 88}
        complex_array = [(o1, d1), (o2, d2)]

        ret = dbus_interface.ObjectPathDictStructArrayPrint(complex_array)
        print "receive a(oa{sv}):"
        for struct in ret:
            for mem in struct:
                if type(mem) == dbus.Dictionary:
                    for key in mem:
                        print key, ":", mem[key]
                else:
                    print mem
            print '-' * 33
        print '=' * 33 + "\n"

    except dbus.DBusException:
        print_exc()
        sys.exit(1)

main()

使用 C 实现复杂数据类型的传递

以下代码仅仅为了演示数据类型的传递,不保证没有内存泄漏,请仔细检查后再使用。

STRUCT_ARRAY

 

这次我们要传递的是结构体数组 "a(si)"。

因为没有 "(si)" 类型,所以我们自己定义。同样因为没有 "a(si)",所以我们也自己定义。那么接下来如代码所示,就可以进行传递了。

只要知道哪种数据与哪种类型对应后,就不难了。难就难在不知道该与哪种数据类型对应,同时又对 dbus-glib 与 glib 不熟,这样的话,真的是比较头痛的一件事。


#define DBUS_STRUCT_STRING_INT (                         \
        dbus_g_type_get_struct ( "GValueArray", G_TYPE_STRING,  \
                                 G_TYPE_INT, G_TYPE_INVALID))
#define DBUS_ARRAY_STRUCT_STRING_INT ( \
        dbus_g_type_get_collection("GPtrArray", DBUS_STRUCT_STRING_INT) )

int send_recv_struct_array(DBusGProxy *proxy)
{
    gchar *method;
    GError *error = NULL;
    GPtrArray *gparray, *ret;
    GValueArray *garray[3], *tmp_garray;
    GValue gval[3][2] = {`0`};
    GValue *tmp_gval;
    gchar *str[3] = {"apple", "banana", "cherry"};
    gint num[3] = {1, 2, 5};
    int i, j;

    for (i = 0; i < 3; i++) {
        g_value_init (&gval[i][0], G_TYPE_STRING);
        g_value_set_string(&gval[i][0], str[i]);
        g_value_init (&gval[i][1], G_TYPE_INT);
        g_value_set_int(&gval[i][1], num[i]);
    }

    gparray = g_ptr_array_new();
    for (i = 0; i < 3; i++) {
        garray[i] = g_value_array_new(0);
        for (j = 0; j < 2 ; j++) {
            g_value_array_append(garray[i], &gval[i][j]);
        }
        g_ptr_array_add(gparray, garray[i]);
    }

    method = "StructArrayPrint";
    if (!dbus_g_proxy_call(proxy, method, &error,
                           DBUS_ARRAY_STRUCT_STRING_INT, gparray,
                           G_TYPE_INVALID,
                           DBUS_ARRAY_STRUCT_STRING_INT, &ret,
                           G_TYPE_INVALID)) {
        g_printerr("call %s failed: %s\n", method, error->message);
        g_error_free(error);
        error = NULL;
        return -1;
    }

    for (i = 0; i < ret->len; i++) {
        tmp_garray = g_ptr_array_index(ret, i);
        tmp_gval = g_value_array_get_nth(tmp_garray, 0);
        g_print("%s: ", g_value_get_string(tmp_gval));
        tmp_gval = g_value_array_get_nth(tmp_garray, 1);
        g_print("%d\n", g_value_get_int(tmp_gval));
    }
    g_print("=================================\n\n");

    return 0;
}

DICT_DICT

下面演示的是一个 "a{sv}" 的数据类型,特别的是这里的 "v" 我们用它再来容纳一个 "a{ss}" 数据类型。这样的话是不是有点复杂了哇?

源代码如下,俗话说,源代码上没有任何能够隐藏的秘密,有这句话吧?


#define DBUS_TYPE_G_STRING_VALUE_HASHTABLE                             \
    dbus_g_type_get_map ( "GHashTable", G_TYPE_STRING, G_TYPE_VALUE)

int send_recv_dictdict(DBusGProxy *proxy)
{
    int i;
    char *method;
    GHashTable *table, *ret, *subtable;
    GHashTableIter iter, subiter;
    gpointer key, value, subkey, subvalue;
    GError *error = NULL;
    GValue gval[2] = `0`;
    gchar *table_value[2][3] = {{"renhao", "24", "male"},
                                {"wenfeng", "22", "female"}};

    table = g_hash_table_new(NULL, NULL);

    for (i = 0; i < 2; i++) {
        g_value_init(&gval[i], DBUS_TYPE_G_STRING_STRING_HASHTABLE);
        g_value_take_boxed(&gval[i],
                           dbus_g_type_specialized_construct(
                               DBUS_TYPE_G_STRING_STRING_HASHTABLE));
        subtable = g_value_get_boxed(&gval[i]);
        g_hash_table_insert(subtable, "name", table_value[i][0]);
        g_hash_table_insert(subtable, "age", table_value[i][1]);
        g_hash_table_insert(subtable, "gender", table_value[i][2]);
    }

    g_hash_table_insert(table, "fanrenhao", &gval[0]);
    g_hash_table_insert(table, "liwenfeng", &gval[1]);

    method = "DictDictPrint";
    if (!dbus_g_proxy_call(proxy, method, &error,
                           DBUS_TYPE_G_STRING_VALUE_HASHTABLE, table,
                           G_TYPE_INVALID,
                           DBUS_TYPE_G_STRING_VALUE_HASHTABLE, &ret,
                           G_TYPE_INVALID)) {
        g_printerr("call %s failed: %s\n", method, error->message);
        g_error_free(error);
        error = NULL;
        return -1;
    }

    g_print("receive: dictionary\n");
    g_hash_table_iter_init(&iter, ret);
    while (g_hash_table_iter_next(&iter, &key, &value)) {
            g_print("%s:\n", (char *)key);
            subtable = g_value_get_boxed(value);
            g_hash_table_iter_init(&subiter, subtable);
            while (g_hash_table_iter_next(&subiter, &subkey, &subvalue)) {
                g_print("%s, %s\n", (char *)subkey, (char *)subvalue);
            }
            g_print("---------------------------------\n");
        }
    g_print("=================================\n\n");

    return 0;
}

ObjectPath_Dict_Struct_Array

这是一个 "a(oa{sv})" 的数据类型。也就是说首先要定义一个 "a{sv}" 的数据类型, 再由 "a{sv}" 定义一个 "(oa{sv})",最后再定义 "a(oa{sv})" 的数据类型。这很复杂吧,现实中真的传递过这样复杂的数据吗? 真的出现过,就在 connman (connect manager 类似 network-manager 的东东) 的服务进程中! 我就是因为它才接触到了 D-Bus, 它的 "a(oa{sv})" 真的是害得我不浅,所以才有了这篇文章。

具体代码如下:



int send_recv_objectpath_dict_struct_array(DBusGProxy *proxy)
{
    //这个当成是期末考试的试题吧 ^_^
    //好吧,我承认是我懒了
    return 0;
}

C D-Bus 测试完整代码


/**
 * @file more_advanced_data_deliver_test_c.c
 * @brief
 * @author Airead Fan <[email protected]>
 * @date 2012/03/23 17:55:41
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dbus/dbus-glib.h>

#define METHOD_STRLEN 128

/*
 * a{sv}
 * dic = {}
 * dic['fanrenhao'] = {'name':'renhao', 'age':'24', 'gender': 'male'}
 * dic['liwenfeng'] = {'name':'wenfeng', 'age':'22', 'gender': 'female'}
 */

#define DBUS_TYPE_G_STRING_VALUE_HASHTABLE                             \
    dbus_g_type_get_map ( "GHashTable", G_TYPE_STRING, G_TYPE_VALUE)

int send_recv_dictdict(DBusGProxy *proxy)
{
    int i;
    char *method;
    GHashTable *table, *ret, *subtable;
    GHashTableIter iter, subiter;
    gpointer key, value, subkey, subvalue;
    GError *error = NULL;
    GValue gval[2] = `0`;
    gchar *table_value[2][3] = {{"renhao", "24", "male"},
                                {"wenfeng", "22", "female"}};

    table = g_hash_table_new(NULL, NULL);

    for (i = 0; i < 2; i++) {
        g_value_init(&gval[i], DBUS_TYPE_G_STRING_STRING_HASHTABLE);
        g_value_take_boxed(&gval[i],
                           dbus_g_type_specialized_construct(
                               DBUS_TYPE_G_STRING_STRING_HASHTABLE));
        subtable = g_value_get_boxed(&gval[i]);
        g_hash_table_insert(subtable, "name", table_value[i][0]);
        g_hash_table_insert(subtable, "age", table_value[i][1]);
        g_hash_table_insert(subtable, "gender", table_value[i][2]);
    }

    g_hash_table_insert(table, "fanrenhao", &gval[0]);
    g_hash_table_insert(table, "liwenfeng", &gval[1]);

    method = "DictDictPrint";
    if (!dbus_g_proxy_call(proxy, method, &error,
                           DBUS_TYPE_G_STRING_VALUE_HASHTABLE, table,
                           G_TYPE_INVALID,
                           DBUS_TYPE_G_STRING_VALUE_HASHTABLE, &ret,
                           G_TYPE_INVALID)) {
        g_printerr("call %s failed: %s\n", method, error->message);
        g_error_free(error);
        error = NULL;
        return -1;
    }

    g_print("receive: dictionary\n");
    g_hash_table_iter_init(&iter, ret);
    while (g_hash_table_iter_next(&iter, &key, &value)) {
            g_print("%s:\n", (char *)key);
            subtable = g_value_get_boxed(value);
            g_hash_table_iter_init(&subiter, subtable);
            while (g_hash_table_iter_next(&subiter, &subkey, &subvalue)) {
                g_print("%s, %s\n", (char *)subkey, (char *)subvalue);
            }
            g_print("---------------------------------\n");
        }
    g_print("=================================\n\n");

    return 0;
}

/*
 * a(si)
 * astruct = [('apple', 1), ('banana', 2), ('cherry', 5)]
 */

#define DBUS_STRUCT_STRING_INT (                         \
        dbus_g_type_get_struct ( "GValueArray", G_TYPE_STRING,  \
                                 G_TYPE_INT, G_TYPE_INVALID))
#define DBUS_ARRAY_STRUCT_STRING_INT ( \
        dbus_g_type_get_collection("GPtrArray", DBUS_STRUCT_STRING_INT) )

int send_recv_struct_array(DBusGProxy *proxy)
{
    gchar *method;
    GError *error = NULL;
    GPtrArray *gparray, *ret;
    GValueArray *garray[3], *tmp_garray;
    GValue gval[3][2] = {`0`};
    GValue *tmp_gval;
    gchar *str[3] = {"apple", "banana", "cherry"};
    gint num[3] = {1, 2, 5};
    int i, j;

    for (i = 0; i < 3; i++) {
        g_value_init (&gval[i][0], G_TYPE_STRING);
        g_value_set_string(&gval[i][0], str[i]);
        g_value_init (&gval[i][1], G_TYPE_INT);
        g_value_set_int(&gval[i][1], num[i]);
    }

    gparray = g_ptr_array_new();
    for (i = 0; i < 3; i++) {
        garray[i] = g_value_array_new(0);
        for (j = 0; j < 2 ; j++) {
            g_value_array_append(garray[i], &gval[i][j]);
        }
        g_ptr_array_add(gparray, garray[i]);
    }

    method = "StructArrayPrint";
    if (!dbus_g_proxy_call(proxy, method, &error,
                           DBUS_ARRAY_STRUCT_STRING_INT, gparray,
                           G_TYPE_INVALID,
                           DBUS_ARRAY_STRUCT_STRING_INT, &ret,
                           G_TYPE_INVALID)) {
        g_printerr("call %s failed: %s\n", method, error->message);
        g_error_free(error);
        error = NULL;
        return -1;
    }

    for (i = 0; i < ret->len; i++) {
        tmp_garray = g_ptr_array_index(ret, i);
        tmp_gval = g_value_array_get_nth(tmp_garray, 0);
        g_print("%s: ", g_value_get_string(tmp_gval));
        tmp_gval = g_value_array_get_nth(tmp_garray, 1);
        g_print("%d\n", g_value_get_int(tmp_gval));
    }
    g_print("=================================\n\n");

    return 0;
}

int send_recv_objectpath_dict_struct_array(DBusGProxy *proxy)
{

    return 0;
}

int main(int argc, char *argv[])
{
    DBusGConnection *connection;
    GError *error = NULL;
    DBusGProxy *proxy;

    g_type_init();

    /* conect system connection and get proxy */
    connection = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
    if (connection == NULL) {
        g_printerr("get system bus failed: %s\n", error->message);
        g_error_free(error);
        return -1;
    }

    /* get proxy */
    proxy = dbus_g_proxy_new_for_name(connection,
                                      "airead.fan.MoreAdvancedDataType",
                                      "/airead/fan/MoreAdvancedDataType",
                                      "airead.fan.MoreAdvancedDataType");

    send_recv_dictdict(proxy);
    send_recv_struct_array(proxy);
    send_recv_objectpath_dict_struct_array(proxy);

    return 0;
}

Makefile

有些东西实际上没用,我也懒得去了。


CC	= gcc

CFLAGS	= -Wall -g
CFLAGS += $(shell pkg-config --cflags glib-2.0 )
CFLAGS += $(shell pkg-config --cflags dbus-glib-1)
#CFLAGS += $(shell pkg-config --cflags gtk+-2.0)

LDFLAGS	=
LDFLAGS += $(shell pkg-config --libs glib-2.0)
LDFLAGS += $(shell pkg-config --libs dbus-glib-1)
#LDFLAGS += $(shell pkg-config --libs gtk+-2.0)

SOURCE =  $(wildcard *.c)
TARGETS	:= $(patsubst %.c, %, $(SOURCE))
TARGETS_OUT = common_marshaler basic_data
TARGETS := $(filter-out $(TARGETS_OUT), $(TARGETS))
TARGETS := $(addsuffix .out, $(TARGETS))

%.out: %.c
	@echo CC $< -o $@
	@$(CC) $< common_marshaler.c basic_data.c $(CFLAGS) -o $@ $(LDFLAGS)

.PHONY: all clean test marshaler

all: $(TARGETS)

marshaler:
	glib-genmarshal --prefix _common_marshal --header common_marshaler.list > common_marshaler.h
	glib-genmarshal --prefix _common_marshal --body common_marshaler.list > common_marshaler.c
	dbus-binding-tool --prefix=airead_fan --mode=glib-server all_basic_data_deliver_server.xml > all_basic_data_deliver_server.h

clean:
	rm -f *~ a.out *.o $(TARGETS) core.*

test:
	@echo TARGETS: $(TARGETS)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章