Programming with sockets

Input/output multiplexing

The ability to multiplex I/O requests among multiple sockets or files is a facility that is often used in developing applications. The select call is used for this type of input/output multiplexing:

   #include <sys/time.h>
   #include <sys/types.h>
   #include <sys/select.h>

fd_set readmask, writemask, exceptmask; struct timeval timeout; ... select(nfds, &readmask, &writemask, &exceptmask, &timeout);

select takes pointers to three sets as arguments. One pointer is to the set of file descriptors on which the caller wishes to be able to read data; one is to those descriptors to which data is to be written; and one is to pending exceptional conditions. Out-of-band data is the only exceptional condition currently implemented. If the user is not interested in certain conditions (that is, read, write, or exceptions), the corresponding argument to the select should be a properly cast null pointer.

Each set is a structure containing an array of long integer bit masks. The size of the array is set by FD_SETSIZE. The array is long enough to hold one bit for each of FD_SETSIZE file descriptors.

The macros FD_SET(fd, &mask), and FD_CLR(fd, &mask) have been provided for adding and removing file descriptor fd in the set mask. The set should be zeroed before use, and the macro FD_ZERO(&mask) has been provided to clear the set mask.

The nfds argument specifies the range of file descriptors (that is, one plus the value of the largest descriptor) to be examined in a set.

A timeout value may be specified if the selection is not to last more than a predetermined period of time. If the fields in timeout are set to 0, the selection takes the form of a poll, returning immediately. If the last parameter is a NULL pointer, the selection will block indefinitely.

NOTE: To be more specific, if the last parameter is a NULL pointer, a return takes place only when a descriptor is selectable, or when a signal is received by the caller, interrupting the system call.

select normally returns the number of file descriptors selected. If the select call returns because the timeout has expired, the value 0 is returned. If the select terminates because of an error or interrupt, a -1 is returned with the error number in errno, and with the file descriptor masks unchanged.

Assuming a successful return, the three sets will indicate which file descriptors are ready to be read from, written to, or have exceptional conditions pending.

The status of a file descriptor in a select mask may be tested with the FD_ISSET(fd, &mask) macro, which returns a non-zero value if fd is a member of the set mask, and 0 if it is not.

To determine if there are connections waiting on a socket to be used with an accept call, select can be used, followed by a FD_ISSET(fd, &mask) macro to check for read readiness on the appropriate socket. If FD_ISSET returns a non-zero value, indicating permission to read, then a connection is pending on the socket.

As an example, to read data from two sockets, ``s1'' and ``s2'', as it is available from each and with a five-second timeout, the following code might be used:

   #include <sys/types.h>
   #include <sys/socket.h>
   #include <sys/time.h>
   #include <netinet/in.h>
   #include <netdb.h>
   #include <stdio.h>
   #define TRUE 1

/* * This program uses select to check that someone is trying to connect * before calling accept. */

main() { int sock, length; struct sockaddr_in server; int msgsock; char buf[1024]; int rval; fd_set ready; struct timeval to;

/* Create socket. */ sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { perror("opening stream socket"); exit(1); } /* Name socket using wildcards. */ server.sin_len = sizeof(server); server.sin_family = AF_INET; server.sin_addr.s_addr = INADDR_ANY; server.sin_port = 0; if (bind(sock, (struct sockaddr *)&server, sizeof(server)) < 0) { perror("binding stream socket"); exit(1); } /* Find out assigned port number and print it out. */ length = sizeof(server); if (getsockname(sock, (struct sockaddr *)&server, &length) < 0) { perror("getting socket name"); exit(1); } printf("Socket port #%d\n", ntohs(server.sin_port));

/* Start accepting connections. */ listen(sock, 5); do { FD_ZERO(&ready); FD_SET(sock, &ready); to.tv_sec = 5; if (select(sock + 1, &ready, (fd_set *)0, (fd_set *)0, &to) < 0) { perror("select"); continue; } if (FD_ISSET(sock, &ready)) { msgsock = accept(sock, (struct sockaddr *)0, (int *)0); if (msgsock == -1) perror("accept"); else do { memset(buf, 0, sizeof(buf)); if ((rval = read(msgsock, buf, 1024)) < 0) perror("reading stream message"); else if (rval == 0) printf("Ending connection\n"); else printf("-->%s\n", buf); } while (rval > 0); close(msgsock); } else printf("Do something else\n"); } while (TRUE); exit(0); }

In previous versions of select its arguments were pointers to integers instead of pointers to fd_sets. This type of call will still work as long as the number of file descriptors being examined is less than the number of bits in an integer; however, the methods illustrated above should be used in all current programs.

select provides a synchronous multiplexing scheme. The SIGIO and SIGURG signals described in ``Advanced topics'' may be used to provide asynchronous notification of output completion, input availability, and exceptional conditions.

© 2005 The SCO Group, Inc. All rights reserved.
SCO OpenServer Release 6.0.0 -- 02 June 2005