如何使 D-Bus 服务器端调用异步?
How to make D-Bus server side call as asynchronous?
对于我的项目,我使用 DBUS 作为 IPC 在 QT 应用程序(客户端)和我的服务守护程序(服务器端 - GIO / GDBUS)之间进行交互。在客户端,使用 QDBusPendingCallWatcher 异步调用方法。
但是在服务器端,如何使方法调用异步? .根据我的理解,"g_dbus_method_invocation_return_value" 将 return 响应输出参数使方法调用同步。
我能想到的一种方法是使用 g_dbus_method_invocation_return_value return 中间响应,然后在收到最终响应后发出最终响应作为信号。
示例代码:-
//Method invocation
static void handle_method_call(GDBusConnection *conn,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
GVariant *parameters,
GDBusMethodInvocation *invocation,
gpointer user_data)
{
if (!g_strcmp0(method_name, "Scan")) {
guint8 radiotype = 0;
guint8temp_resp = 0 ;
g_variant_get(parameters, "(y)", radiotype);
// Async Function Call and takes very
// long time to return final response as needs to scan whole radio band
temp_resp = RadioScan(radiotype);
g_dbus_method_invocation_return_value(invocation, g_variant_new("(y", temp_resp)); // return intermediate response to client and when final response is received then emit the signal
g_free(response);
}
}
// Final scan response callback function
static gboolean on_scanfinalresponse_cb (gpointer user_data)
{
GDBusConnection *connection = G_DBUS_CONNECTION (user_data);
GVariantBuilder *builder;
GVariantBuilder *invalidated_builder;
GError *error;
g_dbus_connection_emit_signal (connection,
NULL,
"/org/example/test",
"org.example.test",
"ScanFinalResponse",
g_variant_new ("(s)",
builder),
&error);
g_assert_no_error (error);
return TRUE;
}
请告诉我这是正确的方法还是有更好的方法来实现上述情况的异步调用?
However at server side , how to make method call as async ?
这里 "async" 您可能会提到两个概念,D-Bus(或 GDBus)不会将您限制在其中任何一个。
API设计:如果能修改实际暴露出来的API,当然可以做出一个return马上return然后[=34]的方法=] 通过 属性 变化或信号。对于 Wi-Fi 扫描调用等特定情况,这可能是个好主意。
方法实现:您的 API 可能有一个方法在 returning 之前需要很长时间,并且它可能在 "asynchronously" 的意义上实现您的服务不会在该方法未被 returned 时阻塞——在此期间可以调用其他方法并发出信号和 属性 更改。 g_dbus_method_invocation_return_*
函数可以用来实现这个。创建长 运行 D-Bus 方法不是问题,只要它们被如此记录:客户端可以异步处理调用,如果需要甚至可以增加默认方法调用超时。
在您发布的示例代码的上下文中,您需要做的第一件事是使 RadioScan() 调用异步,或在另一个线程中进行调用:这可确保您的服务在调用期间保持响应。
在您的 RadioScan 是异步的之后,将很容易实现任何一种解决方案。如果 RadioScan() 方法有一个明确定义的 return 值(并且您不想 return 之前的中间结果)我会选择一个需要更长的时间的普通方法调用:
static void handle_method_call(GDBusConnection *conn, ...)
{
if (!g_strcmp0(method_name, "Scan")) {
// start the async scan (maybe using another thread): this should
// return immediately and call the callback when scan is done
start_radio_scan(..., radio_scan_finished_cb);
// do not return the invocation here, just store a pointer to it
}
}
static void radio_scan_finished_cb (...)
{
// return the D-Bbus method call with the invocation that was stored earlier
g_dbus_method_invocation_return_value(invocation, ...)
}
如果您的扫描结果实际上随着时间的推移到达(例如,1 秒后第一个结果,3 秒后更多结果),实际上 return 可能有意义将结果作为信号可用,然后仅return 方法调用作为扫描最终完成的标志。
拥有一个 "ScanFinalResponse" 信号当然是可能的,但我认为这样做与只需要更长时间的方法调用相比没有任何优势。
对于我的项目,我使用 DBUS 作为 IPC 在 QT 应用程序(客户端)和我的服务守护程序(服务器端 - GIO / GDBUS)之间进行交互。在客户端,使用 QDBusPendingCallWatcher 异步调用方法。
但是在服务器端,如何使方法调用异步? .根据我的理解,"g_dbus_method_invocation_return_value" 将 return 响应输出参数使方法调用同步。
我能想到的一种方法是使用 g_dbus_method_invocation_return_value return 中间响应,然后在收到最终响应后发出最终响应作为信号。
示例代码:-
//Method invocation
static void handle_method_call(GDBusConnection *conn,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
GVariant *parameters,
GDBusMethodInvocation *invocation,
gpointer user_data)
{
if (!g_strcmp0(method_name, "Scan")) {
guint8 radiotype = 0;
guint8temp_resp = 0 ;
g_variant_get(parameters, "(y)", radiotype);
// Async Function Call and takes very
// long time to return final response as needs to scan whole radio band
temp_resp = RadioScan(radiotype);
g_dbus_method_invocation_return_value(invocation, g_variant_new("(y", temp_resp)); // return intermediate response to client and when final response is received then emit the signal
g_free(response);
}
}
// Final scan response callback function
static gboolean on_scanfinalresponse_cb (gpointer user_data)
{
GDBusConnection *connection = G_DBUS_CONNECTION (user_data);
GVariantBuilder *builder;
GVariantBuilder *invalidated_builder;
GError *error;
g_dbus_connection_emit_signal (connection,
NULL,
"/org/example/test",
"org.example.test",
"ScanFinalResponse",
g_variant_new ("(s)",
builder),
&error);
g_assert_no_error (error);
return TRUE;
}
请告诉我这是正确的方法还是有更好的方法来实现上述情况的异步调用?
However at server side , how to make method call as async ?
这里 "async" 您可能会提到两个概念,D-Bus(或 GDBus)不会将您限制在其中任何一个。
API设计:如果能修改实际暴露出来的API,当然可以做出一个return马上return然后[=34]的方法=] 通过 属性 变化或信号。对于 Wi-Fi 扫描调用等特定情况,这可能是个好主意。
方法实现:您的 API 可能有一个方法在 returning 之前需要很长时间,并且它可能在 "asynchronously" 的意义上实现您的服务不会在该方法未被 returned 时阻塞——在此期间可以调用其他方法并发出信号和 属性 更改。
g_dbus_method_invocation_return_*
函数可以用来实现这个。创建长 运行 D-Bus 方法不是问题,只要它们被如此记录:客户端可以异步处理调用,如果需要甚至可以增加默认方法调用超时。
在您发布的示例代码的上下文中,您需要做的第一件事是使 RadioScan() 调用异步,或在另一个线程中进行调用:这可确保您的服务在调用期间保持响应。
在您的 RadioScan 是异步的之后,将很容易实现任何一种解决方案。如果 RadioScan() 方法有一个明确定义的 return 值(并且您不想 return 之前的中间结果)我会选择一个需要更长的时间的普通方法调用:
static void handle_method_call(GDBusConnection *conn, ...)
{
if (!g_strcmp0(method_name, "Scan")) {
// start the async scan (maybe using another thread): this should
// return immediately and call the callback when scan is done
start_radio_scan(..., radio_scan_finished_cb);
// do not return the invocation here, just store a pointer to it
}
}
static void radio_scan_finished_cb (...)
{
// return the D-Bbus method call with the invocation that was stored earlier
g_dbus_method_invocation_return_value(invocation, ...)
}
如果您的扫描结果实际上随着时间的推移到达(例如,1 秒后第一个结果,3 秒后更多结果),实际上 return 可能有意义将结果作为信号可用,然后仅return 方法调用作为扫描最终完成的标志。
拥有一个 "ScanFinalResponse" 信号当然是可能的,但我认为这样做与只需要更长时间的方法调用相比没有任何优势。