dbus实例讲解(四下):使用dbus-glib

11 篇文章 1 订阅
 

<script type="text/javascript"> document.body.oncopy = function() { if (window.clipboardData) { setTimeout(function() { var text = clipboardData.getData("text"); if (text && text.length>300) { text = text + "/r/n/n本文来自CSDN博客,转载请标明出处:" + location.href; clipboardData.setData("text", text); } }, 100); } } </script> <script type="text/javascript">function StorePage(){d=document;t=d.selection?(d.selection.type!='None'?d.selection.createRange().text:''):(d.getSelection?d.getSelection():'');void(keyit=window.open('http://www.365key.com/storeit.aspx?t='+escape(d.title)+'&u='+escape(d.location.href)+'&c='+escape(t),'keyit','scrollbars=no,width=475,height=575,left=75,top=20,status=no,resizable=yes'));keyit.focus();}</script>

4、复杂的数据类型

在dbus中怎样处理复杂的数据类型?第一个建议是尽量不要使用复杂的数据类型。但如果确实需要呢?有的网友建议 用GArray作为容器,不管什么参数,在客户端都手工放入GArray,在服务器端再自己取出来。这确实是个思路,比较适合服务器和客户端都是自己开发的情况。还有一篇"How to pass a variant with dbus-glib " 介绍了怎样用GValue传递复杂的数据类型,读者可以参考。

下面看看在我们的例子中是怎样处理a{sv}参数的:

$ cat sms_features.h
#ifndef SMS_FEATURES_H
#define SMS_FEATURES_H

#include <glib-object.h>

GHashTable *sms_create_features(const char * alphabet, int csm_num, int csm_seq);

GType sms_get_features_type(void);

void sms_release_features(GHashTable *features);

void sms_show_features(GHashTable *features);

#endif

sms_features.h声明了几个函数。这个例子的服务器、客户端都会调用。以下是这些函数的实现:

$ cat -n sms_features.c
1 #include "sms_features.h"
2
3 static void release_val(gpointer data)
4 {
5 GValue *val = (GValue *)data;
6 g_value_unset(val);
7 g_free(val);
8 }
9
10 GHashTable *sms_create_features(const char * alphabet, int csm_num, int csm_seq)
11 {
12 GHashTable *hash;
13 GValue *val;
14
15 hash = g_hash_table_new_full (g_str_hash, NULL, NULL, release_val);
16
17 val = g_new0(GValue, 1);
18 g_value_init (val, G_TYPE_STRING);
19 g_value_set_string (val, alphabet);
20 g_hash_table_insert(hash, "alphabet", val);
21
22 val = g_new0(GValue, 1);
23 g_value_init (val, G_TYPE_INT);
24 g_value_set_int (val, csm_num);
25 g_hash_table_insert(hash, "csm_num", val);
26
27 val = g_new0(GValue, 1);
28 g_value_init (val, G_TYPE_INT);
29 g_value_set_int (val, csm_seq);
30 g_hash_table_insert(hash, "csm_seq", val);
31
32 return hash;
33 }
34
35 GType sms_get_features_type(void)
36 {
37 return dbus_g_type_get_map("GHashTable", G_TYPE_STRING, G_TYPE_VALUE);
38 }
39
40 void sms_show_features(GHashTable *features)
41 {
42 GList *keys = g_hash_table_get_keys(features);
43 gint len = g_list_length(keys);
44 gint i;
45
46 for (i = 0; i < len; i++) {
47 gchar *key = g_list_nth_data(keys, i);
48 GValue *val = g_hash_table_lookup(features, key);
49
50 g_print("%s=", key);
51 switch (G_VALUE_TYPE(val)) {
52 case G_TYPE_STRING:
53 g_print("%s/n", g_value_get_string(val));
54 break;
55 case G_TYPE_INT:
56 g_print("%d/n", g_value_get_int(val));
57 break;
58 default:
59 g_print("Value is of unmanaged type!/n");
60 }
61 }
62
63 g_list_free(keys);
64 }
65
66 void sms_release_features(GHashTable *features)
67 {
68 g_hash_table_destroy(features);
69 }
70

sms_get_features_type调用dbus_g_type_get_map创建a{sv}类型。服务器在创建信号时用到。客户端在调 用方法和注册信号时都会用到。 sms_create_features调用g_hash_table_new_full创建哈希表,在创建的同时登记了值对象的清理函数。在 sms_release_features调用g_hash_table_destroy销毁哈希表时,创建时登记的值对象清理函数会被调用。

