有没有办法检查是否有人听dbus信号?

时间:2015-05-12 16:33:56

标签: c dbus gdbus

有没有办法检查DBus中的侦听客户端?

甚至可以吗?我正在使用gdbus。

背景

我正在创建与串行端口接口的服务,如果有人在监听,我想隐式打开串口,如果最后一个客户端断开连接则自动关闭它。我可以使用打开/关闭方法来执行此操作,但是当其他客户端仍在侦听时,存在一个客户端关闭连接的风险。

我的问题的另一个解决方案是连接计数,但客户端也会忘记关闭端口或崩溃。

你还有其他想法如何实现这个吗?

我的代码(缩写)

基于: https://github.com/bratsche/glib/blob/master/gio/tests/gdbus-example-server.c

#include <gio/gio.h>
#include <stdlib.h>

#ifdef G_OS_UNIX
#include <unistd.h>
#endif

/* ---------------------------------------------------------------------------------------------------- */

static GDBusNodeInfo *introspection_data = NULL;

/* Introspection data for the service we are exporting */
static const gchar introspection_xml[] =
  "<node>"
  "  <interface name='info.skorepa.serial.port'>"
  "    <signal name='DataRecieved'>"
  "      <arg type='ay' name='data'/>"
  "    </signal>"
  "  </interface>"
  "</node>";

/* ---------------------------------------------------------------------------------------------------- */

static void
handle_method_call (GDBusConnection       *connection,
                    const gchar           *sender,
                    const gchar           *object_path,
                    const gchar           *interface_name,
                    const gchar           *method_name,
                    GVariant              *parameters,
                    GDBusMethodInvocation *invocation,
                    gpointer               user_data)
{
  // nothing - signal only
}

static GVariant *
handle_get_property (GDBusConnection  *connection,
                     const gchar      *sender,
                     const gchar      *object_path,
                     const gchar      *interface_name,
                     const gchar      *property_name,
                     GError          **error,
                     gpointer          user_data)
{
  // nothing - signal only
}

static gboolean
handle_set_property (GDBusConnection  *connection,
                     const gchar      *sender,
                     const gchar      *object_path,
                     const gchar      *interface_name,
                     const gchar      *property_name,
                     GVariant         *value,
                     GError          **error,
                     gpointer          user_data)
{
  // nothing - no properties
}


/* for now */
static const GDBusInterfaceVTable interface_vtable =
{
  handle_method_call,
  handle_get_property,
  handle_set_property
};

/* ---------------------------------------------------------------------------------------------------- */
// Here I emit signal - for now I just emit every 2 seconds
static gboolean
on_timeout_cb (gpointer user_data)
{
  GDBusConnection *connection = G_DBUS_CONNECTION (user_data);
  GVariantBuilder *builder;
  GVariantBuilder *invalidated_builder;
  GError *error;

  error = NULL;
  printf("Constructing array\n");
  builder = g_variant_builder_new (G_VARIANT_TYPE ("ay"));
  printf("Adding 65\n");
  g_variant_builder_add (builder,
                         "y",
                         65);
  printf("Adding 66\n");
  g_variant_builder_add (builder,
                         "y",
                         66);
  printf("Emitting signal\n");
  g_dbus_connection_emit_signal (connection,
                                 NULL,
                                 "/info/skorepa/TestObject",
                                 "info.skorepa.serial.port",
                                 "DataRecieved",
                                 g_variant_new ("(ay)",
                                                builder),
                                 &error);
  printf("Checking for errors\n");
  g_assert_no_error (error);


  return TRUE;
}

/* ---------------------------------------------------------------------------------------------------- */

static void
on_bus_acquired (GDBusConnection *connection,
                 const gchar     *name,
                 gpointer         user_data)
{
  guint registration_id;

  registration_id = g_dbus_connection_register_object (connection,
                                                       "/info/skorepa/TestObject",
                                                       introspection_data->interfaces[0],
                                                       &interface_vtable,
                                                       NULL,  /* user_data */
                                                       NULL,  /* user_data_free_func */
                                                       NULL); /* GError** */
  g_assert (registration_id > 0);

  /* swap value of properties Foo and Bar every two seconds */
  g_timeout_add_seconds (2,
                         on_timeout_cb,
                         connection);
}

static void
on_name_acquired (GDBusConnection *connection,
                  const gchar     *name,
                  gpointer         user_data)
{
}

static void
on_name_lost (GDBusConnection *connection,
              const gchar     *name,
              gpointer         user_data)
{
  exit (1);
}

int
main (int argc, char *argv[])
{
  guint owner_id;
  GMainLoop *loop;

  g_type_init ();

  introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
  g_assert (introspection_data != NULL);

  owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
                             "info.skorepa.serial",
                             G_BUS_NAME_OWNER_FLAGS_NONE,
                             on_bus_acquired,
                             on_name_acquired,
                             on_name_lost,
                             NULL,
                             NULL);

  loop = g_main_loop_new (NULL, FALSE);
  g_main_loop_run (loop);

  g_bus_unown_name (owner_id);

  g_dbus_node_info_unref (introspection_data);

  return 0;
}

使用编译:

gcc signal-sample.c `pkg-config --cflags --libs glib-2.0 gio-2.0` -o test

谢谢

1 个答案:

答案 0 :(得分:1)

  

有没有办法检查DBus中的侦听客户端?

不,由于D-Bus的设计方式,这是不可能的。

当客户想要订阅信号时,他们会向D-Bus守护程序发送AddMatch method call,该守护程序在内部注册该状态。当您的服务发出信号时,它会将信号发送到D-Bus守护程序,然后该守护程序将其转发给已订阅该信号的客户端(根据有关广播和权限的各种策略规则)。您的服务无法了解D-Bus守护程序中的内部订阅状态。

处理此类事情的模式是您的服务明确公开客户端必须调用以打开串行端口的subscribeopen方法。您可以使用第二种方法在客户端完成后关闭串口;您还可以侦听客户端断开连接以自动关闭端口。 (在GDBus中,这是使用g_bus_watch_name()完成的,并将客户端的唯一名称传递给它,其名称类似于:1.5。)