samedi 4 janvier 2020

Why does this poll() code blocks in my Linux Environment?

This poll() multiplexing I/O code is from UNP Chap 6 and I tested in my Ubuntu 18 Platform, and I tried to transfer data from a client which may cause the program to be blocked at the point of poll() function. In detail, we I connected a client to this server, "connect" will be printed, but when I want to send data from client, the code cannot display them but be blocked at the point of the poll() invocation. The sample output is :

Before poll()

After poll()

connect

Before poll()

Then blocked.

#define _GNU_SOURCE
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <errno.h>
#include <signal.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <poll.h>
#include <limits.h>
#include <sys/stropts.h>

#define MAXLINE 1024
#define OPENMAX 1024
#define BACKLOG 1024
#define SERV_PORT 8888

void errExit(char *arg)
{
    printf("Err: %s\n", arg);
    exit(EXIT_FAILURE);
}

int main(int argc, char *argv[])
{
    int i, maxi, listenfd, connfd, sockfd;
    int nready;
    ssize_t n;
    char buf[MAXLINE];
    socklen_t clilen;
    struct pollfd client[OPENMAX];
    struct sockaddr_in cliaddr, servaddr;

    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if(listenfd < 0)errExit("socket()");

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port =  htons(SERV_PORT);

    int opt = 1;
    setsockopt(listenfd,SOL_SOCKET, SO_REUSEPORT, (const void*)&opt,sizeof(opt));

    if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1)
        errExit("bind()");

    if(listen(listenfd,BACKLOG) == -1)
        errExit("listen()");

    client[0].fd = listenfd;
    client[0].events = POLLRDNORM;

    for(i = 1; i < OPENMAX; i ++ )
        client[i].fd = -1;
    maxi = 0;

    while(1)
    {
        printf("Before poll()\n");

        if( (nready = poll(client, maxi + 1, -1))==-1)
            errExit("poll()");

        printf("After poll()\n");
        //**new connection**//
        if(client[0].revents & POLLRDNORM)
        {
            clilen = sizeof(cliaddr);
            if(connfd = accept(listenfd,(struct sockaddr*)&clilen, &clilen) == -1)
                errExit("accept()");
            printf("connect\n");

            for( i = 1; i < OPENMAX ; i ++ )
            {
                if(client[i].fd < 0)
                {
                    client[i].fd = connfd;
                    break;
                }
            }

            if( i == OPENMAX)
                errExit("TOO MANY CLIENTS");

            client[i].events = POLLRDNORM;

            if( i > maxi)
                maxi = i;

            if( --nready == 0)  //no more readable descriotor 
                continue;
        }

        for( i = 1; i < maxi ; i ++)
        {
            if( (sockfd = client[i].fd) < 0)
                continue;
            if( client[i].revents & (POLLRDNORM | POLLERR))
            {
                if( (n = read(sockfd, buf, MAXLINE)) < 0)
                {
                    if(errno == ECONNRESET)
                    {
                        printf("Connection Reset by Client\n");
                        close(sockfd);
                        client[i].fd = -1;
                    }
                    else
                    {
                        perror("Server: read error\n");
                    }                
                }

                else if( n == 0)
                {
                    client[i].fd = -1;
                    close(sockfd);
                }
                else
                {
                    printf("%s\n",buf);
                    write(sockfd, buf, n);
                }

                if( -- nready <= 0 )
                    break;
            }
        }
    }

}



Aucun commentaire:

Enregistrer un commentaire