Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Boost.Asio errors in async_receive and async_send handlers

Tags:

c++

boost-asio

  1. Is the connection officially 'dead' when the error_code is not 0?

  2. Is it possible for the read handler's bytesReceived parameter to be anything other than 0 when the error_code is not 0? If it is possible, should those bytes be processed or no?

like image 334
NFRCR Avatar asked Dec 04 '25 22:12

NFRCR


1 Answers

In short, a non-success error_code does not guarantee the connection is dead, and for non-composed operations, such async_receive(), bytes_transferred will be 0 or more on success, and always 0 if an error occurs.


A non-success error_code does not officially indicate that the connection is official dead. For example, a TCP connection still persists in the following cases:

  • An outstanding basic_stream_socket::async_receive() operation cancelled via cancel() will resulting in the handler being invoked with an boost::asio::error::operation_aborted error.
  • Only part of a full-duplex connection has been shutdown(). For example, if the receive side of a socket is shutdown, then data can still be sent over the connection via the socket.

For non-composed operations, when an error occurs, bytes_transferred will be 0. However, a 0 does not indicate an error occurred. For example, bytes_transferred can be 0 and error_code can indicate success when using reactor-style operations or when an empty buffer is provided. The async_receive() and async_send() documentation for StreamSocketService states:

If the operation completes successfully, the [handler] is invoked with the number of bytes transferred. Otherwise it is invoked with 0.

On the other hand, composed operations, such as boost::asio::async_read() may be invoked with a non-success error_code and a non-zero bytes_transferred. For example, if an async_read() operation is initiated and set to read 1024 bytes before completing, it may invoke async_read_some() multiple times. If 256 bytes are received and then the connection is closed, the async_read() handler will have a non-zero error_code and bytes_transferred will indicate that 256 bytes of the buffer are valid.


Here is a complete example demonstrating a connection persisting even when certain operations fail:

#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>

void noop() {}

void print_status(
  const boost::system::error_code& error,
  std::size_t bytes_transferred)
{
  std::cout << "error = (" << error << ") " << error.message() << "; "
               "bytes_transferred = " << bytes_transferred
            << std::endl;
}

void run_io_service(std::string message, boost::asio::io_service& io_service)
{
  std::cout << message << ": ";
  io_service.run();
  io_service.reset();
}

int main()
{
  using boost::asio::ip::tcp;

  // Create all I/O objects.
  boost::asio::io_service io_service;
  tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), 0));
  tcp::socket socket1(io_service);
  tcp::socket socket2(io_service);

  // Connect the sockets.
  acceptor.async_accept(socket1, boost::bind(&noop));
  socket2.async_connect(acceptor.local_endpoint(), boost::bind(&noop));
  io_service.run();
  io_service.reset();

  const char data[] = "hello\n";
  char buffer[128];

  // Create an async receive operation and cancel it.
  socket1.async_receive(boost::asio::buffer(buffer), &print_status);
  socket1.cancel();
  run_io_service("async_receive1", io_service);

  // Create an async write operation.
  socket1.async_send(boost::asio::buffer(data), &print_status);
  run_io_service("async_send1", io_service);

  // Shutdown the receive side of the socket then create an async
  // receive operation.
  socket1.shutdown(tcp::socket::shutdown_receive);
  socket1.async_receive(boost::asio::buffer(buffer), &print_status);
  run_io_service("async_receive2", io_service);

  // Create an async write operation.
  socket1.async_send(boost::asio::buffer(data), &print_status);
  run_io_service("async_send2", io_service);
}

Output:

async_receive1: error = (system:125) Operation canceled; bytes_transferred = 0
async_send1: error = (system:0) Success; bytes_transferred = 7
async_receive2: error = (asio.misc:2) End of file; bytes_transferred = 0
async_send2: error = (system:0) Success; bytes_transferred = 7
like image 88
Tanner Sansbury Avatar answered Dec 06 '25 12:12

Tanner Sansbury