dbus使用講解- 下(轉)


轉自:http://blog.csdn.net/fmddlmyy/article/details/3798048

 

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實例講解》到此結束。其實我的所有文章只是希望能讓這復雜的世界簡單一點。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM