1

I am trying to implement a ConnectWithTimeout function for a boost socket. So I'm using one of the examples I found here. This works perfectly on this first try, but the io_service.run_one() returns immediately with a timeout or a cancel error.

Here is my code

using NetStatus = boost::system::error_code;

NetStatus handleWait(const NetStatus& error)
{
    return boost::asio::error::timed_out;
}

NetStatus handleConnect(const NetStatus& error)
{
    // The async_connect() function automatically opens the socket at the start
    // of the asynchronous operation. If the socket is closed at this time then
    // the timeout handler must have run first.
    if (!m_socket.is_open())
        return boost::asio::error::timed_out;

    // Otherwise, a connection has been established.  Update the timer state
    // so that the timeout handler does not close the socket.
    m_connectionTimeoutTimer.cancel();
    return error;
}

void connectWithTimeout(boost::asio::ip::tcp::endpoint& endpoint, NetStatus& e)
{
    // Stop last time's waiting objects
    m_socket.cancel()
    m_connectionTimeoutTimer.cancel();
    m_ioService.stop();
    m_ioService.reset();

    // Set-up new objects to wait
    m_connectionTimeoutTimer.expires_from_now(boost::posix_time::seconds(5));
    m_connectionTimeoutTimer.async_wait([this, &e](const NetStatus& error) { e = handleWait(error); } );
    m_socket.async_connect(endpoint,    [this, &e](const NetStatus& error) { e = handleConnect(error); } );

    // Block until one of them is done
    m_ioService.run_one(e);
}

boost::asio::ip::tcp::socket m_socket;
boost::asio::deadline_timer m_connectionTimeoutTimer;

The results I'm seeing when running this in a loop are: Timeout (after 5 seconds as expected) Cancel (Immediately) Timeout (Immediately) Cancel (Immediately) Timeout (Immediately) Cancel (Immediately) Timeout (Immediately) ...

Can anyone please help spot what I'm doing wrong?

  • I don't undestand why you call `m_ioService.stop();` and `m_ioService.reset();` inside `connectWithTimeout()` function – Elvis Dukaj Aug 10 '17 at 15:15
  • I tried to get the io_service to stop poping events from the previous run, so I made sure that no previous handlers are waiting by running reset and stop. – Daniel Duvilanski Aug 10 '17 at 15:18

2 Answers2

0

Are you closing the socket between each loop? You call async_connect on the same m_socket instance but I'm not seeing where the socket is closed. You probably have an error where you're trying to connect a socket that is already opened. Try m_socket.close() instead of m_socket.cancel().

0

Ok so I found the answer, it appears that the only way to get a handler out of the io_service is to to call it with run or run_one, reset etc doesn't do it. is the final code I got if anyone is interested:

#include <iostream>
#include <string>
#include <boost/asio.hpp>
#include <boost/filesystem.hpp>
#include <boost/iostreams/filtering_streambuf.hpp>
#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/filter/gzip.hpp>
#include <boost/timer/timer.hpp>

std::string bindToIp;
std::string ip;
int         port;
auto tcpConnectionTrials = 5ul;

using NetStatus = boost::system::error_code;

boost::asio::io_service m_ioService;
boost::asio::ip::tcp::socket   m_socket(m_ioService);
boost::asio::deadline_timer    connectionTimeoutTimer(m_ioService);
bool isTimedOut = false;

void handleWait(boost::asio::ip::tcp::socket& socket, const NetStatus& error)
{
        if (error != boost::asio::error::operation_aborted)
        {
                isTimedOut = true;
                socket.cancel();
                socket.close();
        }
}

void handleConnect(boost::asio::ip::tcp::socket& socket, const NetStatus& error)
{
        if (error != boost::asio::error::operation_aborted)
        {
                // The async_connect() function automatically opens the socket at the start
                // of the asynchronous operation. If the socket is closed at this time then
                // the timeout handler must have run first.
                if (socket.is_open())
                {
                        connectionTimeoutTimer.cancel();
                }
        }
}


void connectWithTimeout(boost::asio::ip::tcp::socket& socket,
                                                boost::asio::deadline_timer& connectionTimeoutTimer,
                                                boost::asio::io_service& m_ioService,
                                                boost::asio::ip::tcp::endpoint& endpoint,
                                                NetStatus& e)
{
        isTimedOut = false;

        // Set-up new objects to wait
        connectionTimeoutTimer.expires_from_now(boost::posix_time::seconds(5));
        connectionTimeoutTimer.async_wait([&socket](const NetStatus& error)     { handleWait(socket, error); } );
        socket.async_connect(endpoint,    [&socket, &e](const NetStatus& error) { e = error; handleConnect(socket, error); } );

        // Block until one of them is done
        m_ioService.run();
        if (isTimedOut)
        {
                e = boost::asio::error::timed_out;
        }
        m_ioService.reset();
}

NetStatus connect()
{
        NetStatus e;

        boost::asio::ip::tcp::endpoint remoteEndpoint(boost::asio::ip::address_v4::from_string(ip.c_str()), port);
        boost::asio::ip::tcp::endpoint localEndpoint(boost::asio::ip::address_v4::from_string(bindToIp.c_str()), 0);

        std::cout <<  "Open socket: " << std::endl;
        if (m_socket.open(boost::asio::ip::tcp::v4(), e))
        {
                std::cout << "Socket " << ": could not open!!!" << std::endl;
                return e;
        }
        m_socket.set_option(boost::asio::socket_base::reuse_address(true));
        m_socket.set_option(boost::asio::ip::tcp::no_delay(true));

        std::cout << " binds to " << bindToIp << std::endl;
        m_socket.bind(localEndpoint);

        std::cout << " connect to " << ip  << std::endl;
        connectWithTimeout(m_socket, connectionTimeoutTimer, m_ioService, remoteEndpoint, e);
        return e;
}

int main(int argc, char *argv[])
{
        bindToIp = argv[1];
        ip = argv[2];
        port = atoi(argv[3]);

        for(int i =0; i < 10; i++)
        {
                auto e = connect();
                if (!e)
                {
                        std::cout << "GOOD!" << std::endl;
                        break;
                }
                else
                {
                        std::cout << "Failed: " << e.message() << std::endl;
                }
                m_socket.close();
        }
        std::cout << "DONE!" << std::endl;
        return 0;
}
  • I found out that the mechanism should and does work! It wasn't boost but an driver issue because I was using a special driver that boosts socket performance. – Daniel Duvilanski Sep 16 '17 at 10:55