You are here

TutorialLibraryTest ApplicationDownload
[Tutorial]LibraryTest ApplicationDownload

Error message

Deprecated function: The each() function is deprecated. This message will be suppressed on further calls in menu_set_active_trail() (line 2404 of /home/quantics/public_html/dp/includes/menu.inc).

Socket Tutorial

Chapter 4 - Programming timeout

A timeout is a mechanism that specifies a deadline for an event to occur. This mechanism must be integrated into every function of our sockets library so that they all have an argument specifying the maximum amount of time they are allowed to block. If a function fails doing its work within a given time line, it must give back hand and return an error.

However, we have seen that a socket can be configured in 2 default modes: blocking or non-blocking. These modes correspond to special cases with a timeout of 0 in one hand, and an infinity in the other. To implement timeout inside functions of our library, we will have to use a third mechanism that allows to wait for an event to occur, which is provided by the function select. This later will be the cornerstone of our implementation of timeout, so we'll take the time to explain it somewhat.

Function select

Prototype of this function is identical whether we are using Linux or Windows:

int select(
   int nfds,
   fd_set* readfds,
   fd_set* writefds,
   fd_set* exceptfds,
   const struct timeval* timeout
);
  • nfds : [in] value of the largest socket identifier contained in the following three tables, plus 1 (not used on Windows)
  • readfds : [in, out] optional pointer to a set of socket identifiers for which you want to be informed of the possibility of reading;
  • writefds : [in, out] optional pointer to a set of socket identifiers for which you want to be informed of the possibility of writing;
  • exceptfds : [in, out] optional pointer to a set of socket identifiers for which you want to be informed of error exceptions;
  • timeout : [out] maximum deadline for function select to return when waiting an event on at least one socket of the 3 previous tables. A null pointer means the function can wait indefinitely (blocking mode);
  • Return value : Number of sockets on which an event has been detected.

Before calling function select, it is necessary to fill at least one of the three structures readfds, writefds and exceptfds with one or more socket identifiers. Null can be passed if a structure is not used. Timevalt structure must also be properly completed to ensure that the function does not block longer than a specified timeout parameter.

Readfds structure must be filled with a list of sockets for which we want to test the possibility of reading. If the socket is in listening mode (listen, see subsequent chapters), it will be marked as readable if a client makes a socket connection request. In this case, function accept (see following chapters) is guaranteed not to block (even if the socket is in blocking mode). Other sockets will be marked as readable if they have received data, in which case function recv (see following chapters) is guaranteed not to block (even if the socket is in blocking mode).

Writefds structure must be filled with sockets for which we want to test the possibility of writing. A socket is marked as writable if it is still possible to write data into its internal send queue, in which case function send (see following chapters) is guaranteed not to block (even if the socket is in blocking mode ).

We will not use the structure exceptfds in this tutorial.

Thus, function select waits until one of the socket fd_set structures toggle in one of the states described above. It returns immediately if it is already the case, or it can wait for these event until the timeout variable expire. Once returned, the contents of the fd_set structures is modified to contain only identifiers of sockets on which an event took place.

unction select is the only way to implement the concept of timeout. So, we will use it to implement ourself this timeout mechanism for all operations that can be performed on sockets: connect, accept connections, write data and read data.

Macro for fd_set structures manipulation

For portability reasons, the fd_set structures should be handled only by means of the 4 following macro:

  • FD_CLR (s, *set) : Remove the socket identifier s from the set;
  • FD_ISSET (s, *set) : Return a non zero value if identifier s belongs to the set;
  • FD_SET (s, *set) : Add the socket identifier s to the set;
  • FD_ZERO (*set) : Flush all socket identifiers from the set.