Boost::asio::async_write,处理程序只调用一次

Boost::asio::async_write, handler called only once

本文关键字:调用 一次 处理 async asio write Boost 程序      更新时间:2023-10-16

我是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.reset()在io_service.run()之后运行良好,每次都调用处理程序。

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

提前感谢

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

当由于io_service停止或耗尽工作而返回对run()run_one()poll()poll_one()函数的前一次调用时,必须在对这些函数的任何第二次或以后的一组调用之前调用reset()

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


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

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并不总是具有未完成的工作,则当run()返回时,可以使用io_service::work进行控制。有关io_service::run()何时阻止和取消阻止的更多详细信息,请参阅此答案
  • 作为缓冲区(p_command)提供给async_write()的底层内存需要保持有效,直到调用了操作的处理程序Client::onSendingFinished()。在这种情况下,可能需要在Client::sendCommand()中制作p_command的副本,将副本写入套接字,然后从处理程序中删除副本。

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

虽然偶尔调用reset()本身并不坏,但有两种典型的方法可以避免这样做。

  1. 在第一个异步操作的处理程序中启动一个新的异步操作。CCD_ 32仅在所有处理程序完成后返回,因此在处理程序中启动的新异步操作仍然及时以保持阻塞CCD_ 33。

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

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

如果你也实现了一个接收器,那么你的接收代码总是需要一个在io_service中运行的接收处理程序,你就不会有这个问题。。。