【问题标题】:Boost::asio::async_write, handler called only onceBoost::asio::async_write,处理程序只调用一次
【发布时间】:2015-11-17 18:38:27
【问题描述】:

我是 boost::asio 的新手,但我遇到了问题。我正在编写将一些命令循环发送到服务器的客户端。我正在使用 boost::asio::async_write 发送命令,我希望每次发送命令时都会调用处理程序。事实上,只有在第一次发送期间,我才看到调用了该处理程序。我的客户是这样的:

Client::Client(boost::asio::io_service & p_ioService,
           boost::asio::ip::tcp::endpoint p_endpoint)
 : io_service(p_ioService), endpoint(p_endpoint), socket(p_ioService)
{
   socket.connect(endpoint); 
}
Client::~Client()
{
    socket.close();
}
void Client::sendCommand(const string & p_command)
{
    boost::asio::async_write(socket,boost::asio::buffer(p_command), 
                              boost::bind(&Client::onSendingFinished,this, _1, _2));
    io_service.run();
}
void Client::onSendingFinished(const boost::system::error_code& ec, std::size_t    bytes_transferred)
{
    cout<<"Sent "<<bytes_transferred<<endl;
}

在 main.cpp 中没有其他地方调用 io_service.run。我注意到如果我打电话 io_service.run() 之后的 io_service.reset() 工作正常,每次都会调用处理程序。

如果没有 io_service.reset() 我应该如何解决这个问题

提前致谢

【问题讨论】:

    标签: c++ boost-asio


    【解决方案1】:

    我不理解对调用io_service::reset() 的厌恶。在这种情况下,有必要在对io_service::run() 的任何后续调用之前调用:

    reset() 必须在 run()run_one()poll()poll_one() 函数的任何第二组或更晚的调用集之前调用,当这些函数的先前调用由于 io_service 而返回时被停止或工作结束。

    由于抛出异常,线程可能从run() 返回,但io_service 既没有停止也没有耗尽工作。在这种情况下,线程可以调用run()而不调用reset()


    当前Client::sendCommand() 是同步的。这是一个实现细节,它启动一个异步操作,然后阻塞io_service::run() 等待操作完成。除非有多个线程在socket 上调用命令,多个线程运行io_service,或者写操作需要可取消,例如超时,那么它在功能上是等效的并且可能更容易实现Client::sendCommand()一个同步的write()

    void Client::sendCommand(const string & p_command)
    {
      boost::system::error_code ec;
      std::size_t bytes_transferred =
          boost::asio::write(socket, boost::asio::buffer(p_command), ec);
      onSendingFinished(ec, bytes_transferred);
    }
    

    如果Client::sendCommand()需要异步,那么:

    • io_service 应该从Client::sendCommand() 之外运行。如果io_service 并不总是有出色的工作,那么io_service::work 可以在run() 返回时使用控制。有关io_service::run() 何时阻止和解除阻止的更多详细信息,请参阅this 答案。
    • 作为缓冲区 (p_command) 提供给 async_write() 的底层内存需要保持有效,直到调用操作的处理程序 Client::onSendingFinished()。在这种情况下,可能需要在Client::sendCommand() 中复制p_command,将副本写入套接字,然后从处理程序中删除副本。

      [...] 底层内存块的所有权由调用者保留,它必须保证它们在调用处理程序之前保持有效。

    【讨论】:

      【解决方案2】:

      虽然时不时地拨打reset() 并不是一件坏事,但有两种典型的方法可以避免这样做。

      1. 在第一个处理程序中启动一个新的异步操作。 run() 仅在所有处理程序完成后才返回,因此在处理程序中启动的新异步操作仍然及时阻止io_service

      2. 使用io_service::work。如果您创建一个使用io_service 作为参数构造的io_service::work 实例,那么只要work 对象仍然存在,您对run() 的后续调用就不会返回。因此,您无需重置任何内容。当然,这意味着如果您希望 run() 停止阻塞,您的一个处理程序或另一个线程必须在某个时间销毁 work 对象。

      【讨论】:

        【解决方案3】:

        只是发送消息是很不寻常的,双向通信更为常见。

        如果您也实现了接收器,那么您的接收代码将始终需要在 io_service 中运行的接收处理程序,您就不会遇到这个问题...

        【讨论】:

          猜你喜欢
          • 2011-12-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-12-22
          • 1970-01-01
          • 1970-01-01
          • 2012-11-14
          相关资源
          最近更新 更多