Unexpected results with select and recvfrom

2019-08-12 09:25发布

问题:

fd_set rset;
struct timeval tv;
FD_ZERO(&rset);
FD_SET(sockfd, &rset);
tv.tv_sec = 1;
tv.tv_usec = 0;

for(;;)
{
  for(count = 0; count < elements in sockaddr_in array; count++)
  {
    //flag_array is filled with -1 before for(;;)
    if(flag_array[count] == -1 && select(sockfd+1, &rset, NULL, NULL, &tv))
    {
      recvfrom(...)
    }
    tv.tv_sec = 1;
    FD_ZERO(&rset);//this fixed it
    FD_SET(sockfd, &rset);//and this too
  }

  //contact everyone from sockaddr array (works like a charm!)
}

If I don't send my message from my other program to this program before a "timeout" occurs, the select statement "fails", so I can't use the recvfrom statement inside of it. I once made it so that my other program contacts this one on an infinite loop, it never went inside of the if statement.

What does work: If I contact this program before each timeout occurs, everything is fine. If I put the recvfrom statement outside of the if(___ && select), it works completely fine.

Here's a little diagram where this program is going to be called Recv:

if(A contacts Recv before timeout)    count = 0
   Recv stores contact A in struct
if(B contacts Recv before timeout)    count = 1
   Recv stores contact B in struct
if(timeout)                           count = 2

if(C contacts Recv after timeout)     count = 3
   nothing
                                      count = 4

the program will contact A and B just fine //goes back to start of loop

flag_array == -1 is false             count = 0
flag_array == -1 is false             count = 1
flag_array == -1 is true...select "fails" count = 2..3..4..(exit loop)

2 minutes before posting this I decided to take one last look at my previous code. I guess I forgot

FD_ZERO(&rset);
FD_SET(sockfd, &rset);

after the for loop (where tv.tv_sec = 1) is at.

Can someone elaborate on why it's necessary to do this?

回答1:

select() modifies the passed fd_set - You have to set it up before each call to select(). This is just how select() should work.



回答2:

It's necessary because select() may modify the file descriptor sets.

Citing from the Linux manpage for select(3):

Upon successful completion, the pselect() or select() function shall modify the objects pointed to by the readfds, writefds, and errorfds arguments to indicate which file descriptors are ready for reading, ready for writing, or have an error condition pending, respectively, and shall return the total number of ready descriptors in all the output sets. For each file descriptor less than nfds, the corresponding bit shall be set on successful completion if it was set on input and the associated condition is true for that file descriptor.

Note that select() may also modify its struct timeval argument, e.g. Linux stores the elapsed time in it. Thus, you should reset all fields of tv as well.