0

I have an array of 6 sockets, each designed to listen for incoming messages. On each socket, I need to call select() to listen for incoming messages. This code compiles, but when I run it, I get a message

select failure: Invalid argument

errno: 22

Can anyone see what I am doing wrong? Thanks!

This code is in int main():

int listenfd[6];
fd_set read_set;
struct timeval tv;
time_t start_time;

make_connections(listenfd, neighbor, servAddr, read_set);

FD_ZERO(&read_set);

// add the accepted connections to the read set for use with select()
for (i=0; i<6; i++) {
    FD_SET(listenfd[i], &read_set);
}

tv.tv_sec = 5;                  // seconds
tv.tv_usec = 5000000;    // microseconds

gettimeofday(&tv, NULL);
start_time = tv.tv_sec;

// EDIT: made the following change per suggestion by ThePelaton
//        same error exists, though
//if ( (select(6, &read_set, NULL, NULL, &tv)) == -1) {

int max = listenfd[0];
for (i=1; i<6; i++) {
    if (listenfd[i] > listenfd[i-1]) {
        max = i;
    }
}

if ( (select(listenfd[max]+1, &read_set, NULL, NULL, &tv)) == -1) {
       perror("select failure");
       fprintf(stdout, "errno: %d\n", errno);
       exit(EXIT_FAILURE);
}

My other function:

void make_connections(int listenfd[6], Neighbor neighbor[6], struct sockaddr_in servAddr, fd_set read_set) {

    int num_hosts = 6;
    int i, rc, on=1;
    struct sockaddr_storage their_addr;
    socklen_t addr_size;

    for (i=0; i<6; i++) {        
        // Create one socket for each neighbor
        if((listenfd[i] = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
            exit(1);
        }

        /*************************************************************/
        /* Allow socket descriptor to be reuseable                   */
        /*************************************************************/
        rc = setsockopt(listenfd[i], SOL_SOCKET,  SO_REUSEADDR,
                        (char *)&on, sizeof(on));
        if (rc < 0)
        {
            perror("setsockopt() failed");
            close(listenfd[i]);
            exit(-1);
        }

        /*************************************************************/
        /* Set socket to be non-blocking.  All of the sockets for    */
        /* the incoming connections will also be non-blocking since  */
        /* they will inherit that state from the listening socket.   */
        /*************************************************************/
        rc = ioctl(listenfd[i], FIONBIO, (char *)&on);
        if (rc < 0)
        {
            perror("ioctl() failed");
            close(listenfd[i]);
            exit(-1);
        }

        /* get server IP address (input must be IP address, not DNS name) */
        bzero(&servAddr,sizeof(servAddr));                          //zero the struct
        servAddr.sin_family = AF_INET;                              //address family (ipv4)
        servAddr.sin_port = htons(neighbor.port);  //sets port to network byte order
        servAddr.sin_addr.s_addr = INADDR_ANY;

        // first attempt to bind / listen on each port
        if (bind(listenfd[i], (struct sockaddr *)&servAddr, sizeof(servAddr)) < 0) {
            perror("bind failed");
            exit (-1);
        }

        if (listen(listenfd[i], 10) < 0) {
            perror("listen() failed");
            exit (-1);
        }

    }
}

EDIT 2: Pretty sure that I figured this out. I was using my time value (tv) to do some timestamping of my process. Looks like that is where the invalid argument comes from, not from the sockets.

AndroidDev
  • 20,466
  • 42
  • 148
  • 239
  • 1
    Perhaps the `struct timeval` doesn't like having 5000000 (5 seconds) as part of the tv_usec field? What timeout value do you want? – Austin Phillips Nov 10 '13 at 22:27
  • At the moment, I don't really care about what value this is. I've tried a few different values here, but I get the same result every time. – AndroidDev Nov 10 '13 at 22:38
  • Actually, I just checked with a large tv_usec example on a Linux kernel and it appears to work as expected. In your example, the max delay would be 10 seconds. I don't think this is your issue, see ThePelaton's answer. – Austin Phillips Nov 10 '13 at 22:39

3 Answers3

4

Use the maximum value in listenfd[] + 1 on the call to select, not the hardcoded value of 6, other file descriptors, like stdin, etc., take the first few slots.

ThePelaton
  • 41
  • 3
  • I tried this before, but same error. You're right, though, that is how I should write this line. My use of the hardcoded value came from trying unsuccessfully to use this approach. I still get the same error, even when trying this. – AndroidDev Nov 10 '13 at 22:41
  • Make sure you are checking the return codes from socket(), bind(), and listen(). If the fd's are valid, it cannot fail. Refer to [another answer] (http://stackoverflow.com/questions/4006786/socket-programming-sendto-always-fails-with-errno-22) for a possible explanation. – ThePelaton Nov 11 '13 at 05:37
0

I think there is a bug in the following lines:

int max = listenfd[0];
for (i=1; i<6; i++) {
    if (listenfd[i] > listenfd[i-1]) {
        max = i;
    }
}

It should be modified as:

int max = 0;
for (i=1; i<6; i++) {
    if (listenfd[i] > listenfd[max]) {
        max = i;
    }
}
ciphor
  • 8,018
  • 11
  • 53
  • 70
0

The problem is in these lines:

gettimeofday(&tv, NULL);
start_time = tv.tv_sec;

The tv variable cannot be used to capture the current time, and then subsequently be passed as an argument to select(). Use a different timeval to handle time stamping and leave the original tv value for its primary purpose as a parameter of select();

AndroidDev
  • 20,466
  • 42
  • 148
  • 239