Synchronous multiplexing with select()

The select() function has several useful features. Given a set of sockets, it can be used to block until any of the sockets in that set is ready to be read from. It can also be configured to return if a socket is ready to be written to or if a socket has an error. Additionally, we can configure select() to return after a specified time if none of these events take place.

The C function prototype for select() is as follows:

int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);

Before calling select(), we must first add our sockets into an fd_set. If we have three sockets, socket_listen, socket_a, and socket_b, we add them to an fd_set, like this:

fd_set our_sockets;
FD_ZERO(&our_sockets);
FD_SET(socket_listen, &our_sockets);
FD_SET(socket_a, &our_sockets);
FD_SET(socket_b, &our_sockets);

It is important to zero-out the fd_set using FD_ZERO() before use.

Socket descriptors are then added to the fd_set one at a time using FD_SET(). A socket can be removed from an fd_set using FD_CLR(), and we can check for the presence of a socket in the set using FD_ISSET().

You may see some programs manipulating an fd_set directly. I recommend that you use only FD_ZERO(), FD_SET(), FD_CLR(), and FD_ISSET() to maintain portability between Berkeley sockets and Winsock.

select() also requires that we pass a number that's larger than the largest socket descriptor we are going to monitor. (This parameter is ignored on Windows, but we will always do it anyway for portability.) We store the largest socket descriptor in a variable, like this:

SOCKET max_socket;
max_socket = socket_listen;
if (socket_a > max_socket) max_socket = socket_a;
if (socket_b > max_socket) max_socket = socket_b;

When we call select(), it modifies our fd_set of sockets to indicate which sockets are ready. For that reason, we want to copy our socket set before calling it. We can copy an fd_set with a simple assignment like this, and then call select() like this:

fd_set copy;
copy = our_sockets;

select(max_socket+1, &copy, 0, 0, 0);

This call blocks until at least one of the sockets is ready to be read from. When select() returns, copy is modified so that it only contains the sockets that are ready to be read from. We can check which sockets are still in copy using FD_ISSET(), like this:

if (FD_ISSET(socket_listen, &copy)) {
//socket_listen has a new connection
accept(socket_listen...
}

if (FD_ISSET(socket_a, &copy)) {
//socket_a is ready to be read from
recv(socket_a...
}

if (FD_ISSET(socket_b, &copy)) {
//socket_b is ready to be read from
recv(socket_b...
}

In the previous example, we passed our fd_set as the second argument to select(). If we wanted to monitor an fd_set for writability instead of readability, we would pass our fd_set as the third argument to select(). Likewise, we can monitor a set of sockets for exceptions by passing it as the fourth argument to select().

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.224.62.25