5、客户端

5.1、代码

客户端程序如下:

$ cat -n smsc.c
1 #include <dbus/dbus-glib.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <glib/giochannel.h>
6 #include "sms-marshal.h"
7 #include "sms_features.h"
8
9 #define SMSC_DEBUG
10
11 static void lose (const char *str, ...)
12 {
13 va_list args;
14 va_start (args, str);
15 vfprintf (stderr, str, args);
16 fputc ('/n', stderr);
17 va_end (args);
18 exit (1);
19 }
20
21 static void lose_gerror (const char *prefix, GError *error)
22 {
23 if (error) {
24 lose ("%s: %s", prefix, error->message);
25 }
26 else {
27 lose ("%s", prefix);
28 }
29 }
30
31 static void incoming_message_handler (DBusGProxy *proxy, const char *address, const char *contents, GHashTable *features, gpointer user_data)
32 {
33 printf ("Received message with addree /"%s/" and it says: /n%s/n", address, contents);
34 sms_show_features(features);
35 }
36
37 static void send_message(DBusGProxy *remote_object)
38 {
39 GError *error = NULL;
40 GHashTable *features;
41 int ret;
42
43 features = sms_create_features ("gsm", 8, 2);
44 printf("SendMessage ");
45
46 if (!dbus_g_proxy_call (remote_object, "SendMessage", &error,
47 G_TYPE_STRING, "10987654321", G_TYPE_STRING, "hello world",
48 sms_get_features_type(), features, G_TYPE_INVALID,
49 G_TYPE_INT, &ret, G_TYPE_INVALID))
50 lose_gerror ("Failed to complete SendMessage", error);
51
52 printf("return %d/n", ret);
53 sms_release_features(features);
54 }
55
56 static void shell_help(void)
57 {
58 printf( "/ts/tsend message/n"
59 "/tq/tQuit/n"
60 );
61 }
62
63 #define STDIN_BUF_SIZE 1024
64 static gboolean channel_cb(GIOChannel *source, GIOCondition condition, gpointer data)
65 {
66 int rc;
67 char buf[STDIN_BUF_SIZE+1];
68 DBusGProxy *remote_object = (DBusGProxy *)data;
69
70 if (condition != G_IO_IN) {
71 return TRUE;
72 }
73
74 /* we've received something on stdin. */
75 printf("# ");
76 rc = fscanf(stdin, "%s", buf);
77 if (rc <= 0) {
78 printf("NULL/n");
79 return TRUE;
80 }
81
82 if (!strcmp(buf, "h")) {
83 shell_help();
84 } else if (!strcmp(buf, "?")) {
85 shell_help();
86 } else if (!strcmp(buf, "s")) {
87 send_message(remote_object);
88 } else if (!strcmp(buf, "q")) {
89 exit(0);
90 } else {
91 printf("Unknown command `%s'/n", buf);
92 }
93 return TRUE;
94 }
95
96 int main (int argc, char **argv)
97 {
98 DBusGConnection *bus;
99 DBusGProxy *remote_object;
100 GError *error = NULL;
101 GMainLoop *mainloop;
102 GIOChannel *chan;
103 guint source;
104 GType features_type;
105
106 #ifdef SMSC_DEBUG
107 g_slice_set_config(G_SLICE_CONFIG_ALWAYS_MALLOC, TRUE);
108 #endif
109 g_type_init ();
110 mainloop = g_main_loop_new (NULL, FALSE);
111
112 bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
113 if (!bus)
114 lose_gerror ("Couldn't connect to session bus", error);
115
116 remote_object = dbus_g_proxy_new_for_name (bus, "org.freesmartphone.ogsmd",
117 "/org/freesmartphone/GSM/Device",
118 "org.freesmartphone.GSM.SMS");
119 if (!remote_object)
120 lose_gerror ("Failed to get name owner", NULL);
121
122 features_type = sms_get_features_type();
123 dbus_g_object_register_marshaller (sms_marshal_VOID__STRING_STRING_BOXED, G_TYPE_NONE, G_TYPE_STRING, G_TYPE_STRING,
124 features_type, G_TYPE_INVALID);
125 dbus_g_proxy_add_signal (remote_object, "IncomingMessage", G_TYPE_STRING, G_TYPE_STRING, features_type, G_TYPE_INVALID);
126 dbus_g_proxy_connect_signal (remote_object, "IncomingMessage", G_CALLBACK (incoming_message_handler), NULL, NULL);
127
128 chan = g_io_channel_unix_new(0);
129 source = g_io_add_watch(chan, G_IO_IN, channel_cb, remote_object);
130 g_main_loop_run (mainloop);
131 exit (0);
132 }

