Boost asio 用于多个异步网络客户端操作

Boost asio for multiple asynchronous network client operations

我想使用 boost::asio(或独立的 asio)通过异步套接字每分钟查询一次多个网络设备的数据。为了进行测试,我已经实现了一个客户端 class 和一个为一台设备执行此操作的控制台程序(无需重复)。 像这样:

class MyClient
{
public:
    MyClient(asio::io_service& io_service);

    void GetData(CompletionHandler completionHandler);
}; 

MyClient::GetData class 在内部使用多个异步操作,其中每个操作的完成都会触发下一个操作,直到数据可用:

使用这个class的控制台程序是这样工作的:

int main(...)
{
    asio::io_service io_service_;

    MyClient c(io_service_, ...);
    ...
    c.GetData([](std::error_code ec, const FloatVector& values){
        //do something with values
    });

    io_service_.run();
    ...
}

现在我想在 GUI 程序中使用 MyClient class 以每分钟连接到 >10 台设备一次,但我坚持整体设计。

首先,我创建了一个线程池,其中每个线程执行单个 io_service 实例的 io_service::run()。

现在,每当我的程序想要从设备读取数据时,它都会在所有设备上循环,并且必须为每个设备创建一个 MyClient 实例并调用 GetData() 方法。

既然 io_service::run() 在池的线程中执行,那么它如何与 io_service 一起工作?我可以简单地在 GUI 线程中调用 MyClient::GetData() 因为它在内部使用异步操作还是我必须调用类似 io_service::post() 的东西?

更新: 我的代码和控制台演示大致遵循这个例子: www.boost.org/doc/libs/1_36_0/doc/html/boost_asio/example/http/client/async_client.cpp

但是在 GUI 程序中我不想在 GUI 线程中 运行 io_service.run() 。现在假设我至少有一个执行 io_service.run() 的额外线程,并且用户按下了一个应该开始设备读取的按钮。最终完成处理程序应将数据存储在数据库中并更新图形显示给用户。

也许按钮处理程序可以简单地实例化 MyClient 并在其上调用 GetData(),一切正常,因为 MyClient 知道 io_service 并使用它 f.e。在 async_connect 等

是这样的还是我弄错了?

注意:此时我的问题不是如何处理完成处理程序中的数据!就是如何在多线程GUI程序中正确获取数据。

检查这个例子 www.boost.org/doc/libs/1_36_0/doc/html/boost_asio/example/http/client/async_client.cpp 。那应该有帮助。

如果您看到 handle_resolve 是否成功,它会调用 async_connect,这将导致 handle_connect 被触发。如果调用 handle_connect 没有错误,它会将一些数据写入连接 (async_write),然后调用 async_read_until(没有错误),这将触发 handle_read_status_line,这可能会触发 handle_read_headers,这可能会触发 handle_read_content.
如果您看到没有明确断开连接,因为析构函数将在内部执行此操作。

以下是您需要做的事情的粗略概述:

  • 首先请注意,如果 io_service.run() 没有工作要做,它会立即 return。因此,根据您的工作流程,您可能需要推迟调用它,直到您真正将第一个异步连接排队。如果您查看示例客户端,您会看到首先实例化客户端,它将第一个操作排队(在本例中为异步解析),然后调用 io_service.run()。
  • 因此,假设您开始按下按钮,此时您需要做的是安排连接或解析,然后启动一个新线程并从该线程调用 io_service.run()。
  • 一旦异步操作链完成并且您拥有数据,您的处理程序将在您启动的新线程的上下文中被调用。这意味着您必须 post 向您的 UI 返回一条消息(因为通常 UI 工作只能在主线程上完成)。例如。您示例中的 lambda 需要执行某种 UI post 消息操作(详细信息取决于我们在这里谈论的 OS/GUI)。
  • 然后您的 GUI 线程将获取该消息并更新您想要更新的任何 UI 状态(例如显示结果)