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.