Sockets – Why does FD_ISSET return true after select()


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().

The problem is that in my code, after select detects activity and the fd stays set, it seems that on next iterations FD_ISSET will 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:

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_ZERO and FD_SET before select.

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_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.

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, UNSETTING FD_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 before the timeout. At this point, FD_ISSET(0, &rfds) returns != 0, it prints fd 0 is set, and then each loop fd will be set

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 ?

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?

Thanks for the help.

Best Solution

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.

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