I am using boost::asio
to make a synchronous TCP socket connection to a node.js TCP server application that runs on the same computer. I am using Embarcadero RAD studio XE4 on Windows building 64 bit application that uses the Embarcadero integrated boost version 1.50.
Things work fine except when the node.js TCP server shuts down. When this occurs my C++ client application does not detect the disconnection when I read from the socket. However, it does detect the disconnection when I write to the socket.
My current code was written after trying to make sense of the boost documentation and a variety of answers here on SO. The reading portion of the code is as follows (I have omitted the checking of the error code for compactness)
boost::system::error_code ec;
boost::asio::io_service io_service;
boost::asio::ip::tcp::socket s(io_service);
boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address::from_string("127.0.0.1"),m_port);
ec = s.connect(ep,ec);
std::size_t bytes_available = s.available(ec);
std::vector<unsigned char> data(bytes_available,0);
size_t read_len = boost::asio::read(s, boost::asio::buffer(data),boost::asio::transfer_at_least(bytes_available),ec);
if ((boost::asio::error::eof == ec) || (boost::asio::error::connection_reset == ec))
{
// disconnection
break;
}
This is not a great system as it is running in its own thread inside a loop and polling for data constantly until the program is shutdown. Normally I would not perform a read()
on a socket when there is no data there but I do it in this case since all documentation leads me to believe that the socket disconnection is detected only when performing a read or write to the socket. The problem is that the above code is simply never detecting the disconnection when the node.js application shuts down. If I am writing I detect it (the write part of the code is using the same boost::asio::error` errors as the read for detection) but not when reading.
Obviously I cannot perform a read for byte amounts larger than that are available or my thread will block and I will be unable to perform writes later on in my thread loop.
Am I missing another specific boost error code to detect the error condition? Or is it a problem specifically with the zero length read. If that is the case are there any other options available to me?
Currently I am getting the node.js server to write out a specific message when it to the socket when it shuts down which I am detecting and then closing the client end myself. However, this is a bit of a hack and I would prefer a clean way to detect the disconnect if possible.
In general, Boost.Asio's
read()
functions return when either:It is unspecified as to the order in which these conditions are checked. However, last time I looked at the implementation, Boost.Asio treated operations that read zero bytes on a stream as a no-op prior to attempting reading from the socket. Hence, an end of file error will not be observed, because the 0 size buffer is considered full.
While using asynchronous operations will likely provide better results and scalability, detecting a disconnect in a non-blocking manner can still be accomplished with synchronous operations. By default, the synchronous operations are blocking, but this behavior can be controlled via the
socket::non_blocking()
function. The documentation states:Therefore, if the synchronous operations are set to not block and the read operation attempts to read a minimum of 1 byte, then a disconnect can be observed in a non-blocking manner.
Here is a complete example demonstrating a non-blocking synchronous
read()
operation that detects a disconnect. In order to throttle the print messages, I have opted to perform a sleep when the operation would have blocked (i.e. has a connection but no data is available to read).The following output was produced by connecting the example program to a server that wrote "test", "more testing", then closed the connection: