I currently use Windows 7 64bit, MSVC2010 and Boost.Asio 1.57. I would like to connect to a TCP server with a timeout. If the timeout expires, I should close the connection as soon as possible as the IP address (chosen by a user) is probably wrong.
I know I should use async requests because sync requests have no timeouts options included. So I'm using async_connect with an external timeout. This is a solution I have found in many places, including stackoverflow.
The problem is that the following code does not behave like I wished. async_connect is not "cancelled" by the socket.close(). With my computer, closing the socket takes about 15 seconds to complete, which makes my program not responsive for a while... I would like to have a decent timeout (approx. 3 seconds) and close the socket after this time, so that the user can try to connect with another IP address (from the HMI)
#include <iostream>
#include <boost\asio.hpp>
#include <boost\shared_ptr.hpp>
#include <boost\bind.hpp>
using boost::asio::ip::tcp;
class tcp_client
{
public:
tcp_client(boost::asio::io_service& io_service, tcp::endpoint& endpoint, long long timeout = 3000000)
:m_io_service (io_service),
m_endpoint(endpoint),
m_timer(io_service),
m_timeout(timeout)
{
connect();
}
void stop()
{
m_socket->close();
}
private:
void connect()
{
m_socket.reset(new tcp::socket(m_io_service));
std::cout << "TCP Connection in progress" << std::endl;
m_socket->async_connect(m_endpoint,
boost::bind(&tcp_client::handle_connect, this,
m_socket,
boost::asio::placeholders::error)
);
m_timer.expires_from_now(boost::posix_time::microseconds(m_timeout));
m_timer.async_wait(boost::bind(&tcp_client::HandleWait, this, boost::asio::placeholders::error));
}
void handle_connect(boost::shared_ptr<tcp::socket> socket, const boost::system::error_code& error)
{
if (!error)
{
std::cout << "TCP Connection : connected !" << std::endl;
m_timer.expires_at(boost::posix_time::pos_infin); // Stop the timer !
// Read normally
}
else
{
std::cout << "TCP Connection failed" << std::endl;
}
}
public:
void HandleWait(const boost::system::error_code& error)
{
if (!error)
{
std::cout << "Connection not established..." << std::endl;
std::cout << "Trying to close socket..." << std::endl;
stop();
return;
}
}
boost::asio::io_service& m_io_service;
boost::shared_ptr<tcp::socket> m_socket;
tcp::endpoint m_endpoint;
boost::asio::deadline_timer m_timer;
long long m_timeout;
};
int main()
{
boost::asio::io_service io_service;
tcp::endpoint endpoint(boost::asio::ip::address_v4::from_string("192.168.10.74"), 7171); // invalid address
tcp_client tcpc(io_service, endpoint);
io_service.run();
system("pause");
}
The only solution I found is to run io_service:run() in many threads, and create a new socket for each connection. But this solution does not appear valid to me as I have to specify a number of threads and I don't know how many wrong address the user will enter in my HMI. Yes, some users are not as clever as others...
What's wrong with my code ? How do I interrupt a TCP connection in a clean and fast way ?
Best regards, Poukill