Boost.Asio:异步操作超时

Boost.Asio: Async operations timeout

本文关键字:超时 异步操作 Asio Boost      更新时间:2023-10-16

My Program充当客户端可以连接的服务器。客户端连接后,他将每隔 ~5 秒从服务器获取更新。这是每 5 秒调用一次以将新数据发送到客户端的write函数:

void NIUserSession::write(std::string &message_orig)
{
    std::cout << "Writing message" << std::endl;
    std::shared_ptr<std::string> message = std::make_shared<std::string>( message_orig );
    message->append("<EOF>");
    boost::system::error_code ec;
    boost::asio::async_write(this->socket_, boost::asio::buffer(*message),
        boost::asio::transfer_all(), boost::bind(&NIUserSession::writeHandler,
               this, boost::asio::placeholders::error,
               boost::asio::placeholders::bytes_transferred(),
               message 
               ));
}
void NIUserSession::writeHandler(const boost::system::error_code &error, std::size_t bytes_transferred, std::shared_ptr<std::string> message)
{
    std::cout << "Write Handler" << std::endl;
    if(error)
    {
        std::cout << "Write handler error: " << error.message() << std::endl;
        this->disconnect();
    }
}
void NIUserSession::disconnect()
{
    std::cout << "Disconnecting client, cancling all write and read operations." << std::endl;
    this->socket_.lowest_layer().cancel();
    delete this;
}

如果写入操作中出现错误,服务器和客户端之间的连接将关闭,并且所有异步操作都将被取消(this->socket_.lowest_layer().cancel();)。

问题是,如果连接超时,将不会立即调用writeHandler。相反,写入操作会"堆叠",直到第一个操作达到writeHandler

这应该是程序的正常输出:

Writing message
Write Handler
... Other stuff ...
... Other stuff ...
Writing message
Write Handler

如果连接超时,则会发生以下情况:

Writing message
Write Handler
Write handler error: Connection timed out
Disconnecting client, cancling all write and read operations.
Write Handler
Write Handler
Write Handler
Write Handler
Write Handler
Write Handler
Write Handler
Write Handler
Write Handler
Write Handler
Write Handler
Segmentation fault

最后,分段错误上升。我认为这是因为在其他异步操作仍在进行时调用disconnect。我以为我可以在第一次异步操作失败后直接使用 this->socket_.lowest_layer().cancel(); 来避免它,但它不起作用。

如何避免分段错误?

好吧,在取消操作时不应删除this,因为挂起的 I/O 操作的回调仍将被调用,然后访问this会导致未定义的行为。有多种方法可以解决此问题:

  1. 在你真正知道以前的数据已经写入之前,不要写入数据。您可以将传递给NIUserSession::writestd::string实例排队,以防未完成的写入仍处于挂起状态,然后在未完成的写入操作完成时将它们实际写入处理程序中。这样,您就不会在进行多个 I/O 操作。
  2. std::enable_shared_from_this继承并传递shared_from_this()而不是thisasync_write调用(这是 Boost 异步 TCP 日间服务器示例所做的)。这样,挂起的 I/O 操作将保留对类的引用,并且如果所有操作都完成,则将调用析构函数。