Is the connection officially 'dead' when the error_code is not 0?
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?
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:
basic_stream_socket::async_receive() operation cancelled via cancel() will resulting in the handler being invoked with an boost::asio::error::operation_aborted error.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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With