dbus-glib是dbus底层接口的一个封装。本讲我们用dbus-glib做一个dus接口,并写一个客户程序。
首先编写接口描述文件。我们要实现的连接的公共名是"org.freesmartphone.ogsmd",接口描述文件如下:
$ cat smss.xml <?xml version="1.0" encoding="UTF-8" ?> <node name="/org/freesmartphone/GSM/Device"> <interface name="org.freesmartphone.GSM.SMS"> <method name="SendMessage"> <arg name="number" type="s"/> <arg name="contents" type="s"/> <arg name="featuremap" type="a{sv}"/> <arg type="i" direction="out"/> </method> <signal name="IncomingMessage"> <arg name="address" type="s"/> <arg name="contents" type="s"/> <arg name="features" type="a{sv}"/> </signal> </interface> </node>
我们要在连接"org.freesmartphone.ogsmd"中实现对象"/org/freesmartphone/GSM/Device"。这个对象有接口"org.freesmartphone.GSM.SMS"。这个接口有一个SendMessage方法和一个IncomingMessage信号。
SendMessage方法和IncomingMessage信号除了两个字符串参数外,还有一个a{sv}参数,这是一个哈希表,即python的字典。键-值对的键类型是字符串,值类型是VARIANT。这个接口是openmoko fso接口的一部分。但为简单起见,本例在哈希表部分,只用三个键值。
请注意方法和信号名应采用单词连写,首字母大写的格式。
有一个叫dbus-binding-tool的工具,它读入接口描述文件,产生一个绑定文件。这个文件包含了dbus对象的接口信息。在主程序中我们通过dbus_g_object_type_install_info函数向dbus-glib登记对象信息(DBusGObjectInfo结构)。
本例使用了autotool,在Makefile.am中可以这样调用dbus-binding-tool:
smss-glue.h: smss.xml $(LIBTOOL) --mode=execute dbus-binding-tool --prefix=gsm_sms --mode=glib-server --output=smss-glue.h $(srcdir)/smss.xml
"--prefix"参数定义了对象前缀。设对象前缀是$(prefix),则生成的DBusGObjectInfo结构变量名就是dbus_glib_$(prefix)_object_info。绑定文件会为接口方法定义回调函数。回调函数的名称是这样的:首先将xml中的方法名称转换到全部小写,下划线分隔的格式,然后增加前缀"$(prefix)_"。例如:如果xml中有方法SendMessage,绑定文件就会引用一个名称为$(prefix)_send_message的函数。
绑定文件还会为接口方法生成用于散集(Unmarshaling)的函数。在dbus消息中,方法参数是以流格式存在的。该函数将方法参数由数据流还原到glib的数据格式,并传入方法的回调函数。本例中,dbus-binding-tool生成以下的smss-glue.h:
$ cat smss-glue.h /* Generated by dbus-binding-tool; do not edit! */ #ifndef __dbus_glib_marshal_gsm_sms_MARSHAL_H__ #define __dbus_glib_marshal_gsm_sms_MARSHAL_H__ #include <glib-object.h> G_BEGIN_DECLS #ifdef G_ENABLE_DEBUG #define g_marshal_value_peek_boolean(v) g_value_get_boolean (v) #define g_marshal_value_peek_char(v) g_value_get_char (v) #define g_marshal_value_peek_uchar(v) g_value_get_uchar (v) #define g_marshal_value_peek_int(v) g_value_get_int (v) #define g_marshal_value_peek_uint(v) g_value_get_uint (v) #define g_marshal_value_peek_long(v) g_value_get_long (v) #define g_marshal_value_peek_ulong(v) g_value_get_ulong (v) #define g_marshal_value_peek_int64(v) g_value_get_int64 (v) #define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v) #define g_marshal_value_peek_enum(v) g_value_get_enum (v) #define g_marshal_value_peek_flags(v) g_value_get_flags (v) #define g_marshal_value_peek_float(v) g_value_get_float (v) #define g_marshal_value_peek_double(v) g_value_get_double (v) #define g_marshal_value_peek_string(v) (char*) g_value_get_string (v) #define g_marshal_value_peek_param(v) g_value_get_param (v) #define g_marshal_value_peek_boxed(v) g_value_get_boxed (v) #define g_marshal_value_peek_pointer(v) g_value_get_pointer (v) #define g_marshal_value_peek_object(v) g_value_get_object (v) #else /* !G_ENABLE_DEBUG */ /* WARNING: This code accesses GValues directly, which is UNSUPPORTED API. * Do not access GValues directly in your code. Instead, use the * g_value_get_*() functions */ #define g_marshal_value_peek_boolean(v) (v)->data[0].v_int #define g_marshal_value_peek_char(v) (v)->data[0].v_int #define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint #define g_marshal_value_peek_int(v) (v)->data[0].v_int #define g_marshal_value_peek_uint(v) (v)->data[0].v_uint #define g_marshal_value_peek_long(v) (v)->data[0].v_long #define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong #define g_marshal_value_peek_int64(v) (v)->data[0].v_int64 #define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64 #define g_marshal_value_peek_enum(v) (v)->data[0].v_long #define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong #define g_marshal_value_peek_float(v) (v)->data[0].v_float #define g_marshal_value_peek_double(v) (v)->data[0].v_double #define g_marshal_value_peek_string(v) (v)->data[0].v_pointer #define g_marshal_value_peek_param(v) (v)->data[0].v_pointer #define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer #define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer #define g_marshal_value_peek_object(v) (v)->data[0].v_pointer #endif /* !G_ENABLE_DEBUG */ /* BOOLEAN:STRING,STRING,BOXED,POINTER,POINTER (/tmp/dbus-binding-tool-c-marshallers.3YAGNU:1) */ extern void dbus_glib_marshal_gsm_sms_BOOLEAN__STRING_STRING_BOXED_POINTER_POINTER (GClosure *closure, GValue *return_value, guint n_param_values, const GValue *param_values, gpointer invocation_hint, gpointer marshal_data); void dbus_glib_marshal_gsm_sms_BOOLEAN__STRING_STRING_BOXED_POINTER_POINTER (GClosure *closure, GValue *return_value, guint n_param_values, const GValue *param_values, gpointer invocation_hint, gpointer marshal_data) { typedef gboolean (*GMarshalFunc_BOOLEAN__STRING_STRING_BOXED_POINTER_POINTER) (gpointer data1, gpointer arg_1, gpointer arg_2, gpointer arg_3, gpointer arg_4, gpointer arg_5, gpointer data2); register GMarshalFunc_BOOLEAN__STRING_STRING_BOXED_POINTER_POINTER callback; register GCClosure *cc = (GCClosure*) closure; register gpointer data1, data2; gboolean v_return; g_return_if_fail (return_value != NULL); g_return_if_fail (n_param_values == 6); if (G_CCLOSURE_SWAP_DATA (closure)) { data1 = closure->data; data2 = g_value_peek_pointer (param_values + 0); } else { data1 = g_value_peek_pointer (param_values + 0); data2 = closure->data; } callback = (GMarshalFunc_BOOLEAN__STRING_STRING_BOXED_POINTER_POINTER) (marshal_data ? marshal_data : cc->callback); v_return = callback (data1, g_marshal_value_peek_string (param_values + 1), g_marshal_value_peek_string (param_values + 2), g_marshal_value_peek_boxed (param_values + 3), g_marshal_value_peek_pointer (param_values + 4), g_marshal_value_peek_pointer (param_values + 5), data2); g_value_set_boolean (return_value, v_return); } G_END_DECLS #endif /* __dbus_glib_marshal_gsm_sms_MARSHAL_H__ */ #include static const DBusGMethodInfo dbus_glib_gsm_sms_methods[] = { { (GCallback) gsm_sms_send_message, dbus_glib_marshal_gsm_sms_BOOLEAN__STRING_STRING_BOXED_POINTER_POINTER, 0 }, }; const DBusGObjectInfo dbus_glib_gsm_sms_object_info = { 0, dbus_glib_gsm_sms_methods, 1, "org.freesmartphone.GSM.SMS\0SendMessage\0S\0number\0I\0s\0contents\0I\0s\0featuremap\0I\0a{sv}\0arg3\0O\0F\0N\0i\0\0\0", "org.freesmartphone.GSM.SMS\0IncomingMessage\0\0", "\0" };
在包含绑定文件前,我们必须声明绑定文件要引用的回调函数。
dbus-glib用GObject实现dbus对象。所以我们首先要实现一个对象。在本例中,我们实现一个GsmSms对象,它继承了GObject:
$ cat gsm_sms.h #ifndef GSM_SMS_H #define GSM_SMS_H typedef struct GsmSms GsmSms; typedef struct GsmSmsClass GsmSmsClass; struct GsmSms { GObject parent; }; struct GsmSmsClass { GObjectClass parent; }; #define GSM_SMS_TYPE (gsm_sms_get_type ()) GType gsm_sms_get_type (void); gboolean gsm_sms_send_message (GsmSms *obj, const char *number, const char *contents, GHashTable *featuremap, int *ret, GError **error); void gsm_sms_emit_incoming_message(GsmSms *obj, const char * address, const char * contents, GHashTable *hash); #endif
GObject的对象定义虽然繁琐,但有固定的套路。依样画葫芦,画多了就习惯了。我们在gsm_sms.h中声明了gsm_sms_send_message函数。 gsm_sms_send_message函数是在gsm_sms.c中实现的,在绑定文件smss-glue.h中用到。因为主程序要使用绑定文件中的对象信息,所以应由主程序包含绑定文件。主程序只要在包含绑定文件前包含gsm_sms.h,编译器就不会抱怨gsm_sms_send_message函数未声明。
列集(Marshaling)是将数据从某种格式存为流格式的操作;散集(Unmarshaling)则是列集的反操作,将信息从流格式中还原出来。在绑定文件中,dbus-binding-tool自动生成函数将方法参数从dbus消息中还原出来,即实现了散集。那么我们怎么把信号参数由glib的数据结构转换到消息中的数据流呢?
因为GsmSms对象有一个信号,所以在对象类初始化函数gsm_sms_class_init中,我们要调用g_signal_new创建信号。 g_signal_new要求我们提供一个列集函数。
glib有一些标准的列集函数,在gmarshal.h中定义。例如g_cclosure_marshal_VOID__STRING,这个函数适合只有一个字符串参数的信号。如果gmarshal.h没有提供适合的列集函数,我们可以用一个叫glib-genmarshal的工具自动生成列集函数。后面我们会看到,无论是标准列集函数还是生成的列集函数都是既可以用于列集也可以用于散集,这些函数通常都被称作列集函数。
使用glib-genmarshal前,我们同样要准备一个输入文件:
$ cat sms-marshal.list # see glib-genmarshal(1) for a detailed description of the file format, # possible parameter types are: # VOID indicates no return type, or no extra # parameters. if VOID is used as the parameter # list, no additional parameters may be present. # BOOLEAN for boolean types (gboolean) # CHAR for signed char types (gchar) # UCHAR for unsigned char types (guchar) # INT for signed integer types (gint) # UINT for unsigned integer types (guint) # LONG for signed long integer types (glong) # ULONG for unsigned long integer types (gulong) # ENUM for enumeration types (gint) # FLAGS for flag enumeration types (guint) # FLOAT for single-precision float types (gfloat) # DOUBLE for double-precision float types (gdouble) # STRING for string types (gchar*) # PARAM for GParamSpec or derived types (GParamSpec*) # BOXED for boxed (anonymous but reference counted) types (GBoxed*) # POINTER for anonymous pointer types (gpointer) # OBJECT for GObject or derived types (GObject*) # NONE deprecated alias for VOID # BOOL deprecated alias for BOOLEAN VOID:STRING,STRING,BOXED
我们需要的函数返回类型是VOID,参数是STRING,STRING,BOXED。在Makefile.am中可以这样调用glib-genmarshal:
sms-marshal.h: sms-marshal.list $(LIBTOOL) --mode=execute glib-genmarshal --header sms-marshal.list --prefix=sms_marshal > sms-marshal.h sms-marshal.c: sms-marshal.list $(LIBTOOL) --mode=execute glib-genmarshal --body sms-marshal.list --prefix=sms_marshal > sms-marshal.c
"--prefix"和函数原型决定输出函数名。如果"--prefix=sms_marshal",函数原型是"OID:STRING,STRING,BOXED",生成的列集函数名就必然是sms_marshal_VOID__STRING_STRING_BOXED。
在本例中自动生成的文件内容如下:
$ cat sms-marshal.h #ifndef __sms_marshal_MARSHAL_H__ #define __sms_marshal_MARSHAL_H__ #include <glib-object.h> G_BEGIN_DECLS /* VOID:STRING,STRING,BOXED (sms-marshal.list:24) */ extern void sms_marshal_VOID__STRING_STRING_BOXED (GClosure *closure, GValue *return_value, guint n_param_values, const GValue *param_values, gpointer invocation_hint, gpointer marshal_data); G_END_DECLS #endif /* __sms_marshal_MARSHAL_H__ */ $ cat sms-marshal.c #include <glib-object.h> #ifdef G_ENABLE_DEBUG #define g_marshal_value_peek_boolean(v) g_value_get_boolean (v) #define g_marshal_value_peek_char(v) g_value_get_char (v) #define g_marshal_value_peek_uchar(v) g_value_get_uchar (v) #define g_marshal_value_peek_int(v) g_value_get_int (v) #define g_marshal_value_peek_uint(v) g_value_get_uint (v) #define g_marshal_value_peek_long(v) g_value_get_long (v) #define g_marshal_value_peek_ulong(v) g_value_get_ulong (v) #define g_marshal_value_peek_int64(v) g_value_get_int64 (v) #define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v) #define g_marshal_value_peek_enum(v) g_value_get_enum (v) #define g_marshal_value_peek_flags(v) g_value_get_flags (v) #define g_marshal_value_peek_float(v) g_value_get_float (v) #define g_marshal_value_peek_double(v) g_value_get_double (v) #define g_marshal_value_peek_string(v) (char*) g_value_get_string (v) #define g_marshal_value_peek_param(v) g_value_get_param (v) #define g_marshal_value_peek_boxed(v) g_value_get_boxed (v) #define g_marshal_value_peek_pointer(v) g_value_get_pointer (v) #define g_marshal_value_peek_object(v) g_value_get_object (v) #else /* !G_ENABLE_DEBUG */ /* WARNING: This code accesses GValues directly, which is UNSUPPORTED API. * Do not access GValues directly in your code. Instead, use the * g_value_get_*() functions */ #define g_marshal_value_peek_boolean(v) (v)->data[0].v_int #define g_marshal_value_peek_char(v) (v)->data[0].v_int #define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint #define g_marshal_value_peek_int(v) (v)->data[0].v_int #define g_marshal_value_peek_uint(v) (v)->data[0].v_uint #define g_marshal_value_peek_long(v) (v)->data[0].v_long #define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong #define g_marshal_value_peek_int64(v) (v)->data[0].v_int64 #define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64 #define g_marshal_value_peek_enum(v) (v)->data[0].v_long #define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong #define g_marshal_value_peek_float(v) (v)->data[0].v_float #define g_marshal_value_peek_double(v) (v)->data[0].v_double #define g_marshal_value_peek_string(v) (v)->data[0].v_pointer #define g_marshal_value_peek_param(v) (v)->data[0].v_pointer #define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer #define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer #define g_marshal_value_peek_object(v) (v)->data[0].v_pointer #endif /* !G_ENABLE_DEBUG */ /* VOID:STRING,STRING,BOXED (sms-marshal.list:24) */ void sms_marshal_VOID__STRING_STRING_BOXED (GClosure *closure, GValue *return_value, guint n_param_values, const GValue *param_values, gpointer invocation_hint, gpointer marshal_data) { typedef void (*GMarshalFunc_VOID__STRING_STRING_BOXED) (gpointer data1, gpointer arg_1, gpointer arg_2, gpointer arg_3, gpointer data2); register GMarshalFunc_VOID__STRING_STRING_BOXED callback; register GCClosure *cc = (GCClosure*) closure; register gpointer data1, data2; g_return_if_fail (n_param_values == 4); if (G_CCLOSURE_SWAP_DATA (closure)) { data1 = closure->data; data2 = g_value_peek_pointer (param_values + 0); } else { data1 = g_value_peek_pointer (param_values + 0); data2 = closure->data; } callback = (GMarshalFunc_VOID__STRING_STRING_BOXED) (marshal_data ? marshal_data : cc->callback); callback (data1, g_marshal_value_peek_string (param_values + 1), g_marshal_value_peek_string (param_values + 2), g_marshal_value_peek_boxed (param_values + 3), data2); }
准备好列集函数后,我们来实现GsmSms。
$ cat -n gsm_sms.c 1 #include <dbus/dbus-glib.h> 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <string.h> 5 #include "gsm_sms.h" 6 #include "sms-marshal.h" 7 #include "sms_features.h" 8 9 enum 10 { 11 INCOMING_MESSAGE, 12 LAST_SIGNAL 13 }; 14 15 static guint signals[LAST_SIGNAL]; 16 17 G_DEFINE_TYPE(GsmSms, gsm_sms, G_TYPE_OBJECT) 18 19 static void gsm_sms_init (GsmSms *obj) 20 { 21 } 22 23 static void gsm_sms_class_init (GsmSmsClass *klass) 24 { 25 signals[INCOMING_MESSAGE] = g_signal_new ( 26 "incoming_message", 27 G_OBJECT_CLASS_TYPE (klass), 28 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 29 0, 30 NULL, NULL, 31 sms_marshal_VOID__STRING_STRING_BOXED, 32 G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_STRING, 33 sms_get_features_type()); 34 } 35 36 gboolean gsm_sms_send_message (GsmSms *obj, const char *number, const char *contents, 37 GHashTable *featuremap, int *ret, GError **error) 38 { 39 printf("number=%s\n", number); 40 printf("contents=%s\n", contents); 41 sms_show_features(featuremap); 42 *ret = strlen(contents); 43 return TRUE; 44 } 45 46 void gsm_sms_emit_incoming_message(GsmSms *obj, const char * address, 47 const char * contents, GHashTable *hash) 48 { 49 g_signal_emit (obj, signals[INCOMING_MESSAGE], 0, address, contents, hash); 50 }
在类初始化函数gsm_sms_class_init中,我们调用g_signal_new创建了信号。g_signal_new函数的原型是:
guint g_signal_new (const gchar *signal_name, GType itype, GSignalFlags signal_flags, guint class_offset, GSignalAccumulator accumulator, gpointer accu_data, GSignalCMarshaller c_marshaller, GType return_type, guint n_params, ...);
31行提供了列集函数。32-33行是返回值类型和参数类型。其中第三个参数调用了函数sms_get_features_type(在sms_features.h中声明)。因为a{sv}类型的参数处理起来比较繁琐,我专门写了一个sms_features模块处理,后面会介绍。
在主程序中登记对象信息时,对象信息把SendMessage方法和gsm_sms_send_message函数以及自动生成的散集函数联系起来。当客户程序调用SendMessage方法时,dbus-glib会通过对象信息表格找到回调函数和散集函数,用散集函数从method_call消息中取出参数传入回调函数gsm_sms_send_message。 gsm_sms_send_message调用sms_show_features函数处理a{sv}参数。 sms_show_features也在sms_features模块定义,后面会介绍。
gsm_sms模块提供了一个gsm_sms_emit_incoming_message函数供外部模块调用。调用这个函数可以发射一个信号。在真实环境中,只有外部事件发生后才会发射信号。本例中会有测试代码发射信号。
下面就是主程序
$ cat -n smss.c 1 #include <dbus/dbus-glib.h> 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <glib/giochannel.h> 5 #include "gsm_sms.h" 6 #include "smss-glue.h" 7 #include "sms_features.h" 8 9 #define SMSS_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 shell_help(void) 32 { 33 printf( "\ts\tsend signal\n" 34 "\tq\tQuit\n" 35 ); 36 } 37 38 void emit_signal(GsmSms *obj) 39 { 40 GHashTable *features = sms_create_features("ucs2", 3, 1); 41 gsm_sms_emit_incoming_message(obj, "12345678901", "hello signal!", features); 42 sms_release_features(features); 43 } 44 45 #define STDIN_BUF_SIZE 1024 46 static gboolean channel_cb(GIOChannel *source, GIOCondition condition, gpointer data) 47 { 48 int rc; 49 char buf[STDIN_BUF_SIZE+1]; 50 GsmSms *obj = (GsmSms *)data; 51 52 if (condition != G_IO_IN) { 53 return TRUE; 54 } 55 56 /* we've received something on stdin. */ 57 printf("# "); 58 rc = fscanf(stdin, "%s", buf); 59 if (rc <= 0) { 60 printf("NULL\n"); 61 return TRUE; 62 } 63 64 if (!strcmp(buf, "h")) { 65 shell_help(); 66 } else if (!strcmp(buf, "?")) { 67 shell_help(); 68 } else if (!strcmp(buf, "s")) { 69 emit_signal(obj); 70 } else if (!strcmp(buf, "q")) { 71 exit(0); 72 } else { 73 printf("Unknown command `%s'\n", buf); 74 } 75 return TRUE; 76 } 77 78 int main (int argc, char **argv) 79 { 80 DBusGConnection *bus; 81 DBusGProxy *bus_proxy; 82 GError *error = NULL; 83 GsmSms *obj; 84 GMainLoop *mainloop; 85 guint request_name_result; 86 GIOChannel *chan; 87 88 #ifdef SMSS_DEBUG 89 g_slice_set_config(G_SLICE_CONFIG_ALWAYS_MALLOC, TRUE); 90 #endif 91 g_type_init (); 92 93 dbus_g_object_type_install_info (GSM_SMS_TYPE, &dbus_glib_gsm_sms_object_info); 94 95 mainloop = g_main_loop_new (NULL, FALSE); 96 97 bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error); 98 if (!bus) 99 lose_gerror ("Couldn't connect to system bus", error); 100 101 bus_proxy = dbus_g_proxy_new_for_name (bus, "org.freedesktop.DBus", 102 "/", "org.freedesktop.DBus"); 103 104 if (!dbus_g_proxy_call (bus_proxy, "RequestName", &error, 105 G_TYPE_STRING, "org.freesmartphone.ogsmd", 106 G_TYPE_UINT, 0, 107 G_TYPE_INVALID, 108 G_TYPE_UINT, &request_name_result, 109 G_TYPE_INVALID)) 110 lose_gerror ("Failed to acquire org.freesmartphone.ogsmd", error); 111 112 obj = g_object_new (GSM_SMS_TYPE, NULL); 113 dbus_g_connection_register_g_object (bus, "/org/freesmartphone/GSM/Device", G_OBJECT (obj)); 114 115 printf ("service is running\n"); 116 chan = g_io_channel_unix_new(0); 117 g_io_add_watch(chan, G_IO_IN, channel_cb, obj); 118 g_main_loop_run (mainloop); 119 120 exit (0); 121 }
93行调用dbus_g_object_type_install_info登记GsmSms类的接口信息。97行连接会话总线。 101-102行在会话总线上为连接"org.freedesktop.DBus"的"/"对象的接口"org.freedesktop.DBus"建立代理。 104-109行通过接口代理调用"RequestName"方法,请求公共名"org.freesmartphone.ogsmd"。
请求公共名成功后,112行建立GsmSms对象。113行登记GsmSms对象,登记时指定对象路径"/org/freesmartphone/GSM/Device",并传入对象指针。118行进入主循环等待客户消息。
我想增加一个敲键测试信号发射。但我又必须在glib主循环里等待dbus消息。怎样才能既等待dbus消息,又等待敲键呢?这种情况可以使用glib的IO Channels。glib的IO Channels允许我们在glib主循环等待指定的文件或socket句柄。
要使用IO Channels,首先包含"glib/giochannel.h"。116行用句柄0(即标准输入)创建一个GIOChannel。 117行为我们创建的GIOChannel登记回调函数。我们在回调函数channel_cb中处理敲键,发射信号。
读者可以从这里下载完整的示例程序。下集会介绍本例的autotool工程。目前,我们先编译运行一下,解压后执行:
$ ./configure $ make $ cd src $ ./smss service is running h # s send signal q Quit
键入h后回车,可以看到敲键的帮助信息。
我想找个客户程序测试一下,dbus-send不能发a{sv}这样复杂的参数。我们可以用d-feet测试,或者写个python脚本:
$ cat ./smsc.py #!/usr/bin/env python import dbus bus=dbus.SessionBus() bus_obj=bus.get_object('org.freesmartphone.ogsmd', '/org/freesmartphone/GSM/Device') iface=dbus.Interface(bus_obj, 'org.freesmartphone.GSM.SMS') ret=iface.SendMessage('1234567890', 'hello from python', {'alphabet':'gsm','csm_num':8,'csm_seq':2}) print "SendMessage return %d" % (ret)
执行smsc.py,在服务器端看到:
$ ./smss service is running h # s send signal q Quit number=1234567890 contents=hello from python csm_num=8 alphabet=gsm csm_seq=2
说明服务器可以正常工作。主程序的89行要求glib直接用malloc分配内存,这样用valgrind才能检查到内存泄漏(如果有的话)。我们可以这样运行smss以检查是否有内存泄漏:
$ valgrind --tool=memcheck --leak-check=full ./smss