RootsAmongRuins
RootsAmongRuins

Reputation: 111

blocking select() from multiple sockets

Unix/C question here.

I have multiple sockets that I am trying to poll for periodic data. I don't want select to wait indefinitely so I have a timeout in place and I'm running in a loop. I have found that once a socket is ready to read, it is always ready to read. As in, I cannot have select go to sleep when there is no data to be read from any of the sockets.

for (i = 0; i < n_connections; i++) {
  FD_SET( sockfd[i], &master );
  if (sockfd[i] > fdmax) 
    fdmax = sockfd[i];
  }

for(;;) {
  int nready = 0;
  timeout.tv_sec  = 1;
  timeout.tv_usec = 0;
  read_fds = master;
  if ( (nready = select(fdmax+1, &read_fds, NULL, NULL, NULL)) == -1 ) {
    fprintf( stderr, "Select Error\n" );
    return FAILURE;
  }
  printf( "Number of ready descriptors: %d\n", nready );

  for (i = 0; i <= fdmax; i++) {
    if (FD_ISSET(i, &read_fds)) {
      if (( nbytes = recv(i, buf, sizeof(buf), 0)) <= 0 ) {
        if (nbytes == 0) {
          //connection closed
          printf("Socket %d hung up\n", i );
        }
        else {
          fprintf( stderr, "Recv Error %d\n", nbytes);
        }
      }
    else {
      printf( "Data Received on %d: %s\n", i, buf );
    }
  }
} // end file descriptor loop

It seems that after my first read, the 1 second timeout no longer applies and the socket is always "ready to read", even if there are 0 bytes available. How can I get select to sleep until data comes in (for the one second, or by switching the final argument to NULL, indefinitely waiting for data to come in on the socket?)

Output:

Number of Ready Descriptors: 2
Data Received on 4: GreetingsChap
Data Received on 5: HiMatengsChap
Loop...
Number of Ready Descriptors: 2
Socket 4 hung up
Socket 5 hung up
Loop...
Number of Ready Descriptors: 2
Socket 4 hung up
Socket 5 hung up
Loop...

Note: Code updated for clarity Updated based on @yvesBraumes suggestions - still doesn't work.

Upvotes: 7

Views: 5842

Answers (3)

Den-Jason
Den-Jason

Reputation: 2573

You are using FD_ISSET incorrectly. You need to be passing a socket ID to the "fd" parameter, not an index:

if (FD_ISSET(i, &read_fds))...

needs to be

if (FD_ISSET(sockfd[i], &read_fds))...

Likewise with recv.

Upvotes: 0

lundman
lundman

Reputation: 1726

Indeed, if recv returns 0 (and not -1, with errno=EWOULDBLOCK), the socket is closed. You should call close() on it as well, and take it out of the select() call. Otherwise it will remain in WAIT1 and release select() each time.

Upvotes: 6

Karoly Horvath
Karoly Horvath

Reputation: 96326

If you detect that a connection is closed, remove the socket from the fd set, otherwise select is going to report them (Socket 4 hung up).. select is not edge triggered, if you don't handle the event, it's going to report it again.

Upvotes: 7

Related Questions