select和recvfrom出现意外结果

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!) } 

如果在“超时”发生之前我没有将我的消息从我的其他程序发送到该程序,则select语句“失败”,因此我不能在其中使用recvfrom语句。 我曾经这样做,以便我的其他程序在无限循环中联系这个,它从未进入if语句。

什么工作:如果我在每次超时发生之前联系这个程序,一切都很好。 如果我把recvfrom语句放在if(___ && select)之外,它就可以正常工作了。

这是一个小图,这个程序将被称为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 

程序将很好地联系A和B //返回循环开始

 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) 

在发布这个post前2分钟,我决定最后一次看看我之前的代码。 我想我忘记了

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

在for循环(其中tv.tv_sec = 1)之后。

有人可以详细说明为什么有必要这样做吗?

select() 修改传递的fd_set – 你必须在每次调用select()之前设置它。 这就是select()应该如何工作。

这是必要的,因为select()可以修改文件描述符集。

引用Linux手册页中的select(3) :

成功完成后,pselect()或select()函数将修改readfds,writefds和errorfds参数指向的对象,以指示哪些文件描述符已准备好进行读取,准备写入或具有待处理的错误条件, ,并应返回所有输出集中的准备描述符的总数。 对于小于nfds的每个文件描述符,如果在输入上设置了相应的位,则应在成功完成时设置相应的位,并且该文件描述符的相关条件为真。

请注意, select()也可以修改其struct timeval参数,例如Linux将经过的时间存储在其中。 因此,您也应该重置所有tv领域。