天天看点

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

<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; inter:1.21"

   string ""

   string ":1.21"

smss向会话总线发送Hello取得自己的唯一名":1.21"。 method call sender=:1.21 -> dest=org.freedesktop.DBus path=/org/freedesktop/DBus; intertype='signal',sender='org.freedesktop.DBus',path='/org/freedesktop/DBus',inter,member='NameOwnerChanged'"
smss调用AddMatch要求接收会话总线发送的所有信号。 method call sender=:1.21 -> dest=org.freedesktop.DBus path=/org/freedesktop/DBus; intertype='signal',sender='org.freedesktop.DBus',path='/',inter"
smss调用GetNameOwner获取连接"org.freedesktop.DBus"的唯一名。 method call sender=:1.21 -> dest=org.freedesktop.DBus path=/org/freedesktop/DBus; interorg.freedesktop.DBus"
会话总线发送NameOwnerChanged信号,通知唯一名为":1.21"的连接获得了公众名"org.freesmartphone.ogsmd"。

signal sender=org.freedesktop.DBus -> dest=(null destination) path=/org/freedesktop/DBus; interorg.freesmartphone.ogsmd"

   string ""

   string ":1.21"

smss请求公众名"org.freesmartphone.ogsmd"。分配公众名在前,请求公众名在后,应该是监控过程颠倒了消息次序。

method call sender=:1.21 -> dest=org.freedesktop.DBus path=/; interorg.freesmartphone.ogsmd"

   uint32 0

smsc连接会话总线。会话总线发NameOwnerChanged信号,通知唯一名":1.22"被分配。

signal sender=org.freedesktop.DBus -> dest=(null destination) path=/org/freedesktop/DBus; inter:1.22"

   string ""

   string ":1.22"

smss向会话总线发送Hello取得自己的唯一名":1.22"。 method call sender=:1.22 -> dest=org.freedesktop.DBus path=/org/freedesktop/DBus; intertype='signal',sender='org.freedesktop.DBus',path='/org/freedesktop/DBus',inter,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; intertype='signal',sender='org.freesmartphone.ogsmd',path='/org/freesmartphone/GSM/Device',inter"
smsc调用GetNameOwner获取连接"org.freesmartphone.ogsmd"的唯一名。 method call sender=:1.22 -> dest=org.freedesktop.DBus path=/org/freedesktop/DBus; interorg.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; inter10987654321"

   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; inter12345678901"

   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; inter:1.22"

   string ":1.22"

   string ""

会话总线通知拥有公共名"org.freesmartphone.ogsmd"的连接已经切断。

signal sender=org.freedesktop.DBus -> dest=(null destination) path=/org/freedesktop/DBus; interorg.freesmartphone.ogsmd"

   string ":1.21"

   string ""

会话总线通知拥有唯一名":1.21"的连接已经切断。即smss已经终止。

signal sender=org.freedesktop.DBus -> dest=(null destination) path=/org/freedesktop/DBus; inter: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实例讲解》到此结束。其实我的所有文章只是希望能让这复杂的世界简单一点。