5

I am using boost::asio::connect on a tcp::socket. When all goes fine, the connect returns immediately but on a poor network, the connect times out after a log wait of 15 seconds. I cannot afford to wait that long and so want to reduce the timeout. Unfortunately I have not come across any solution so far.

I see solutions where async_wait is been used together with deadline_timer but all those examples are for receive / send operations and not for connect.

Can anyone help me with a sample code for boost::asio::connect(socket, endpoints);. Requirement is that it should timeout in 5 seconds instead of 15.

BTR Naidu
  • 1,063
  • 3
  • 18
  • 45
  • 1
    Did you check http://stackoverflow.com/questions/2597608/c-socket-connection-timeout ? – gabhijit Sep 21 '15 at 10:15
  • I presume I cannot mix posix api calls while using boost libs? – BTR Naidu Sep 21 '15 at 10:23
  • Moreover that link talks about setting timeout in a connected socket while my question is how to set timeout for connect call itself. – BTR Naidu Sep 21 '15 at 10:26
  • All you need to do is do something else in 5 seconds. You don't need to timeout the connect in any special way. Just don't wait for the connection. – David Schwartz Sep 21 '15 at 10:42
  • @DavidSchwartz it can be required to cancel the async operation, of course. (You don't want to receive the completion handler on an already defunct object) – sehe Sep 21 '15 at 11:07
  • Yes, you can cancel the async operation. The connect will still proceed normally, it just won't invoke any completion handler. (Unless that happens to release the last reference to the socket through `shared_ptr`s or the like.) – David Schwartz Sep 21 '15 at 11:11
  • @DavidSchwartz I am looking for a solution and not workaround. Workaround for example, do something else and dont wait. Thanks for the tip anyway. – BTR Naidu Sep 21 '15 at 11:21
  • That's not a workaround, that's a solution. If you don't want to wait, just don't. Nothing is forcing you to. Figuring out a way to make a wait work when you don't want to wait is a workaround. If you want to do something in 5 seconds, do something in 5 seconds. Just don't wait. – David Schwartz Sep 21 '15 at 11:24
  • And whats that something else could be? I have only two choice: 1) connect -> read -> return result. 2) connect -> fail -> return error. – BTR Naidu Sep 21 '15 at 12:17
  • I think the answer below is what you want - the link above mentions how that is implemented at a lower level. – gabhijit Sep 21 '15 at 13:11

2 Answers2

7

Have you take a look to the following example? It contains a sample code an async_connect with timeout.

The connect with timeout method could be implemented using the following code:

void connect(const std::string& host, const std::string& service,
  boost::posix_time::time_duration timeout)  {
// Resolve the host name and service to a list of endpoints.
tcp::resolver::query query(host, service);
tcp::resolver::iterator iter = tcp::resolver(io_service_).resolve(query);

// Set a deadline for the asynchronous operation. As a host name may
// resolve to multiple endpoints, this function uses the composed operation
// async_connect. The deadline applies to the entire operation, rather than
// individual connection attempts.
deadline_.expires_from_now(timeout);

// Set up the variable that receives the result of the asynchronous
// operation. The error code is set to would_block to signal that the
// operation is incomplete. Asio guarantees that its asynchronous
// operations will never fail with would_block, so any other value in
// ec indicates completion.
boost::system::error_code ec = boost::asio::error::would_block;

// Start the asynchronous operation itself. The boost::lambda function
// object is used as a callback and will update the ec variable when the
// operation completes. The blocking_udp_client.cpp example shows how you
// can use boost::bind rather than boost::lambda.
boost::asio::async_connect(socket_, iter, var(ec) = _1);

// Block until the asynchronous operation has completed.
do io_service_.run_one(); while (ec == boost::asio::error::would_block);

// Determine whether a connection was successfully established. The
// deadline actor may have had a chance to run and close our socket, even
// though the connect operation notionally succeeded. Therefore we must
// check whether the socket is still open before deciding if we succeeded
// or failed.
if (ec || !socket_.is_open())
  throw boost::system::system_error(
      ec ? ec : boost::asio::error::operation_aborted);
}
ingomueller.net
  • 4,097
  • 2
  • 36
  • 33
rkachach
  • 16,517
  • 6
  • 42
  • 66
  • 1
    This did not work for me when the value of timeout was small ( boost::posix_time::seconds(1)). The code "io_service_.run_one();" runs until one event completes, which seems to happen only after connect() times out naturally (15seconds)). I had more success calling io_service.poll_one() in a loop until 1 second elapsed or ec is updated – aCuria Jul 13 '17 at 08:14
  • @aCuria could you please share the code? Is 'timer' still required? – user1633272 Mar 02 '19 at 13:23
  • @aCuria it works fine once I add the 'check_deadline' related logic according to blocking_tcp_client.cpp. – user1633272 Mar 03 '19 at 02:22
  • What is the link for the newest version? Is this still the recommended way? – ingomueller.net Dec 14 '20 at 13:08
  • You also need to deadline_.async_wait() with a handler to close the socket as in the linked-to example. run_one() worked fine for me (win/linux) with timeout 5s. In boost 1.66+, io_service becomes io_context and deadline_timer becomes steady_timer. – jtbr Jan 20 '22 at 12:32
  • this doesnt even compile – basil Oct 19 '22 at 21:55
0

I know there is already an acceted answer. I found an other solution, which may be usefull for others:

boost::asio::io_service ios;
boost::asio::ip::tcp::endpoint endpoint(remoteAddress);
boost::asio::ip::tcp::socket socket(ios);
boost::system::error_code ec;

auto connectFuture=std::async(std::launch::async,
        [this, &socket, &endpoint]() {
            boost::system::error_code connectEc;
            socket.connect(endpoint, connectEc);
    });

auto timeoutResult=connectFuture.wait_for(std::chrono::milliseconds{timeout});
if( timeoutResult==std::future_status::timeout ) {
 // timeout occured
}
Bil_Bomba
  • 183
  • 1
  • 8