C语言 为什么 FD_ISSET 在 select() 后返回 true

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14485517/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 05:07:50  来源:igfitidea点击:

Why does FD_ISSET return true after select()

csocketsselect

提问by Dan

I am new to sockets programming and I'm trying to thoroughly understand how it works, but for now I'm really stuck on select().

我是套接字编程的新手,我正在尝试彻底了解它的工作原理,但现在我真的坚持使用select().

The problem is that in my code, after select detects activity and the fd stays set, it seems that on next iterations FD_ISSETwill return true automatically, like it would ignore the select function. The problem seems to be identical to this one, but I did all that I found there and to no avail: http://compgroups.net/comp.unix.programmer/how-does-fd_isset-return-0-after-returne/55058

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

I made sure to reinitialize the timeval variable after select()since I'm on Linux and I understood this function behaves differently on different OSes, I also reinitialized the fd set with FD_ZEROand FD_SETbefore select.

我确保重新初始化 timeval 变量,select()因为我在 Linux 上并且我知道这个函数在不同的操作系统上的行为不同,我还使用FD_ZEROFD_SET之前重新初始化了 fd 集。

What am I doing wrong? Here is the code:

我究竟做错了什么?这是代码:

#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");
    }
  }
}

Question edit since I'm a new user and can't answer this:

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

The fact is I initialize rfds before select when it is assigned the value of afds, which in turn is always set with FD_ZERO(&afds); FD_SET(0, &afds); This still doesn't work for me.

事实是我在选择之前初始化 rfds,当它被分配 afds 的值时,它总是用 FD_ZERO(&afds); 设置。FD_SET(0, &afds); 这仍然对我不起作用。

Here's what I understand:

这是我的理解:

  1. I add stdin file descriptor to afds

  2. Enter while infinite loop, rfds = afds (rfds will always be = afds at the start of the loop)

  3. Also, at this time, FD_ISSET(0, &rfds) will always be != 0

  4. select has a timeout of 5 seconds, so at this time if I don't type anything before the 5 seconds pass, it exits, UNSETTINGFD_ISSET(0, &rfds) - is that correct? so select will actually unset the fd 0 if nothing is typed. This seems to work OK

  5. The problem arrives when I type something beforethe timeout. At this point, FD_ISSET(0, &rfds) returns != 0, it prints fd 0 is set, and then each loop fd will be set

  1. 我将标准输入文件描述符添加到 afds

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

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

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

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

Ok, is this accurate, did I get it right? So practically select doesn't wait for the time to pass because it actually detects that the fd is ready and exits, setting the fd != 0 ?

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

Which would beg for a further question: if I need the server to send automatically messages to several clients every once in a while (independently of what it reads from clients), would it be possible to do it with select and gettimeofday by adapting the code above?

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

Thanks for the help.

谢谢您的帮助。

回答by duskwuff -inactive-

select()is level-triggered, not edge-triggered. When you pass it a set of file descriptors, it will come back telling you which ones are readable/writable/exceptional at the moment, not just the ones that have changed state recently.

select()是电平触发,不是边沿触发。当您向它传递一组文件描述符时,它会回来告诉您目前哪些是可读/可写/异常的,而不仅仅是最近更改状态的那些。

In this case, fhe FD is being marked as readable every time you call select()because you're not doing anything to make it notreadable (like draining the available input) when it shows up.

在这种情况下,FHE FD被每次调用时被标记为可读select(),因为你没有做任何事情,使之可读(如排水可用的输入),当它显示出来。

回答by Wes Miller

The values stored in your fd_set do remain after select() fires. In an application it is possible for select() to monitor many sockets. Automatically cleaning the fd_set would mean you could never detect that multiple sockets need servicing.

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

You need to do your FD_ZERO() and FD_SET() inside the infinite loop so that on each pass the fd_set is initialized cleanly before select() is called.

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