views:

1153

answers:

3
while (xxx) {
    timeout.tv_sec=TIMEOUT;
    timeout.tv_usec=0;
    FD_ZERO(&set); 
    FD_SET(sd,&set);

    switch (select(FD_SETSIZE,&set,NULL,NULL,&timeout))
    xxxxx
}

works fine, however

FD_ZERO(&set); 
FD_SET(sd,&set);

while (xxx) {
    timeout.tv_sec=TIMEOUT;
    timeout.tv_usec=0;

    switch (select(FD_SETSIZE,&set,NULL,NULL,&timeout))
    xxxxx
}

doesn't. It works the first time around, but the next time it runs through the while loop it gets a timeout even if the sd socket receives data. It seems to me to be a waste of resources to have to empty and fill set every time.

Anybody have a good explanation why this is, and even better, perhaps a suggestion how to avoid it?

+7  A: 

Read the select man page. The returned set is only the file descriptors that are ready to be used. You are supposed to use FD_ISSET to check each one if it is set or not.

Always initialize the fd_set right before using it.

Greg Rogers
I know about the FD_ISSET, but I haven't included it yet, because as of now I'm only listining to one socket (I'll be adding more sockets later on).So there is now way to be able to "reuse" without having to set fd_set first?
deadcyclo
The documented behavior of select() includes modifying the sets in place. To quote The Pragmatic Programmer, "'select' isn't broken".
bk1e
A: 

That's the way select works. It works best, and makes more sense, if you have more than one socket. That's kind of the point: you are selecting across many sockets. If you want to read from one socket, just read or recv it.

J. John
Read doesn't offer timeout. Hence the use of select. + the fact that I will be adding a second socket later in the development stage.
deadcyclo
Just to keep offering alternatives: if sd is a socket you can use setsockopt(sd, SO_RCVTIMEO, ...) to add a read timeout. However if you are going to add a second socket later, select is the better option to pursue.
DGentry
+6  A: 

select modifies its arguments. You really do have to re-initialize it each time.

If you're concerned about overhead, the cost of processing the complete FD_SET in the kernel is somewhat more significant than the cost of FD_ZERO. You'd want to only pass in your maximum fd, not FD_SETSZIZE, to minimize the kernel processing. In your example:

switch (select((sd + 1),&set,NULL,NULL,&timeout))

For a more complex case with multiple fds, you typically end up maintaining a max variable:

FD_SET(sd,&set);
if (sd > max) max = sd;
... repeat many times...

switch (select((max + 1),&set,NULL,NULL,&timeout))


If you will have a large number of file descriptors and are concerned about the overhead of schlepping them about, you should look at some of the alternatives to select(). You don't mention the OS you're using, but for Unix-like OSes there are a few:

  • for Linux, epoll()
  • for FreeBSD/NetBSD/OpenBSD/MacOS X, kqueue()
  • for Solaris, /dev/poll

The APIs are different, but they are all essentially a stateful kernel interface to maintain a set of active file descriptions. Once an fd is added to the set, you will be notified of events on that fd without having to continually pass it in again.

DGentry
Thank you for a great answer :)
deadcyclo
For an alternative to select(), why not poll() which, unlike the ones you mention, is the same on many Unices?
bortzmeyer
poll() is better in that you don't have to reinitialize its array each time, but you're still copying a large structure in and out of the kernel on every call. If you have a lot of fds, the other alternatives avoid this constant overhead by modifying the fd set in the kernel.
DGentry