112行连接会话总线。116-118行在会话总线上获取连接"org.freesmartphone.ogsmd"的对象"/org /freesmartphone/GSM/Device" 的接口"org.freesmartphone.GSM.SMS"的接口代理对象。

123行调用dbus_g_object_register_marshaller向dbus-glib登记列集函数。 125行调用dbus_g_proxy_add_signal增加对信号IncomingMessage的监听。126行登记信号 IncomingMessage的回调函数。 123行登记的还是我们用glib-genmarshal生成的函数sms_marshal_VOID__STRING_STRING_BOXED。 dbus-glib使用这个函数从signal消息中取出信号参数,传递给回调函数,即执行散集操作。这说明glib-genmarshal生成的列集函 数既可以用于列集,也可以用于散集。

客户端程序同样用IO Channel接受用户输入。129行在登记回调函数时将指向接口代理对象的指针作为参数传入。回调函数channel_cb在用户键入's'命令后通过 send_message函数调用org.freesmartphone.GSM.SMS接口对象的SendMessage方法。 107行的设置G_SLICE_CONFIG_ALWAYS_MALLOC同样是为了用valgrind检查内存泄漏。

5.2、执行

我们先运行 dbus-monitor,然后运行smss,再运行smsc。先在smsc中键入's'回车调用SendMessage方法。然后在smss中键入 's'回车发送IncomingMessage信号。然后在smsc中键入'q'回车退出。最后在smss中键入'q'回车退出。

$ ./smss
service is running
number=10987654321
contents=hello world
csm_num=8
alphabet=gsm
csm_seq=2
h
# s send signal
q Quit
s
# q
$ ./smsc
h
# s send message
q Quit
s
# SendMessage return 11
Received message with addree "12345678901" and it says:
hello signal!
csm_num=3
alphabet=ucs2
csm_seq=1
q

我们可以看到打印出来的信号和消息。对于同一件事情,不同的层次的观察者会看到不同的细节,下表是dbus-monitor看到的东西:

 

smss连接会话总线。会话总线发NameOwnerChanged信号,通知唯一名":1.21"被分配。 signal sender=org.freedesktop.DBus -> dest=(null destination) path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameOwnerChanged
   string ":1.21"
   string ""
   string ":1.21"
smss向会话总线发送Hello取得自己的唯一名":1.21"。 method call sender=:1.21 -> dest=org.freedesktop.DBus path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=Hello
smss调用AddMatch要求接收会话总线的NameOwnerChanged信号。 method call sender=:1.21 -> dest=org.freedesktop.DBus path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch
   string "type='signal',sender='org.freedesktop.DBus',path='/org/freedesktop/DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged'"
smss调用AddMatch要求接收会话总线发送的所有信号。 method call sender=:1.21 -> dest=org.freedesktop.DBus path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch
   string "type='signal',sender='org.freedesktop.DBus',path='/',interface='org.freedesktop.DBus'"
smss调用GetNameOwner获取连接"org.freedesktop.DBus"的唯一名。 method call sender=:1.21 -> dest=org.freedesktop.DBus path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=GetNameOwner
   string "org.freedesktop.DBus"
会话总线发送NameOwnerChanged信号,通知唯一名为":1.21"的连接获得了公众名"org.freesmartphone.ogsmd"。 signal sender=org.freedesktop.DBus -> dest=(null destination) path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameOwnerChanged
   string "org.freesmartphone.ogsmd"
   string ""
   string ":1.21"
smss请求公众名"org.freesmartphone.ogsmd"。分配公众名在前,请求公众名在后,应该是监控过程颠倒了消息次序。 method call sender=:1.21 -> dest=org.freedesktop.DBus path=/; interface=org.freedesktop.DBus; member=RequestName
   string "org.freesmartphone.ogsmd"
   uint32 0
smsc连接会话总线。会话总线发NameOwnerChanged信号,通知唯一名":1.22"被分配。 signal sender=org.freedesktop.DBus -> dest=(null destination) path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameOwnerChanged
   string ":1.22"
   string ""
   string ":1.22"
