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)