Linux асинхронный сервер epoll (), имеющий проблемы при возникновении EPOLLRDHUP - PullRequest
0 голосов
/ 03 января 2019

Я пытаюсь создать асинхронный веб-сервер с использованием epoll () в Linux, но проблемы возникают всякий раз, когда происходит событие EPOLLRDHUP. Когда флаг EPOLLONESHOT не установлен, сервер пытается обработать бесполезное событие (не EPOLLIN или EPOLLOUT) несколько раз в течение цикла без остановки, что приводит к тому, что сервер полностью перестает отвечать (и требует перезапуска). Когда установлен флаг EPOLLONESHOT, сервер просто перестает отвечать на запросы в течение короткого периода времени (секунды), а затем снова становится отзывчивым (что по-прежнему не идеально). Я не уверен, что может быть причиной этого, так как я закрываю сокет всякий раз, когда происходит EPOLLRDHUP. Вот мой код:

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 Ответ

0 голосов
/ 03 января 2019

Этот шаблон, который вы используете дважды, неверен:

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

Вы устанавливаете data.ptr для своей локальной переменной currentConnection (которая используется повторно и постоянно перезаписывается), когда она действительно должна указывать на ваш массив подключений!

Насколько я понимаю, currentConnection должен быть указателем типа:

struct connection *currentConnection;

и присвоение позже в вашем коде должно быть похоже на

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

Конечно, вы должны исправить доступ к элементам структуры и значение data.ptr, как вы делаете здесь:

currentConnection.event.data.ptr = &currentConnection;

вообще не нужно.

...