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

标签 c linux sockets asynchronous epoll

我正在尝试在 Linux 中使用 epoll() 创建一个异步 Web 服务器,但是每当事件 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;
    }
}

最佳答案

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

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;

根本没有必要。

关于c - Linux 异步 epoll() 服务器在 EPOLLRDHUP 发生时出现问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54014453/

相关文章:

在 Win32 上停止进程的最干净方法?

linux - vi 中删除前 n 行的命令是什么?

ruby - 如何等待进程完成使用 IO.popen?

android - 如何从线程中的套接字获取数据到/从套接字获取数据?

sockets - 当send()成功时,WSAWaitForMultipleEvents返回超时

Malloc 的客户端/服务器问题?

c++ - 颠倒句子的单词?

c - (null) 在打印字符串时正在打印

c - 长文件路径的 stat 替代方案

linux - 如何使 diffstat 计数已删除和新添加的文件到 LOC 计数?