Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use select and FD_SET in socket programming?

I am new to socket programming, and I am having trouble understanding how select() and FD_SET() works.

I modify an example from Beej's tutorial in an attempt to figure it out. What I want to do in the for loop is at each iterations I wait for 4 seconds. If a read is available, I would print "A key was pressed" and if it timeout, then it would print "Timed out." Then I would clear the set and repeat the process 9 more times. But it seems that once file descriptor 0 is set, it never gets unset even after a call to FD_ZERO() and/or FD_CLR(). In other words after I press a key in the first iteration of the loop the file descriptor is set for the rest of the iterations and no more waiting is done. So there must be something I am missing, but I don't know what.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#define SERVERPORT 4950

int main(int argc, char *argv[]) {
    struct sockaddr_in their_addr; // connector's address information
    struct hostent *he;
    int numbytes;
    int broadcast = 1;

    if ((he=gethostbyname(argv[1])) == NULL) {  // get the host info
        perror("gethostbyname");
        exit(1);
    }

    // this call is what allows broadcast packets to be sent:
    if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &broadcast,
        sizeof broadcast) == -1) {
        perror("setsockopt (SO_BROADCAST)");
        exit(1);
    }
    their_addr.sin_family = AF_INET;     // host byte order
    their_addr.sin_port = htons(SERVERPORT); // short, network byte order
    their_addr.sin_addr = *((struct in_addr *)he->h_addr);
    memset(their_addr.sin_zero, '\0', sizeof their_addr.sin_zero);

    struct timeval tv;
    fd_set broadcastfds;

    int i;
    for(i=0; i < 10; i++) {
        tv.tv_sec = 4;
        tv.tv_usec = 500000;

        FD_ZERO(&broadcastfds);
        FD_CLR(0, &broadcastfds);
        FD_SET(0, &broadcastfds);
        if(select(0+1, &broadcastfds, NULL, NULL, &tv) == -1) perror("select");

        if (FD_ISSET(0, &broadcastfds)) printf("A key was pressed!\n");
        else printf("Timed out.\n");
        fflush(stdout); 
    }   
    close(sockfd);
    return 0;
}
like image 405
user1161604 Avatar asked Feb 06 '12 18:02

user1161604


1 Answers

You are using FD_SET correctly. You are asking select() to notify you when file descriptor 0 (standard input) is ready for reading. It does this. The problem is that you are not reading standard input to consume the input that is available. So when you loop back and call select() again, standard input is still ready for reading and it returns immediately.

The correct way to use select() (or poll(), which is usually a better option) is:

  • Set all of the file descriptors involved to nonblocking mode. For most use cases you want this because you want to do all your blocking inside select() (or poll()), not while servicing individual file descriptors.
  • Set up the arguments to select() or poll() to register which file descriptors you are interested in.
  • Call select() or poll().
  • React to the notifications it gives you by consuming input and writing output.
  • Loop back to step 2.

P.S.: What does your UDP socket sockfd have to do with anything? You open it but it doesn't get used for anything.

like image 195
Celada Avatar answered Sep 21 '22 12:09

Celada