Stopping async_connect

I am currently using Windows 7 64bit, MSVC2010 and Boost.Asio 1.57. I would like to connect to a TCP server with a timeout. If it times out, I should close the connection as soon as possible as the IP address (selected by the user) is probably wrong.

I know I have to use async requests because there are no timeout parameters in sync requests. So I am using async_connect with an external timeout. This is the solution I've found in many places including stackoverflow.

The problem is, the following code doesn't behave the way I wanted. async_connect is not canceled by socket.close (). With my computer, it takes about 15 seconds to close the socket, which makes my program unresponsive for a while ... I would like to have a decent timeout (about 3 seconds) and close the socket after that time so that the user can try to connect to a different IP -address (from 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 have found is to run io_service: run () on many threads and create a new socket for each connection. But this solution seems invalid to me since I have to specify the number of threads and I don't know how many wrong addresses the user will enter into my HMI. Yes, some users are not as smart as others ...

What's wrong with my code? How to terminate TCP connection in a clean and fast way?

Regards, Poukill

+3


source to share


1 answer


There is nothing rudimentary about the code, and it does exactly what you want in my Linux box:

TCP Connection in progress
Connection not established...
Trying to close socket...
TCP Connection failed

real    0m3.003s
user    0m0.002s
sys 0m0.000s

      



Notes:

  • You might be able to add a call cancel()

    to the function stop()

    :

    void stop()
    {
        m_socket->cancel();
        m_socket->close();
    }
    
          

  • You should check for abortion timeout though:

    void HandleWait(const boost::system::error_code& error)
    {
        if (error && error != boost::asio::error::operation_aborted)
        {
            std::cout << "Connection not established..." << std::endl;
            std::cout << "Trying to close socket..."     << std::endl;
            stop();
            return;
        }
    }
    
          

    Otherwise, implicitly disabling the timer after a successful connection will still close () the socket :)

  • If you want to run (many) connection attempts in parallel, you no longer need threads or even more than one io_service

    . This is the essence of Boost Asio: you can perform asynchronous I / O operations in a single thread.

+2


source







All Articles