select()

PROTOTYPE

#include <sockets.h>

int select(int numsock, fd_set *reads, fd_set *writes, fd_set *excepts, const struct timeval *timeout);

DESCRIPTION

select() monitors multiple sockets for readiness for I/O. reads, writes, and excepts specify sets of sockets that are checked whether read will not block, write will not block, or either urgent data has been marked or a non-blocking connect() call has failed, respectively. The urgent data mark condition persists until the socket’s read position has advanced past the urgent data mark.

reads also tests if a listening TCP socket has a connection waiting. writes tests if a TCP socket has connected. Unwanted tests are omitted by using NULL in place of the respective socket set pointer. numsock should be one plus the highest numbered included socket.

Socket sets are manipulated using four routines. FD_ZERO() initializes a socket set to be empty. FD_CLR() and FD_SET() remove from and add to, respectively, a socket set. FD_ISSET() returns non-zero if a socket is in a particular set. Prototypes for these routines are:

  void FD_ZERO(fd_set *fdset);
  void FD_CLR(int s, fd_set *fdset);
  void FD_SET(int s, fd_set *fdset);
  int  FD_ISSET(int s, fd_set *fdset);

If no errors occur, select() returns the sum of the number of sockets in each set that satisfy at least one of the tested conditions. The sets are modified to include only those sockets meeting a tested condition. If select() returns a positive value, FD_ISSET() is used to determine which set members are ready for I/O, have urgent data, or a failed connection.

If timeout is NULL, select() blocks until any socket test is true. Otherwise, timeout points to a timeval structure, whose definition is reproduced below. If the timeval structure is zero for both fields, select() is non-blocking. Else the sum of the specified seconds and microseconds is the maximum time - rounded to the kernel tick interval - that select() will block. If none of the tested conditions are met prior to timeout, select() empties the socket sets and returns 0.

  struct timeval
  {
    long tv_sec;    /* number of seconds to wait */
    long tv_usec;   /* number of microseconds to wait */
  };

Typically, if a socket with an established connection selects true for reading, data is available to read. However, it may be the connection has been aborted, the socket has been shutdown for reading, or the connected peer has done a half-close. Similarly, if a socket selects true for writing, there may be available space in the write buffer but the connection may have been aborted or the socket shutdown for writing.

If successful, select() returns the sum of the number of sockets in each set that can be processed without blocking. Otherwise, it sets errno and returns -1.

ERROR CODES

ECOLL Another select() call is monitoring one of the same sockets.
ENETDOWN TargetTCP has not been initialized.
ENOTSOCK One or more socket sets contain an invalid socket handle.

EXAMPLE

  int disc, echo, cgen, sock, max;
  fd_set rd_set;

  /*-------------------------------------------------------------------*/
  /* Create listening parent sockets.                                  */
  /*-------------------------------------------------------------------*/
  disc = set_server(PORT_DISCARD);
  echo = set_server(PORT_ECHO);
  cgen = set_server(PORT_CHARGEN);

  /*-------------------------------------------------------------------*/
  /* Determine the highest socket index.                               */
  /*-------------------------------------------------------------------*/
  max = MAX(echo, disc);
  max = MAX(max, cgen);

  /*-------------------------------------------------------------------*/
  /* Use select() to detect client connection requests.                */
  /*-------------------------------------------------------------------*/
  FD_ZERO(&rd_set);
  for (;;)
  {
    /*-----------------------------------------------------------------*/
    /* Block until connection request arrives.                         */
    /*-----------------------------------------------------------------*/
    FD_SET(disc, &rd_set);
    FD_SET(echo, &rd_set);
    FD_SET(cgen, &rd_set);
    select(max + 1, &rd_set, NULL, NULL, NULL);

    /*-----------------------------------------------------------------*/
    /* Check for TCP discard server request.                           */
    /*-----------------------------------------------------------------*/
    if (FD_ISSET(disc, &rd_set))
    {
      sock = accept(disc, NULL, NULL);
      taskCreate("disc", STACK_4KB, 40, tcp_disc, sock, 0);
    }

    /*-----------------------------------------------------------------*/
    /* Check for TCP echo server request.                              */
    /*-----------------------------------------------------------------*/
    if (FD_ISSET(echo, &rd_set))
    {
      sock = accept(echo, NULL, NULL);
      taskCreate("echo", STACK_4KB, 40, tcp_echo, sock, 0);
    }

    /*-----------------------------------------------------------------*/
    /* Check for TCP cgen server request.                              */
    /*-----------------------------------------------------------------*/
    if (FD_ISSET(cgen, &rd_set))
    {
      sock = accept(cgen, NULL, NULL);
      taskCreate("cgen", STACK_4KB, 40, tcp_cgen, sock, 0);
    }
  }