csocketsnonblockingposix-select

non-blocking I/O with select()


Can someone tell me why the following code does not work?

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/time.h>
#include <errno.h>

int main(int argc, char **argv)
{
    int sockfd, i, new = 0, maxfd, nready, on = 1;
    struct sockaddr_in saddr;
    ssize_t nbytes;
    fd_set rfds, master;
    char buffer[BUFSIZ];

    if(-1 == (sockfd = socket(PF_INET, SOCK_STREAM, 0)))
    {
        perror("socket()");
        exit(EXIT_FAILURE);
    }

    if(-1 == (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on))))
    {
        perror("setsockopt()");
        exit(EXIT_FAILURE);
    }

    if(-1 == (fcntl(sockfd, F_SETFD, O_NONBLOCK)))
    {
        perror("fcntl()");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    (void)memset(&saddr, '\0', sizeof(struct sockaddr_in));

    saddr.sin_port = htons(1234);
    saddr.sin_addr.s_addr = htonl(INADDR_ANY);
    saddr.sin_family = AF_INET;

    if(-1 == (bind(sockfd, (struct sockaddr*)&saddr, sizeof(saddr))))
    {
        perror("bind()");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    if(-1 == (listen(sockfd, 32)))
    {
        perror("listen()");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    FD_ZERO(&rfds);
    FD_ZERO(&master);
    maxfd = sockfd;
    FD_SET(sockfd, &master);

    printf("maxfd = %d\n", maxfd);

    for(;;)
    {
        (void)printf("select()ing...\n");

        (void)memcpy(&rfds, &master, sizeof(master));

        if(-1 == (nready = select(maxfd+1, &rfds, NULL, NULL, NULL)))
        {
            perror("select()");
            close(sockfd);
            exit(EXIT_FAILURE);
        }

        for(i=0; i<FD_SETSIZE; i++)
        {
            if(FD_ISSET(i, &rfds))
            {
                if(i == sockfd)
                {
                    (void)printf("trying to accept new connection(s)\n");

                    for(;;)
                    {
                        new = accept(sockfd, NULL, NULL);

                        if(new < 0)
                        {
                            if(errno != EWOULDBLOCK)
                            {
                                perror("accept()");
                                exit(EXIT_FAILURE);
                            }

                            /* no incomming connection */
                            break;
                        }

                        FD_SET(new, &master);

                        if(maxfd < new)
                            new = maxfd;

                        (void)printf("%d was added to set\n", new);

                    }

                }

                else
                {
                    for(;;)
                    {
                        (void)printf("trying to read data from ready socket(s)\n");

                        while((nbytes = recv(i, buffer, sizeof(buffer), 0)) > 0)
                            printf("%s", buffer);

                        if(nbytes < 0)
                        {
                            if(nbytes != EWOULDBLOCK)
                            {
                                perror("recv()");
                                exit(EXIT_FAILURE);
                            }

                            break;
                        }

                        if(0 == nbytes)
                        {
                            close(i);
                            break;
                        }

                        FD_CLR (i, &master);
                    }

                }


            }
        }


    }

    return 0;
}

It listens on *:1234 for new connections. It builds fine without any errors or warnings, but it can't recv() data from ready descriptors. I can't find the root of the problem, can someone help me?


Solution

  • The problem is the reading loop, as you read the bytes you receive, but when that's done you try to receive again, making recv block. You need to make the sockets non-blocking. And no, the blocking/non-blocking status is not inherited from the passive listening socket.