1

I am having this server code below, select function in this code throws some times bad file descriptor error along with the error code 9 when some client tries to connect to this, have gone through the code and could not find anything wrong with this, any help will be greatly appreciated, and this behavior is not constant though!

main()
{
    int server_fd, client_fd;
    socklen_t server_len, client_len;
    struct sockaddr_in server_address, client_address;
    fd_set readfds, testfds;
    char errorstr[128];
    char req_buf[REQ_BUF_LEN];
    int result;
    struct itimerspec time_period = {{10,0},{10,0}}; /* periodic timer */
    struct timeval sel_timeout = {0, 100000};
    int lines = 0;

    FD_ZERO(&readfds);
    FD_ZERO(&testfds);

    /* Create and name a socket for the server */
    server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if( server_fd < 0 ) {
        printf("socket creation failed \n");
        exit(0);
    }
    int on = 1;
    /* Enable address reuse */
    int ret = setsockopt( server_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) );

    server_address.sin_family = AF_INET;
    server_address.sin_addr.s_addr = htonl(INADDR_ANY);
    server_address.sin_port = htons(BL_MANAGER_PORT);
    server_len = sizeof(server_address);

    result = bind(server_fd, (struct sockaddr *)&server_address, server_len);
    if( result < 0 ) {
        printf("Bind faield \n");
        exit(0);
    }

    /* Create a connection queue and wait for clients */
    result = listen(server_fd, MAX_CLIENTS);
    if( result < 0 ) {
        printf("Listen failed\n");
        exit(0);
    }

    FD_SET(server_fd, &readfds);

    /* create timer to fire every one second */
 int timer_fd  =  timerfd_create(CLOCK_MONOTONIC , 0);
    if( timer_fd < 0 ) {
        printf("Timer fd failed\n");
        exit(0);
    }

    result = timerfd_settime(timer_fd, 0, &time_period, NULL);
    if( result < 0 ) {
        printf("timer fd set time failed\n");
        exit(0);
    }
    FD_SET(timer_fd, &readfds);


    while(1) {
        int fd;
        int nread;
        int result;
        char *ptr;
        int len_read = 0;
        uint64_t expired = 0;

        testfds = readfds;

        result = select(FD_SETSIZE, &testfds, NULL, NULL, &sel_timeout);
        if( result < 0 ) {
           perror ("The following error occurred");
           printf("select failed");
           exit(0);
        }

        /* There is activity. Find the descriptor */
        for(fd = 0; fd < FD_SETSIZE; fd++) {
            if(FD_ISSET(fd, &testfds)) {
                if (fd == timer_fd) {
                        read(timer_fd, &expired, sizeof(uint64_t));
                        printf("rate = %d\n", (lines/(10 * (uint32_t)expired)));
                        lines = 0;
                } else if( fd == server_fd ) {         /* is this server fd? */
                    client_fd = accept(server_fd,
                                       (struct sockaddr *)&client_address,
                                       &client_len);
                    FD_SET(client_fd, &readfds);
                } else {                                 /* is this read fd? */
                    ioctl(fd, FIONREAD, &nread);
                    //assert(nread < REQ_BUF_LEN);
                    if( nread == 0 ) {
                        FD_CLR(fd, &readfds);
 close(fd);
                    } else {
                        nread = read(fd, req_buf, REQ_BUF_LEN-1);
                        //req_buf[nread] = '\0';
                        //printf("%s\n", req_buf);
                        ptr = req_buf;
                        while ((*ptr != EOF) && ++len_read < nread) {
                                if (*ptr == '\n') {
                                        ++lines;
                                }
                                ptr++;
                        }
                        //printf("num of lines: %d\n", lines);
                    }
                }
            } /* FD_ISSET(fd, &testfds)? */
        }
    } /* while(1) */
}
bana
  • 397
  • 1
  • 5
  • 16
  • Does it help if you give a smaller first argument to `select()` than `FD_SETSIZE`? – Barmar Mar 13 '13 at 23:52
  • i tried giving server_fd+1 but of no help – bana Mar 13 '13 at 23:53
  • What does perror say? errno==9 := EAGAIN ?? `while ((*ptr != EOF) && ++len_read < nread) {` why do you test a character against -1? is this part of the protocol? UPDATE: I saw you made a copy (for the readset) – wildplasser Mar 13 '13 at 23:55
  • @wildplasser : as of now i have just one client which will communicate with this server , in this case does it matter if i don't re-initialize the fd_set? and how come the crash doesn't happen every time? perror says : Bad file descriptor Yea I think so ! – bana Mar 13 '13 at 23:57
  • @bana: You should not directly assign one `fd_set` to another, use `FD_COPY()` to copy it (or copy its contents manually if your platform does not support `FD_COPY()`). You also need to reset your `timeval` every time you call `select()` because it modifies the `timeval` upon output on some platforms, to report how much time is left from the original value so you can calculate how much time actually elapsed during the wait for one of the descriptors to be signaled. – Remy Lebeau Mar 14 '13 at 00:20
  • @RemyLebeau : fd_set is guaranteed to be an lvalue (most probably a struct) so copying is allowed. – wildplasser Mar 14 '13 at 00:24
  • @wildplasser: [Are there any platforms where using structure copy on an fd_set (for select() or pselect()) causes problems?](http://stackoverflow.com/questions/2421672). With that said, it would be better to use `poll()` or `epoll()` when available, then you don't have to deal with `fd_set` at all. – Remy Lebeau Mar 14 '13 at 00:45
  • Poll is not "better", maybe only a bit more flexible. FD_SETSIZE is typically a few hundred items, large enough for programs like the above; pollsets can be made arbitrarily large. The amount of code required to maintain them is comparible. – wildplasser Mar 14 '13 at 00:53

1 Answers1

0

You are misusing select(), so you get random results from it. Try something more like this:

main()
{
    int server_fd, client_fd, max_fd = 0;
    socklen_t server_len, client_len;
    struct sockaddr_in server_address, client_address;
    fd_set readfds, testfds;
    char errorstr[128];
    char req_buf[REQ_BUF_LEN];
    int result;
    struct itimerspec time_period = {{10,0},{10,0}}; /* periodic timer */
    struct timeval sel_timeout;
    int lines = 0;

    FD_ZERO(&readfds);

    /* Create and name a socket for the server */
    server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if( server_fd < 0 ) {
        printf("socket creation failed \n");
        exit(0);
    }
    int on = 1;
    /* Enable address reuse */
    int ret = setsockopt( server_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) );

    server_address.sin_family = AF_INET;
    server_address.sin_addr.s_addr = htonl(INADDR_ANY);
    server_address.sin_port = htons(BL_MANAGER_PORT);
    server_len = sizeof(server_address);

    result = bind(server_fd, (struct sockaddr *)&server_address, server_len);
    if( result < 0 ) {
        printf("Bind faield \n");
        exit(0);
    }

    /* Create a connection queue and wait for clients */
    result = listen(server_fd, MAX_CLIENTS);
    if( result < 0 ) {
        printf("Listen failed\n");
        exit(0);
    }

    FD_SET(server_fd, &readfds);
    max_fd = server_fd;

    /* create timer to fire every one second */
    int timer_fd  =  timerfd_create(CLOCK_MONOTONIC , 0);
    if( timer_fd < 0 ) {
        printf("Timer fd failed\n");
        exit(0);
    }

    result = timerfd_settime(timer_fd, 0, &time_period, NULL);
    if( result < 0 ) {
        printf("timer fd set time failed\n");
        exit(0);
    }

    FD_SET(timer_fd, &readfds);
    if (max_fd < timer_fd)
        max_fd = timer_fd;

    while(1) {
        int fd;
        int nread;
        int result;
        char *ptr;
        uint64_t expired = 0;

        FD_ZERO(&testfds);
        FD_COPY(&readfds, &testfds);

        sel_timeout.tv_sec = 0;
        sel_timeout.tv_usec = 100000;

        result = select(max_fd+1, &testfds, NULL, NULL, &sel_timeout);
        if( result < 0 ) {
           perror ("The following error occurred");
           printf("select failed");
           exit(0);
        }

        /* There is activity. Find the descriptor */
        for(fd = 0; fd <= max_fd; fd++) {
            if(FD_ISSET(fd, &testfds)) {
                if (fd == timer_fd) {
                    read(timer_fd, &expired, sizeof(uint64_t));
                    printf("rate = %d\n", (lines/(10 * (uint32_t)expired)));
                    lines = 0;
                } else if( fd == server_fd ) {         /* is this server fd? */
                    client_fd = accept(server_fd,
                                       (struct sockaddr *)&client_address,
                                       &client_len);
                    if (client_id < 0) {
                        perror ("The following error occurred");
                        printf("accept failed");
                    } else {
                        FD_SET(client_fd, &readfds);
                        if (max_fd < client_fd)
                            max_fd = client_fd;
                    }
                } else {                                 /* is this read fd? */
                    nread = read(fd, req_buf, REQ_BUF_LEN);
                    if( nread <= 0 ) {
                        FD_CLR(fd, &readfds);
                        close(fd);
                        if (fd == max_fd) {
                            max_fd = 0;
                            for (int fd2 = fd-1; fd2 >= 0; fd2--) {
                                if(FD_ISSET(fd2, &readfds)) {
                                    max_fd = fd2;
                                    break;
                                }
                            }
                        }
                    } else {
                        //printf("%.*s\n", nread, req_buf);
                        ptr = req_buf;
                        while ((nread > 0) && (*ptr != EOF)) {
                            if (*ptr == '\n') {
                                ++lines;
                            }
                            ptr++;
                            nread--;
                        }
                        //printf("num of lines: %d\n", lines);
                    }
                }
            } /* FD_ISSET(fd, &testfds)? */
        }
    } /* while(1) */
}

With that said, it would be better to use poll() or epoll() when available, then you don't have to deal with fd_set at all.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770