dbus-glib是dbus底層接口的一個封裝。本講我們用dbus-glib做一個dus接口,並寫一個客戶程序。
1、接口
1.1、編寫接口描述文件
首先編寫接口描述文件。我們要實現的連接的公共名是"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接口的一部分。 但爲簡單起見,本例在哈希表部分,只用三個鍵值。
- 鍵"alphabet"對應的值類型是字符串。
- 鍵"csm_num"對應的值類型是INT32。
- 鍵"csm_seq"對應的值類型是INT32。
請注意方法和信號名應採用單詞連寫,首字母大寫的格式。
1.2、由接口描述文件生成綁定文件
有一個叫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" };
在包含綁定文件前,我們必須聲明綁定文件要引用的回調函數。
2 對象
2.1 對象定義
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函數未聲明。
2.2 信號的列集函數
列集(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); }
2.3 對象實現
準備好列集函數後,我們來實現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函數供外部模塊調用。 調用這個函數可以發射一個信號。在真實環境中,只有外部事件發生後纔會發射信號。 本例中會有測試代碼發射信號。
3 主程序
3.1 登記dbus服務器
下面就是主程序
$ 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行進入主循環等待客戶消息。
3.2 IO Channel
我想增加一個敲鍵測試信號發射。但我又必須在glib主循環裏等待dbus消息。怎樣才能既等待dbus消息,又等待敲鍵呢? 這種情況可以使用glib的IO Channels。glib的IO Channels允許我們在glib主循環等待指定的文件或socket句柄。
要使用IO Channels,首先包含"glib/giochannel.h"。116行用句柄0(即標準輸入)創建一個GIOChannel。 117行爲我們創建的GIOChannel登記回調函數。我們在回調函數channel_cb中處理敲鍵,發射信號。
3.3 編譯運行
讀者可以從這裏下載完整的示例程序。 下集會介紹本例的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