smss向会话总线发送Hello取得自己的唯一名":1.22"。 method call sender=:1.22 -> dest=org.freedesktop.DBus path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=Hello
smsc调用AddMatch要求接收会话总线的NameOwnerChanged信号。 method call sender=:1.22 -> dest=org.freedesktop.DBus path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch
   string "type='signal',sender='org.freedesktop.DBus',path='/org/freedesktop/DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged'"
smsc调用AddMatch要求接收连接'org.freesmartphone.ogsmd'中对象'/org/freesmartphone/GSM/Device'的'org.freesmartphone.GSM.SMS'接口的信号。 method call sender=:1.22 -> dest=org.freedesktop.DBus path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch
   string "type='signal',sender='org.freesmartphone.ogsmd',path='/org/freesmartphone/GSM/Device',interface='org.freesmartphone.GSM.SMS'"
smsc调用GetNameOwner获取连接"org.freesmartphone.ogsmd"的唯一名。 method call sender=:1.22 -> dest=org.freedesktop.DBus path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=GetNameOwner
   string "org.freesmartphone.ogsmd"
smsc调用连接'org.freesmartphone.ogsmd'中对象'/org/freesmartphone/GSM/Device'的'org.freesmartphone.GSM.SMS'接口的SendMessage方法。 method call sender=:1.22 -> dest=org.freesmartphone.ogsmd path=/org/freesmartphone/GSM/Device; interface=org.freesmartphone.GSM.SMS; member=SendMessage
   string "10987654321"
   string "hello world"
   array [
      dict entry(
         string "csm_seq"
         variant int32 2
      )
      dict entry(
         string "alphabet"
         variant string "gsm"
      )
      dict entry(
         string "csm_num"
         variant int32 8
      )
   ]
smss向smsc发送method return消息,返回SendMessage方法的输出参数。 method return sender=:1.21 -> dest=:1.22 reply_serial=5
   int32 11
smss发送IncomingMessage信号。 signal sender=:1.21 -> dest=(null destination) path=/org/freesmartphone/GSM/Device; interface=org.freesmartphone.GSM.SMS; member=IncomingMessage
   string "12345678901"
   string "hello signal!"
   array [
      dict entry(
         string "csm_seq"
         variant int32 1
      )
      dict entry(
         string "alphabet"
         variant string "ucs2"
      )
      dict entry(
         string "csm_num"
         variant int32 3
      )
   ]
会话总线通知连接":1.22",即smsc的连接已经切断。 signal sender=org.freedesktop.DBus -> dest=(null destination) path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameOwnerChanged
   string ":1.22"
   string ":1.22"
   string ""
会话总线通知拥有公共名"org.freesmartphone.ogsmd"的连接已经切断。 signal sender=org.freedesktop.DBus -> dest=(null destination) path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameOwnerChanged
   string "org.freesmartphone.ogsmd"
   string ":1.21"
   string ""
会话总线通知拥有唯一名":1.21"的连接已经切断。即smss已经终止。 signal sender=org.freedesktop.DBus -> dest=(null destination) path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameOwnerChanged
   string ":1.21"
   string ":1.21"
   string ""

6、工程

我提供下载的文件要用make distcheck制作的,其中包含了一些自动生成的文件。执行./clean.sh可以删掉自动生成的文件,只留下我创建的文件:

$ find . -type f
./clean.sh
./Makefile.am
./autogen.sh
./src/gsm_sms.h
./src/Makefile.am
./src/sms-marshal.list
./src/smss.xml
./src/smss.c
./src/gsm_sms.c
./src/sms_features.h
./src/sms_features.c
./src/smsc.c
./configure.ac

前面已经介绍过所有的源文件。我们再看看工程文件:

$ cat autogen.sh
#! /bin/sh
touch `find .`
aclocal
autoconf
autoheader
touch NEWS README AUTHORS ChangeLog
automake --add-missing

$ cat Makefile.am
SUBDIRS = src
EXTRA_DIST = autogen.sh clean.sh

autogen.sh建立工程环境。在执行clean.sh后,执行autogen.sh重新生成configure等工程文件。其中的touch 命令是为了防止文件有将来的时间戳。因为我在虚拟机中运行ubuntu,所以可能会出现这类问题。 Makefile.am将autogen.sh clean.sh也作为发布文件。最重要的工程文件是"configure.ac"和"src/Makefile.am"。

