为什么FD_ISSET在select()之后返回true

为什么FD_ISSET在select()之后返回true,c,sockets,select,C,Sockets,Select,我对sockets编程还不熟悉,我正试图彻底理解它是如何工作的,但现在我真的被select卡住了 问题是,在我的代码中,select检测到活动并且fd保持设置后,在下一次迭代中,fd_ISSET似乎会自动返回true,就像它会忽略select函数一样。这个问题似乎与这个问题相同,但我做了我在那里发现的所有事情,但都无济于事: 我确保在选择后重新初始化timeval变量,因为我在Linux上,并且我了解此函数在不同操作系统上的行为不同,我还使用fd_ZERO和fd_set在选择前重新初始化fd s

我对sockets编程还不熟悉,我正试图彻底理解它是如何工作的,但现在我真的被select卡住了

问题是,在我的代码中,select检测到活动并且fd保持设置后,在下一次迭代中,fd_ISSET似乎会自动返回true,就像它会忽略select函数一样。这个问题似乎与这个问题相同,但我做了我在那里发现的所有事情,但都无济于事:

我确保在选择后重新初始化timeval变量,因为我在Linux上,并且我了解此函数在不同操作系统上的行为不同,我还使用fd_ZERO和fd_set在选择前重新初始化fd set

我做错了什么?代码如下:

#include <stdio.h>
#include <strings.h>

#include <sys/select.h>
#include <sys/time.h>

int main () {
  struct timeval tv;

  tv.tv_sec = 5; // 5 seconds timeout
  tv.tv_usec = 0;

  fd_set afds, rfds;

  FD_ZERO(&afds);
  FD_SET(0, &afds);

  while (1) {
    rfds = afds;
    select(1, &rfds, NULL, NULL, &tv);
    // linux, reinitialize tv?
    tv.tv_sec = 5;
    tv.tv_usec = 0;

    // so at this point after select runs the first time and detects STDIN activity
    // it will enter an infinite loop printing "fd 0 is set" (why?)
    if (FD_ISSET(0, &rfds)) {
      printf("fd 0 is set\n");
      FD_CLR(0, &rfds);
    } else {
      printf("fd 0 is NOT set\n");
    }
  }
}
问题编辑,因为我是新用户,无法回答此问题:

事实上,当为RFD赋值时,我会在选择之前初始化RFD,而AFD的值总是用FD_ZERO&afds;FD_SET0和afds;这对我还是不起作用

以下是我的理解:

我将stdin文件描述符添加到afds

输入无限循环时,rfds=afds rfds将始终在循环开始处=afds

而且,此时,FD_ISSET0和RFD将始终是!=0

select的超时时间为5秒,因此,如果在5秒过去之前我没有键入任何内容,它将退出,取消设置FD_ISSET0和rfds-是否正确?因此,如果没有键入任何内容,select实际上将取消设置fd 0。这似乎行得通

当我在超时之前键入内容时,问题就出现了。此时,FD_为set0,&rfds返回!=0,它将打印设置为0的fd,然后将设置每个循环fd

好的,这准确吗,我说对了吗?因此实际上,select不会等待时间过去,因为它实际上检测到fd已准备就绪并退出,设置fd!=0

这就引出了一个更进一步的问题:如果我需要服务器每隔一段时间自动向多个客户端发送消息,而不依赖于它从客户端读取的内容,那么是否可以通过修改上面的代码来使用select和gettimeofday来实现呢

感谢您的帮助。

选择是级别触发的,而不是边缘触发的。当您向它传递一组文件描述符时,它会回来告诉您当前哪些文件是可读/可写/异常的,而不仅仅是最近状态发生变化的文件


在这种情况下,每次调用select时FD都会被标记为可读,因为您没有做任何事情使其不可读,比如在显示时耗尽可用输入。

在select激发后,FD集合中存储的值确实会保留。在应用程序中,select可以监视多个套接字。自动清理fd_集合意味着您永远无法检测到多个套接字需要维修


您需要在无限循环中执行FD_ZERO和FD_SET,以便在调用select之前,每次传递FD_SET时都会进行干净的初始化。

因此,会有一个解决方法,以便它在调用select之后会像第一次运行select一样进行操作。在我的理解中,它是这样工作的:@Dan:它的行为就像第一次运行一样-它告诉你fd 0 stdin的输入缓冲区中是否有数据。一旦有了数据,它就会一直存在,直到你把它拿出来。我现在得到了它,在我看来,我必须使用read或类似的东西。对。这里的想法是,如果您正在关注FD的可读写性,那么只要select告诉您FD准备好了,您就会从该FD读/写该FD。如果你不这样做,它会有点混乱。你错过了FD_ZERO和FD_SET是通过rfds=afds赋值在循环内部完成的