代码之家  ›  专栏  ›  技术社区  ›  Cpp plus 1

Linux异步epoll()服务器在epollrdhup发生时出现问题

  •  1
  • Cpp plus 1  · 技术社区  · 6 年前

    我正试图在Linux中使用epoll()创建一个异步Web服务器,但每当事件epollrdhup发生时,就会出现问题。如果未设置epolloneshot标志,服务器会在循环过程中多次尝试处理无用事件(非epollin或epollout),而不停止,这会导致服务器完全没有响应(并需要重新启动)。当设置了标记epolloneshot时,服务器只会在短时间内(秒)没有响应,然后再次响应(这仍然不理想)。我不确定是什么导致了这种情况,因为每当发生epolldhup时,我都会关闭套接字。这是我的代码:

    int server_fd, new_socket; 
    long valRead;
    struct sockaddr_in address;
    int addrlen = sizeof(address);
    
    //setup code (bind(), etc.) would be here
    
    struct connection {
        int socket;
        unsigned int connectionType;
        void* dataToSend;
        unsigned int dataByteSize;
        struct epoll_event event;
        bool active;
    };
    
    struct connection* connections = (struct connection*)malloc(1000 * sizeof(struct connection));
    connections[0].socket = server_fd;
    connections[0].connectionType = 1U;
    connections[0].event.events = EPOLLIN;
    connections[0].event.data.ptr = &connections[0];
    
    unsigned int connectionIndex = 1U;
    
    fcntl(server_fd, F_SETFL, O_NONBLOCK);
    
    int epollFd = epoll_create(10);
    epoll_ctl(epollFd, EPOLL_CTL_ADD, server_fd, &connections[0].event);
    
    struct epoll_event* receivedEvents = malloc(sizeof(struct epoll_event) * 1000);
    struct connection currentConnection;
    
    #define MAX_EVENTS 10
    int numEventsReady;
    unsigned int eventIndex;
    while (1) {
        printText("\n+++++++ Waiting for new connection ++++++++\n\n", 46);
    
        numEventsReady = epoll_wait(epollFd, receivedEvents, MAX_EVENTS, -1);
        if (numEventsReady == -1) {
            printf("\nErrno:");
            printf("%i", errno);
            printf("\n");
            fprintf(stderr, "epoll_wait() failed: %s\n", strerror(errno));
            exit(7);
        }
        eventIndex = 0U;
        while (eventIndex < numEventsReady) {
            currentConnection = *((struct connection*)receivedEvents[eventIndex].data.ptr);
            switch (currentConnection.connectionType) {
            case 1U:
                //printText("\nConnected", 10);
                new_socket = accept4(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC);
                if (new_socket != -1) {
                    connections[connectionIndex].socket = new_socket;
                    connections[connectionIndex].connectionType = 2U;
                    connections[connectionIndex].event.events = EPOLLIN | EPOLLRDHUP;
                    connections[connectionIndex].event.data.ptr = &connections[connectionIndex];
    
                    epoll_ctl(epollFd, EPOLL_CTL_ADD, new_socket, &connections[connectionIndex].event);
    
                    ++connectionIndex;
                }
                break;
            case 2U:
                if (receivedEvents[eventIndex].events & EPOLLERR) {
                    printf("\nEPOLLERR\n");
                    close(currentConnection.socket);
                }
                else if(receivedEvents[eventIndex].events & EPOLLHUP) {
                    printf("\nEPOLLHUP\n");
                    close(currentConnection.socket);
                }
                else if(receivedEvents[eventIndex].events & EPOLLRDHUP) {
                    printf("\nEPOLLRDHUP\n");
                    close(currentConnection.socket);
                }
                else if (receivedEvents[eventIndex].events & EPOLLIN) {
                    valRead = recv(currentConnection.socket, buffer, 65536, 0);
                    if (valRead < 1) {
                        printf("recv error");
                        if (errno != EAGAIN && errno != EWOULDBLOCK) {
                            printf("errno != EAGAIN && errno != EWOULDBLOCK");
                            close(currentConnection.socket);
                        }
                        break;
                    }
                    printText(buffer, valRead);
    
                    currentConnection.event.events = EPOLLOUT | EPOLLRDHUP;
                    currentConnection.event.data.ptr = &currentConnection;
                    epoll_ctl(epollFd, EPOLL_CTL_MOD, currentConnection.socket, &currentConnection.event);
    
                    if (buffer[0] == 'G' && buffer[1] == 'E' && buffer[2] == 'T') {
                        switch (buffer[5]) {
                        case ' ': //default web page (index.htm)
                            currentConnection.dataToSend = indexHtm;
                            currentConnection.dataByteSize = sizeof(indexHtm);
                            //sendSocketData(new_socket, indexHtm, sizeof(indexHtm));
                            break;
                        }
                   }
                  else if (receivedEvents[eventIndex].events & EPOLLOUT) {
                    valRead = send(currentConnection.socket, currentConnection.dataToSend, currentConnection.dataByteSize, 0);
                    if (valRead == -1) {
                        printf("send error has ocurred\n");
                        if (errno != EAGAIN && errno != EWOULDBLOCK) {
                            printf("\nerrno != EAGAIN && errno != EWOULDBLOCK\n");
                            close(currentConnection.socket);
                        }
                        break;
                    }
    
                    currentConnection.event.events = EPOLLIN | EPOLLRDHUP;
                    currentConnection.event.data.ptr = &currentConnection;
                    valRead = epoll_ctl(epollFd, EPOLL_CTL_MOD, currentConnection.socket, &currentConnection.event);
                }
                break;
            }
            ++eventIndex;
        }
    }
    
    1 回复  |  直到 6 年前
        1
  •  1
  •   Ctx    6 年前

    这里使用两次的模式是错误的:

    currentConnection.event.data.ptr = &currentConnection;
    valRead = epoll_ctl(epollFd, EPOLL_CTL_MOD, 
                        currentConnection.socket, &currentConnection.event);
    

    你正在设置 data.ptr 到局部变量 currentConnection (它一直被重用和覆盖),当它真正指向您的连接数组时!

    据我所知, 当前连接 应为指针类型:

    struct connection *currentConnection;
    

    你的代码后面的任务应该是

    currentConnection = (struct connection*)receivedEvents[eventIndex].data.ptr;
    

    当然,您必须修复结构成员访问和 数据PTR 就像你在这里做的:

    currentConnection.event.data.ptr = &currentConnection;
    

    根本不需要。