It is often meaningful to distribute inputs and outputs over several sockets. You use the select() or the poll() function for this type of input/output multiplexing. A program can monitor several connections simultaneously using these functions.
The following program extract illustrates using select().
#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);
The parameters required by select() are three pointers to one bit mask each which represent a set of socket descriptors:
select() uses the bit mask passed with readmask to test which sockets data can be read from.
select() uses the bit mask passed with writemask to test which sockets data can be written to.
select() uses the bit mask passed with exceptmask to test which sockets have an exception pending.
The nfds parameter specifies how many bits or descriptors are to be tested: select() tests bits 0 to nfds-1 in each bit mask.
If you are not interested in one of the pieces of information (read, write or pending exceptions), you should pass the null pointer with the select() call for the parameter concerned.
The bit masks which represent the descriptor sets are stored as bit fields in integer strings. The size of the bit fields is defined via the FD_SETSIZE constant. FD_SETSIZE is defined in <sys/select.h> with a default value that is at least as large as the maximum number of descriptors supported by the system.
You can modify the bit masks with macros. You should, in particular, set the bit masks to 0 before modifying them. The bit mask manipulation macros are described in "select() - multiplex input/output".
You can use the timeout parameter to define a timeout value, if the selection process is to be limited to a predefined time. If you pass the null pointer with timeout, the execution of select() blocks for an unspecified time.
You can set polling by passing timeout a pointer to a timeval variable whose components are all set to 0.
After successful execution, the value returned by select() specifies the number of selected descriptors. The bit masks then indicate
which descriptors are ready for reading,
which descriptors are ready for writing,
which descriptors have exceptions pending.
If select() terminates with a timeout, it returns the value 0. However, the bit masks may already have been changed.
If select() terminates with an error, it returns the value -1 and the appropriate error code in errno. The bit masks are then unchanged.
After executing select(), you can use the FD_ISSET(fd, &yxzmask) macro call to test the status of a descriptor fd. The macro returns a value not equal to 0 if fd is a member of bit mask mask, otherwise the value 0.
You can determine whether connection requests to a socket fd are waiting for acceptance by accept() by testing the read readiness of socket fd. To do this, you call select() and then the FD_ISSET (fd, &readmask) macro. If FD_ISSET returns a value not equal to 0, this indicates read readiness of socket fd: i.e. a connection request is pending on socket fd.
Example: using select() to test for pending connection requests
Any process can use the following program code to read data from two sockets. The timeout value is set to five seconds. The example program is only valid for the communications domain AF_INET. If it is modified according to the information in the sections "Socket addressing" and "Creating a socket", it is also valid for the AF_INET6 domain.
#include <sys/select.h> #include <sys/socket.h> #include <sys/time.h> #include <netinet/in.h> #include <netdb.h> #include <stdio.h> #define TRUE 1 #define TESTPORT 2222 int main(int argc, char **argv) { 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); } /* Assign the socket a name using wildcards */ server.sin_family = AF_INET; server.sin_addr.s_addr = htonl(INADDR_ANY); server.sin_port = htons(TESTPORT); if (bind(sock, (struct sockaddr *)&server, sizeof server) < 0) { perror("binding stream socket"); exit(1); } /* Find and output corresponding port number */ 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 acceptance of connections. */ listen(sock, 5); do { FD_ZERO(&ready); FD_SET(sock, &ready); to.tv_sec = 5; to.tv_usec=0; 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); }