How to set a timeout on blocking sockets in boost asio?

0 votes
asked Nov 15, 2008 by brian-r-bondy

Is there a way to cancel a pending operation (without disconnect) or set a timeout for the boost library functions?

I.e. I want to set a timeout on blocking socket in boost asio?

socket.read_some(boost::asio::buffer(pData, maxSize), error_);

Example: I want to read some from the socket, but I want to throw an error if 10 seconds have passed.

8 Answers

0 votes
answered Jan 15, 2008 by paul-betts

On *nix, you'd use alarm() so your socket call would fail with EINTR

0 votes
answered Nov 15, 2008 by nicola-bonelli

Under Linux/BSD the timeout on I/O operations on sockets is directly supported by the operating system. The option can be enabled via setsocktopt(). I don't know if boost::asio provides a method for setting it or exposes the socket scriptor to allow you to directly set it -- the latter case is not really portable.

For a sake of completeness here's the description from the man page:

SO_RCVTIMEO and SO_SNDTIMEO

          Specify the receiving or sending  timeouts  until  reporting  an
          error.  The argument is a struct timeval.  If an input or output
          function blocks for this period of time, and data has been  sent
          or  received,  the  return  value  of  that function will be the
          amount of data transferred; if no data has been transferred  and
          the  timeout has been reached then -1 is returned with errno set
          to EAGAIN or EWOULDBLOCK just as if the socket was specified  to
          be  non-blocking.   If  the timeout is set to zero (the default)
          then the operation  will  never  timeout.   Timeouts  only  have
          effect  for system calls that perform socket I/O (e.g., read(2),
          recvmsg(2), send(2), sendmsg(2)); timeouts have  no  effect  for
          select(2), poll(2), epoll_wait(2), etc.
0 votes
answered Nov 26, 2008 by grepsedawk

You could do an async_read and also set a timer for your desired time out. Then if the timer fires, call cancel on your socket object. Otherwise if your read happens, you can cancel your timer. This requires you to use an io_service object of course.

edit: Found a code snippet for you that does this

http://lists.boost.org/Archives/boost/2007/04/120339.php

0 votes
answered Jan 16, 2011 by hplbsh

You can wrap the synchronous calls into futures and wait for it to complete with a timeout (wait_timeout).

http://www.boost.org/doc/libs/1_47_0/doc/html/thread/synchronization.html#thread.synchronization.futures

Certainly not one size fits all, but works well for e.g. circumventing slow connect timeouts.

0 votes
answered Jan 20, 2011 by ty-hoffman

I had the same question, and after some research, the simplest, cleanest solution I could come up with was to get the underlying native socket, and do a select until there was data to read. Select will take a timeout parameter. Of course, working with the native socket starts to go against the point of using asio in the first place, but again, this seems to be the cleanest way. As far as I could tell, asio doesn't provide a way to do this for synchronous usage easily. Code:

        // socket here is:  boost::shared_ptr<boost::asio::ip::tcp::socket> a_socket_ptr

        // Set up a timed select call, so we can handle timeout cases.

        fd_set fileDescriptorSet;
        struct timeval timeStruct;

        // set the timeout to 30 seconds
        timeStruct.tv_sec = 30;
        timeStruct.tv_usec = 0;
        FD_ZERO(&fileDescriptorSet);

        // We'll need to get the underlying native socket for this select call, in order
        // to add a simple timeout on the read:

        int nativeSocket = a_socket_ptr->native();

        FD_SET(nativeSocket,&fileDescriptorSet);

        select(nativeSocket+1,&fileDescriptorSet,NULL,NULL,&timeStruct);

        if(!FD_ISSET(nativeSocket,&fileDescriptorSet)){ // timeout

                std::string sMsg("TIMEOUT on read client data. Client IP: ");

                sMsg.append(a_socket_ptr->remote_endpoint().address().to_string());

                throw MyException(sMsg);
        }

        // now we know there's something to read, so read
        boost::system::error_code error;
        size_t iBytesRead = a_socket_ptr->read_some(boost::asio::buffer(myVector), error);

        ...

Perhaps this will be useful for your situation.

0 votes
answered Jan 8, 2012 by denn

Following on to what grepsedawk has mentioned. There are a few examples showing how to cancel long running asynchronous operations after a period of time, under the Timeouts section within asio doco. Boost Asio Examples . Async TCP client helped me the most.

Happy Asyncing :)

0 votes
answered Nov 28, 2012 by william-symionow

When this question was asked, I guess ASIO did not have any example on how to accomplish what the OP needed, that is to timeout a blocking operation such as a blocking socket operation. Now there exists examples to show you exactly how to do this. the example seems long, but that is because it is WELL commented. It shows how to use the ioservice in a 'one shot' kind of mode.

I think the example is a great solution. The other solutions here break portability and don't take advantage of ioservice. if portability is not important and the ioservice seems like to much overhead --THEN-- you should not be using ASIO. No matter what, you will have an ioservice created (almost all ASIO functionality depends on it, even sync sockets) so, take advantage of it.

Timeout a blocking asio tcp operation

Timeout a blocking asio udp operation

The ASIO documentation has been updated, so check it out for new examples on how to overcome some of the 'gotchas' ASIO use to have.

0 votes
answered Sep 15, 2017 by scinart

Even years after the original question, there is still not a satisfying answer.

Manually using select is not a good option

  1. file descriptor number must be less than 1024
  2. FD may be spuriously reported as ready due to wrong checksum.

Call io_service.run_one() is also a bad idea, because there may be other async options that needs an io_service to always run(). And boost's document about blocking tcp client is hard to comprehend.

So here is my solution. The key idea is the following:

{
    Semaphore r_sem;
    boost::system::error_code r_ec;
    boost::asio::async_read(s,buffer,
                            [this, &r_ec, &r_sem](const boost::system::error_code& ec_, size_t) {
                                r_ec=ec_;
                                r_sem.notify();
                            });
    if(!r_sem.wait_for(std::chrono::seconds(3))) // wait for 3 seconds
    {
        s.cancel();
        throw boost::system::system_error(boost::asio::error::try_again);
    }
    else if(r_ec)
        throw boost::system::system_error(r_ec);
}

Here Semaphore is just a mutex and a condition_variable.
wait_for is implemented by http://en.cppreference.com/w/cpp/thread/condition_variable/wait_for

Full code is at https://github.com/scinart/cpplib/blob/master/include/asio.hpp
Examples is in https://github.com/scinart/cpplib/blob/master/test/test_asio.cpp

Welcome to Q&A, where you can ask questions and receive answers from other members of the community.
Website Online Counter

...