简体   繁体   中英

Boost.Asio: Async operations timeout

My Program acts as a server to which a client can connect. Once a client connected, he will get updates from the server every ~5 seconds. This is the write -function that is called every 5 seconds to send the new data to the client:

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

If there is an error in the write operations the connection between the server and the client gets closed and all async operations are cancled ( this->socket_.lowest_layer().cancel(); ).

The problem is that if the connection times out, writeHandler will not be called immediately. Instead, the write operations "stack up" until the first one reaches writeHandler .

This should be the normal output of the program:

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

If the connections times out, this is what happens:

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

At the end, a segmentation fault rises. I think this is because disconnect is called while other async operations are still on their way. I thought I could avoid it by using this->socket_.lowest_layer().cancel(); directly after the first async operation fails, but it doesn't work.

How can I avoid a segmentation fault?

Well, you should not delete this when cancelling the operations since the callbacks for the pending I/O operations will still be invoked and then accessing this leads to undefined behavior. There are multiple ways to tackle this:

  1. Don't write data until you actually know that previous data has been written. You could queue the std::string instances passed to NIUserSession::write in case an outstanding write is still pending and then actually write them in the handler when the outstanding write operation completes. That way you will not have multiple I/O operations in flight.
  2. Inherit from std::enable_shared_from_this and pass shared_from_this() instead of this to the async_write call (this is what the Boost asynchronous TCP daytime server example does). That way pending I/O operations will keep a reference to your class and the destructor will be called if all of them complete.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM