I am using boost asio to connect to a TCP Server. When I run the code it works fine after start. I send the request and get the response. When I turn off the tcp server (it is a device) I am running into timeout and callback inside async_read from boost will never be executed when I am running into timeout. Then I close the socket. After turn on the device the connection could be re-established but then the recived buffer size is 0 bytes. I think thats because the async_read was not finished correctly after timeout.
header
#include <iostream>
#include <boost/format.hpp>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <chrono>
#include <thread>
#include <future>
#include <sstream>
#include <iomanip>
class TcpClient{
public:
int connect(boost::asio::ip::tcp::socket &socket, boost::asio::ip::tcp::endpoint &endpoint);
int writeAndRead(boost::asio::ip::tcp::socket &socket);
};
This is the code
#include "tcpclient.h"
int TcpClient::connect(boost::asio::ip::tcp::socket &socket, boost::asio::ip::tcp::endpoint &endpoint)
{
boost::system::error_code error;
socket.connect(endpoint, error);
if (!error)
{
std::cout << "connected" << std::endl;
return 1;
}
else
{
std::cout << "not connected" << std::endl;
return 0;
}
}
int TcpClient::writeAndRead(boost::asio::ip::tcp::socket &socket)
{
boost::system::error_code error;
auto status = std::async(std::launch::async, [&]()
{ boost::asio::write(socket, boost::asio::buffer("mytext"), error); })
.wait_for(std::chrono::milliseconds{1000});
switch (status)
{
case std::future_status::deferred:
std::cout << "std::future_status::deferred" << std::endl;
return 0;
case std::future_status::ready:
std::cout << "write success" << std::endl;
break;
case std::future_status::timeout:
std::cout << "std::future_status::timeout" << std::endl;
return 0;
}
boost::asio::streambuf receive_buffer;
boost::optional<boost::system::error_code> read_result;
boost::optional<boost::system::error_code> timer_result;
boost::asio::deadline_timer timer(socket.get_io_service());
timer.expires_from_now(boost::posix_time::seconds(2));
timer.async_wait([&timer_result](const boost::system::error_code &error)
{
if (error != boost::asio::error::operation_aborted)
{
timer_result = error;
} });
boost::asio::async_read(socket,
receive_buffer,
boost::asio::transfer_at_least(1),
[&read_result](const boost::system::error_code &ec, std::size_t bytes_transferred)
{
std::cout << "read_result: " << read_result << std::endl;
read_result = ec;
});
boost::system::error_code ec;
while (1)
{
socket.get_io_service().reset();
int numHandlers = socket.get_io_service().poll_one(ec);
if (read_result)
{
timer.cancel();
break;
}
else if (timer_result)
{
timer.cancel();
std::cout << "timeout" << std::endl;
return 0;
}
}
if (receive_buffer.size() == 0)
{
std::cout << "receive_buffer size 0" << std::endl;
return 0;
}
std::string rawResponse = boost::asio::buffer_cast<const char *>(receive_buffer.data());
std::cout << "rawResponse: " << rawResponse << std::endl;
return 1;
}
int main()
{
std::string ipAddress = "192.168.2.4";
unsigned short port = 50001;
boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address::from_string(ipAddress), port);
boost::asio::io_service ioService;
boost::asio::ip::tcp::socket socket{ioService};
bool isSuccess{false};
bool isConnected{false};
TcpClient tcpclient = TcpClient();
while (1)
{
if (!isConnected)
{
isConnected = tcpclient.connect(socket, endpoint);
}
if (isConnected)
{
isSuccess = tcpclient.writeAndRead(socket);
if (!isSuccess)
{
std::cout << "failed close socket" << std::endl;
socket.close();
isConnected = false;
}
else
{
std::cout << "success" << std::endl;
}
}
std::cout << "wait for 1 sec" << std::endl;
std::chrono::seconds dura(1);
std::this_thread::sleep_for(dura);
}
return 0;
}
this is the output
success
wait for 1 sec
write success
read_result: 0
rawResponse: 0;
success
wait for 1 sec
write success
timeout
failed close socket
wait for 1 sec
not connected
wait for 1 sec
not connected
wait for 1 sec
not connected
wait for 1 sec
not connected
wait for 1 sec
connected
write success
read_result: 0
receive_buffer size 0
failed close socket
wait for 1 sec
connected
write success
read_result: 0
receive_buffer size 0
failed close socket
Asio is an asynchronous IO library.
You're using the blocking API with std::async to ... fake asynchronous IO. I'd suggest not doing that. It looks like your code is based on (very) old example code.
get_io_service()is long gone andio_serviceitself is only available as part of deprecated API.I don't know what compiler you have access to, but this is how I'd write the same code now:
Live On Coliru
With a local demo:
UPDATE: C++11, Boost 1.53, Single Threading
In response to the added context in the comment, here an time-traveling version that ... well goes back in time :)
Live On Compiler Explorer
With the equivalent demo:
BONUS: Faux Coroutines
Just remembered that with the magic of "faux coroutines" you can write the c++11/boost 1.53 version hiding the callbacks and simulating normal control flow:
Live On Compiler Explorer
With yet another equivalent demo: