UNIX: Using select(…) with Multiple Sockets in C
If you’re having trouble with listening to multiple sockets (or file descriptors) in C in UNIX, then I’m going to save you a lot of time by posting some sample code. I found my answer in a discussion dating back to 1999.
Here is part of my original code:
for (i = 0; i < MAX_SOCKETS; i++) {
FD_SET(sockets[i], &set);
}
while (1) {
int returned = select(highfd + 1, &set, NULL, NULL, NULL);
if (returned) {
for (i = 0; i < MAX_SOCKETS; i++) {
if (FD_ISSET(sockets[i], &set)) {
int byteCount = recvfrom(sockets[i], (void*)&buffer, sizeof(buffer), 0, NULL, NULL);
printf("%s", buffer);
}
}
}
}
Now here’s the code that works:
while (1) {
for (i = 0; i < MAX_SOCKETS; i++) {
FD_SET(sockets[i], &set);
}
int returned = select(highfd + 1, &set, NULL, NULL, NULL);
if (returned) {
for (i = 0; i < MAX_SOCKETS; i++) {
if (FD_ISSET(sockets[i], &set)) {
int byteCount = recvfrom(sockets[i], (void*)&buffer, sizeof(buffer), 0, NULL, NULL);
printf("%s", buffer);
}
}
}
}
Explanation
MAX_SOCKETS= Total number of file descriptors in the arraysocketssockets= An array containing file descriptorshighfd= The value of the highest file descriptor in the array. I believe that instead ofhighfd + 1you could just pass inFD_SETSIZE
In the first code sample, I called FD_SET outside of the loop. Thus it was only being called once for each file descriptor. The problem with this was that the second time select(...) would get called, it would only listen to first file descriptor that was ready for reading. So even if the other file descriptors were ready for reading after the first iteration of the while loop, select(...) would ignore them. The fix was to call FD_SET on all file descriptors at every iteration of the loop.
When calling FD_SET on a file descriptor, that file descriptor is added to the set. Let’s call this set my_set. You pass in my_set to select(...) which hangs until a file descriptor is ready for reading or until it times out. If one or more file descriptors are ready for reading, then select(...) will modify my_set, remove all file descriptors except the ones that were ready for reading. Pause. Read the previous sentence again a couple of times. Make sure you process it. The call to select(...) will modify my_set if one or more file descriptors are ready for reading and leave out the file descriptors that weren’t ready for reading. So if you pass my_set again to select(...) you won’t be passing in a set with all file descriptors you had originally intended to pass in. This is why you have to call FD_SET again for all file descriptors if you intend on calling select(...) more than once with the same set.
PS: The Wordpress WYSIWYG editor sucks for posting code. Why can’t it post code correctly out of the box? Why do I have to look around for an extension or for a fix? Tchuip… I turned the WYSIWYG editor off by running the following SQL UPDATE wp_usermeta SET meta_value = 'false' WHERE meta_key = 'rich_editing'