如何使 D-Bus 服务器端调用异步?

Posted

技术标签:

【中文标题】如何使 D-Bus 服务器端调用异步?【英文标题】:How to make D-Bus server side call as asynchronous? 【发布时间】:2018-01-13 17:31:04 【问题描述】:

对于我的项目,我使用 DBUS 作为 IPC 在 QT 应用程序(客户端)和我的服务守护程序(服务器端 - GIO / GDBUS)之间进行交互。在客户端,使用 QDBusPendingCallWatcher 异步调用方法。

但是在服务器端,如何使方法调用异步? .据我了解,“g_dbus_method_invocation_return_value”将返回响应,输出参数使方法调用同步。

我能想到的一种方法是使用 g_dbus_method_invocation_return_value 返回中间响应,然后在收到最终响应后将最终响应作为信号发出。

示例代码:-

//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;

请让我知道这是正确的方法还是有更好的方法来实现上述情况的异步调用?

【问题讨论】:

【参考方案1】:

但是在服务器端,如何使方法调用异步?

您可能在这里用“异步”指代两个概念,而 D-Bus(或 GDBus)不会限制您使用任何一个。

    API 设计:如果您可以修改实际公开的 API,您当然可以创建一个立即返回的方法,然后通过属性更改或信号“返回值”。对于 Wi-Fi 扫描调用等特定情况,这可能是个好主意。

    方法实现:您的 API 可能有一个需要很长时间才能返回的方法,并且它可能是“异步”实现的,因为您的服务在方法没有返回时不会阻塞 - 其他方法可以调用,并且在此期间可能会发生信号和属性更改。 g_dbus_method_invocation_return_* 函数可用于实现这一点。创建长时间运行的 D-Bus 方法不是问题,只要它们被记录为:客户端可以异步处理调用,甚至可以根据需要增加默认方法调用超时。

在您发布的示例代码的上下文中,您需要做的第一件事是使 RadioScan() 调用异步,或者在另一个线程中进行调用:这样可以确保您的服务在调用期间保持响应。

在您的 RadioScan 是异步的之后,将很容易实现任何一种解决方案。如果 RadioScan() 方法具有明确定义的返回值(并且您不想更早返回中间结果),我会选择只需要更长时间的普通方法调用:

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 秒后的更多结果),当结果可用时实际将结果作为信号返回,然后将方法调用作为标志着扫描终于完成了。

拥有一个“ScanFinalResponse”信号当然是可能的,但我认为这样做比只需要更长时间的方法调用有任何优势。

【讨论】:

感谢您的回复。正如您所提到的,由于无线电“扫描”功能需要很长时间才能返回最终响应,无线电服务不应阻止并接受其他方法,例如在收到最终响应之前取消扫描。我已经发布了示例代码。 也许这些补充有帮助?本质上要解决的问题是 RadioScan() 阻塞:你需要找到一种方法来解决这个问题——要么使用异步的扫描 API,要么在另一个线程中进行扫描 感谢您提供解决方案和示例代码。您建议的解决方案应该有效。我基本上需要做三件事。一种。不从方法调用 b 返回调用指针。注册回调指针以处理无线电扫描完成并在回调被击中时返回存储的调用指针。 3. 使无线电扫描API异步。

以上是关于如何使 D-Bus 服务器端调用异步?的主要内容,如果未能解决你的问题,请参考以下文章

服务消费端泛化调用与异步调用

如何进行服务器端 fetch 调用?

如何使数组过滤器与异步函数端一起使用? [复制]

使用 ActiveMQ 实现JMS 异步调用

关于使用 D-Bus 异步方法调用的 Python 中的线程安全

如何实现服务端客户端异步通信