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

Dan*_*Dan 5 c sockets select

我是套接字编程的新手,我正在努力彻底了解它是如何工作的,但是现在我真的被困在了select().

问题是在我的代码中,在select检测到活动并且fd保持设置之后,似乎在下一次迭代FD_ISSET中将自动返回true,就像忽略select函数一样.问题似乎与这个问题相同,但我做了我在那里找到的所有内容并且无济于事:http://compgroups.net/comp.unix.programmer/how-does-fd_isset-return-0-after-returne/55058

select()因为我在Linux上,所以我确保重新初始化timeval变量,并且我理解这个函数在不同的操作系统上表现不同,我也在select FD_ZEROFD_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");
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

问题编辑,因为我是新用户,无法回答这个问题:

事实是我在选择之前初始化rfds,当它被赋予afds的值时,这反过来总是用FD_ZERO(&afds)设置; FD_SET(0,&afds); 这仍然不适合我.

这是我的理解:

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

  2. 在无限循环时输入,rfds = afds(rfds在循环开始时总是= afds)

  3. 此外,此时,FD_ISSET(0,&rfds)将始终为!= 0

  4. select的超时时间为5秒,所以此时如果我在5秒之前没有输入任何内容,它会退出,UNSETTING FD_ISSET(0,&rfds) - 是正确的吗?所以如果没有输入,select会实际取消设置fd 0.这似乎工作正常

  5. 当我在超时之前输入内容时,问题就出现了.此时,FD_ISSET(0,&rfds)返回!= 0,它打印fd 0设置,然后每个循环fd将被设置

好的,这是准确的,我做对了吗?所以实际选择不等待时间通过,因为它实际上检测到fd已准备好并退出,设置fd!= 0?

这将提出另一个问题:如果我需要服务器每隔一段时间自动向几个客户端发送消息(独立于从客户端读取的内容),是否可以通过调整代码使用select和gettimeofday来完成以上?

谢谢您的帮助.

dus*_*uff 14

select()是水平触发的,而不是边缘触发的.当你传递一组文件描述符时,它会回来告诉你哪些是可读/可写/异常的,而不仅仅是最近改变状态的那些.

在这种情况下,每次调用时都会将FD标记为可读,select()因为当它出现时,您没有做任何事情使其无法读取(如排空可用输入).

  • 对.这里的想法是,如果你正在观察FD上的可读性/可写性,那么只要"select()"告诉你它已准备就绪,你就会读取/写入该FD.如果你不这样做,那就会有点困惑. (2认同)

Wes*_*ler 6

存储在fd_set中的值在select()触发后仍然存在.在应用程序中,select()可以监视许多套接字.自动清理fd_set意味着您永远无法检测到多个套接字需要维修.

您需要在无限循环内执行FD_ZERO()和FD_SET(),以便在每次传递时,在调用select()之前干净地初始化fd_set.