Skip to content
Advertisement

Linux asynchronous epoll() server having problems when EPOLLRDHUP occurrs

I’m trying to make an asynchronous web server using epoll() in Linux, but problems occur whenever the event EPOLLRDHUP occurs. When the flag EPOLLONESHOT is not set, the server tries to process a useless event (not EPOLLIN or EPOLLOUT) multiple times during the loop without stopping, which causes the server to be come completely unresponsive (and require a restart). When the flag EPOLLONESHOT is set, the server simply becomes unresponsive for a short period of time (seconds) and then becomes responsive again (which still is not ideal). I’m unsure what could be causing this, since I close the socket whenever EPOLLRDHUP occurs. Here is my code:

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 ++++++++nn", 46);

    numEventsReady = epoll_wait(epollFd, receivedEvents, MAX_EVENTS, -1);
    if (numEventsReady == -1) {
        printf("nErrno:");
        printf("%i", errno);
        printf("n");
        fprintf(stderr, "epoll_wait() failed: %sn", 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("nEPOLLERRn");
                close(currentConnection.socket);
            }
            else if(receivedEvents[eventIndex].events & EPOLLHUP) {
                printf("nEPOLLHUPn");
                close(currentConnection.socket);
            }
            else if(receivedEvents[eventIndex].events & EPOLLRDHUP) {
                printf("nEPOLLRDHUPn");
                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 ocurredn");
                    if (errno != EAGAIN && errno != EWOULDBLOCK) {
                        printf("nerrno != EAGAIN && errno != EWOULDBLOCKn");
                        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;
    }
}

Advertisement

Answer

This pattern here, which you use twice, is wrong:

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

You are setting the data.ptr to your local variable currentConnection (which is reused and overwritten all the time), when it really should point into your connections-array!

As far as I can see it, currentConnection should be a pointer type:

struct connection *currentConnection;

and the assignment later in your code should be like

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

You have to fix the struct member accesses of course and the setting of data.ptr like you do here:

currentConnection.event.data.ptr = &currentConnection;

shouldn’t be necessary at all.

User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement