有没有办法检查是否有人收听 dbus 信号?
Is there a way to check if someone listens to dbus signal?
有没有办法检查 DBus 中的侦听客户端?
有可能吗?我正在使用 gdbus。
背景
我正在创建与串行端口接口的服务,我想在有人监听时隐式打开串行端口,并在最后一个客户端断开连接时自动关闭它。我可以用 open/close 方法来做到这一点,但是存在一个客户端在另一个客户端仍在监听时关闭连接的风险。
我的问题的另一个解决方案是连接计数,但也存在客户端忘记关闭端口或崩溃的风险。
您还有其他实现方法吗?
我的代码(缩短)
基于:
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
谢谢
Is there a way to check for listening clients in DBus?
不,这是不可能的,因为 D-Bus 的设计方式。
当客户端想要订阅信号时,他们会向 D-Bus 守护进程发送一个 AddMatch
method call,后者会在内部注册该状态。当您的服务发出信号时,它会将信号发送到 D-Bus 守护进程,然后将其转发给已订阅该信号的客户端(遵守有关广播和权限的各种策略规则)。您的服务无法了解 D-Bus 守护进程中的内部订阅状态。
处理这种事情的模式是让您的服务显式公开 subscribe
或 open
客户端必须调用的方法才能打开串行端口。您可以使用第二种方法在客户端完成后关闭串行端口;您还可以侦听客户端断开连接以自动关闭端口。 (在 GDBus 中,这是通过使用 g_bus_watch_name()
并将客户端的 唯一 名称传递给它来完成的,它看起来像 :1.5
。)
有没有办法检查 DBus 中的侦听客户端?
有可能吗?我正在使用 gdbus。
背景
我正在创建与串行端口接口的服务,我想在有人监听时隐式打开串行端口,并在最后一个客户端断开连接时自动关闭它。我可以用 open/close 方法来做到这一点,但是存在一个客户端在另一个客户端仍在监听时关闭连接的风险。
我的问题的另一个解决方案是连接计数,但也存在客户端忘记关闭端口或崩溃的风险。
您还有其他实现方法吗?
我的代码(缩短)
基于: 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
谢谢
Is there a way to check for listening clients in DBus?
不,这是不可能的,因为 D-Bus 的设计方式。
当客户端想要订阅信号时,他们会向 D-Bus 守护进程发送一个 AddMatch
method call,后者会在内部注册该状态。当您的服务发出信号时,它会将信号发送到 D-Bus 守护进程,然后将其转发给已订阅该信号的客户端(遵守有关广播和权限的各种策略规则)。您的服务无法了解 D-Bus 守护进程中的内部订阅状态。
处理这种事情的模式是让您的服务显式公开 subscribe
或 open
客户端必须调用的方法才能打开串行端口。您可以使用第二种方法在客户端完成后关闭串行端口;您还可以侦听客户端断开连接以自动关闭端口。 (在 GDBus 中,这是通过使用 g_bus_watch_name()
并将客户端的 唯一 名称传递给它来完成的,它看起来像 :1.5
。)