当EPOLLRDHUP发生时,Linux异步epoll()服务器有问题。

1 人关注

我试图在Linux中使用epoll()制作一个异步网络服务器,但每当事件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;
    
5 个评论
jxh
你的代码例子有点太复杂了。你能把代码缩减到只有等待循环和关闭套接字以及创建循环的配方吗?
jxh
What is MAX_EVENTS ?
@jxh 10,它被传递到epoll_wait()的maxevents参数中。
jxh
以上是重现你的问题的一个尝试,但没有出现无限循环。
c
linux
sockets
asynchronous
epoll
Cpp plus 1
Cpp plus 1
发布于 2019-01-03
1 个回答
Ctx
Ctx
发布于 2019-01-03
已采纳
0 人赞同

这里的这个模式,你用了两次,是错误的。

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的设置。