为什么epoll_ctl说我的fd不好?

时间:2012-05-11 06:26:26

标签: c linux http sockets epoll

要了解有关linux中的epoll通知工具的更多信息,我一直在研究http服务器。服务器的结构基本上有一个表示请求的事件结构数组,然后在一些嵌套的for和while循环中迭代。

当我第一次写这个原始事件循环时,它工作得相当好。但是,当我重构代码时,循环变得不那么可靠了。特别是,我开始得到一堆(大约75%的请求)epoll_ctl错误,并将errno设置为BADFD。 epoll_ctl显然认为我的套接字文件描述符实际上不是套接字文件描述符。

我真的很困惑,为什么应该会有这样的性能下降,因为重构只包括a)清理请求的解析和b)将main函数移动到程序的开头。我查看了之前提交的服务器,并将主要功能与新版本进行了比较 - 它是相同的。

有没有人对这里可能发生的事情有所了解?不胜感激。

以下是供参考的主要功能的代码:

int main(int argc, char *argv[]) {
    c("in main");
    char *progname=argv[0];
    int sockfd, newsockfd, portno, clilen, n, pid, epollfd;
    struct sockaddr_in serv_addr, cli_addr;

    initFt();
    if (argc < 2 ) {
        fprintf(stderr, "\nERROR: No Port Provided\n");
        exit(EXIT_FAILURE);
    }
    if (!createSocket(&sockfd)) {
        fprintf(stderr, "%s ERROR, COULD NOT CREATE SERVER SOCKET\n", progname);
        exit(EXIT_FAILURE);
    }

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(atoi(argv[1]));
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    Bind(&sockfd, &serv_addr);
    listen(sockfd, 5);

    struct epoll_event *events = calloc(SOMAXCONN,  sizeof(struct epoll_event));
    struct epoll_event event;
    makeSocketNB(&sockfd);
    event.events = EPOLLIN | EPOLLET;
    event.data.fd = sockfd;

    if ((epollfd = createpoll())<0) {
        fprintf(stderr, "epoll create error\n");
        exit(EXIT_FAILURE);
    }
    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, &event) == -1) {
        fprintf(stderr, "error with epoll_ctl on sockfd");
        exit(EXIT_FAILURE);
    }

    while (1) {
        int n, e; //e for events
        e = epoll_wait(epollfd, events, SOMAXCONN, -1);

        for (n=0; n<e; n++) {
            if ((events[n].events & EPOLLERR) || (events[n].events & EPOLLHUP) || !(events[n].events & EPOLLIN)) {
                fprintf (stderr, "epoll error\n");
                close (events[n].data.fd);
                continue;
            }
            else if (events[n].data.fd == sockfd) {
                while (1) {
                    newsockfd=accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
                    if (newsockfd>0) {
                        makeSocketNB(&newsockfd);
                        fprintf(stderr, "Accepted a new connection on fd %d, made nonblocking\n", newsockfd);
                    }
                    else if (errno == EAGAIN || errno == EWOULDBLOCK) {
                        fprintf (stderr, "we have accepted all the clients on this event\n");
                        break;
                    }
                    event.data.fd = newsockfd;
                    event.events = EPOLLIN | EPOLLET;
                    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, newsockfd, &event)<0) {
                        fprintf(stderr, "epoll_ctl error\n");
                        if (errno == EEXIST) {
                            fprintf(stderr, "fd already registered\n");
                        }
                        else if (errno == EBADF) {
                            fprintf(stderr, "fd bad\n%d\n", newsockfd);
                            break;
                        }
                        else if (errno == ENOMEM) {
                            fprintf(stderr, "no memory\n");
                        }
                        else if (errno == ENOSPC) {
                            fprintf(stderr, "enospc\n");
                        }
                        exit(EXIT_FAILURE);
                    }
                    continue;
                }
            }
            else { // there is stuff for us to read
                handleResponse(&events[n].data.fd);
                fprintf(stderr, "we are about to close file descriptor %d\n", events[n].data.fd);
                close (events[n].data.fd);
                fprintf(stderr, "connection closed\n");
            }
        }
    }

    exit(EXIT_SUCCESS);
}

1 个答案:

答案 0 :(得分:2)

你的accept代码并没有真正处理错误:它只处理“成功”案例和willblock案例。

if (newsockfd>0) {
    /* success */
} else if (errno == EAGAIN || errno == EWOULDBLOCK) {
    /* wouldblock */
}

/* Could be error but you call `epoll_ctl` anyway. */