6.1、configure.ac

$ cat -n configure.ac
1 AC_INIT()
2 AM_INIT_AUTOMAKE(hello-dbus5, 0.1)
3 AM_CONFIG_HEADER(config.h)
4
5 AC_PROG_CC
6
7
8 # Dbus detection
9 PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.1, have_dbus=yes, have_dbus=no)
10
11 if test x$have_dbus = xno ; then
12 AC_MSG_ERROR([DBus development libraries not found])
13 fi
14 AM_CONDITIONAL(HAVE_DBUS, test x$have_dbus = xyes)
15
16 AC_SUBST(DBUS_CFLAGS)
17 AC_SUBST(DBUS_LIBS)
18
19
20 # Glib detection
21 PKG_CHECK_MODULES(DBUS_GLIB, gobject-2.0 >= 2.6, have_glib=yes, have_glib=no)
22
23 if test x$have_glib = xno ; then
24 AC_MSG_ERROR([GLib development libraries not found])
25 fi
26
27 AM_CONDITIONAL(HAVE_GLIB, test x$have_glib = xyes)
28
29 AC_SUBST(DBUS_GLIB_CFLAGS)
30 AC_SUBST(DBUS_GLIB_LIBS)
31
32
33 AC_OUTPUT([Makefile
34 src/Makefile])

8-17行检查dbus库,它们会生成编译常数DBUS_CFLAGS和DBUS_LIBS。 20-30行检查dbus-glib库,它们会生成编译常数DBUS_GLIB_CFLAGS和DBUS_GLIB_LIBS。

6.2、src/Makefile.am

$ cat -n src/Makefile.am
1 INCLUDES = /
2 $(DBUS_CFLAGS) /
3 $(DBUS_GLIB_CFLAGS) /
4 -DDBUS_COMPILATION
5
6 LIBS = /
7 $(DBUS_LIBS) /
8 $(DBUS_GLIB_LIBS) /
9 -ldbus-glib-1
10
11 # smss
12 noinst_PROGRAMS = smss
13
14 BUILT_SOURCES = smss-glue.h sms-marshal.h sms-marshal.c
15 smss_SOURCES = $(BUILT_SOURCES) smss.c gsm_sms.c sms_features.c
16 noinst_HEADERS = gsm_sms.h sms_features.h
17
18
19 smss-glue.h: smss.xml
20 $(LIBTOOL) --mode=execute dbus-binding-tool --prefix=gsm_sms --mode=glib-server --output=smss-glue.h $(srcdir)/smss.xml
21
22 sms-marshal.h: sms-marshal.list
23 $(LIBTOOL) --mode=execute glib-genmarshal --header sms-marshal.list --prefix=sms_marshal > sms-marshal.h
24
25 sms-marshal.c: sms-marshal.list
26 $(LIBTOOL) --mode=execute glib-genmarshal --body sms-marshal.list --prefix=sms_marshal > sms-marshal.c
27
28 CLEANFILES = $(BUILT_SOURCES)
29
30 EXTRA_DIST = smss.xml sms-marshal.list
31
32 # smss
33 noinst_PROGRAMS += smsc
34 smsc_SOURCES= smsc.c sms-marshal.c sms_features.c

19-20行由接口描述文件smss.xml生成存根文件smss-glue.h。22-26行由列集接口定义生成包含列集函数的代码。

7、结束语

本文介绍了一个简单的dbus-glib的例子,包括服务器和客户端。第一讲中还有一个加法例子,如果你理解了本文的例子,那个例子就更简单了。 dbus-glib源代码中有两个例子:

  • example-service和example-client演示方法调用。这个例子的接口描述文件中有个参数类型写错了,将(us)写成(ss),运行时会出错。可能作者想演示一下接口定义与代码实现不一致的后果吧。读者可以从这里 下载我修改过的代码。
  • example-signal-emitter和example-signal-recipient演示信号发射。这个例子中,example- signal-recipient调用example-signal-emitter的方法请求发送信号。实际上信号应该是来自服务器侧的信息。我将其改 成在example-signal-emitter中敲键发送信号。读者可以从这里 下载我修改过的代码。

好了,《dbus实例讲解》到此结束。其实我的所有文章只是希望能让这复杂的世界简单一点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值