If you want to do socket communications with timeouts, then select
is the way to go.
You basically set up arrays of file descriptors for various events such as read-ready or write-able, then call select
with a timeout. If one of the events is valid, you will be notified and you can perform your actions.
If none of the events occurs before the timeout, you'll still be notified and can take whatever remedial action you see fit.
See here for the gory details, expanded on below.
Alternatively, you can use setsockopt
with the SO_RCVTIMEO:
struct timeval tv;
tv.tv_sec = 5;
tv.tv_used = 0;
setsockopt (socket_id, SOL_SOCKET, SO_RCVTIMEO,
&tv, sizeof(struct timeval));
For details on select
, you use the FD_ZERO
and FD_SET
macros to construct a set of file descriptors (fdsets) of interest to you. You can have three sets, one indicating whether one or more fds has data to read, one indicating whether one or more is ready for writing to, and one indicating errors. You may not necessarily have all three, it depends on what your code is doing.
Once you've set up the fdsets, you pass them, along with the number of fds and a timeout, to select
which weaves its magic and returns to you. Before you do this, make a copy (FD_COPY
) of the fdsets for later recovery.
On return, there's either been an error, timeout or an event pertaining to one of the fds of interest. In that latter case, the fdsets have been modified to only have the fds set for those with an event and you can use FD_ISSET
to detect which ones.
Then, once you've handled all the events, use FD_COPY
to restore the original fdsets (they were modified by select
, remember) and call select
again. Continue for as long as you need to.
Keep in mind that an error return from select
is not necessarily fatal. You can get (in errno
) EAGAIN
for a temporary resource shortage or EINTR
if a signal was handled. For that second case, you can just re-enter the select call. For the first, I'd implement a retry loop in case it was just a temporary thing.