【问题标题】:nonblocking socket recv problem while using it with epoll与 epoll 一起使用时的非阻塞套接字接收问题
【发布时间】:2010-09-08 03:55:12
【问题描述】:

我遇到了一个问题:在 edge-triggered 模式下使用 epoll 时,有时(不定期)recv 返回 -1errno == EAGAIN。一段代码:

server_sock = startup(&port);

if ( (epollfd = epoll_create(4096)) < 0) {
    perror("epoll_create error");
    exit(EXIT_FAILURE);
}

ev.events = EPOLLIN | EPOLLET;
ev.data.fd = server_sock;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, server_sock, &ev) == -1) {
    perror("epoll_ctl: server_sock");
    exit(EXIT_FAILURE);
}

while (1) {
    int nfds = epoll_wait(epollfd, events, 4096, -1);
    if (nfds == -1) {
        perror("epoll_wait");
        exit(EXIT_FAILURE);
    }

    for (int i = 0; i < nfds; i++) {
        if (events[i].data.fd == server_sock) {
            client_sock = accept(server_sock,
                         (struct sockaddr *)&client_name,
                         (socklen_t *)(&client_name_len));

        if (client_sock == -1) //server overloaded
            continue;

        if (events[i].events & EPOLLIN) {
            std::cout << "EPOLLIN on " << client_sock << std::endl;
        }

        Arch::set_nonblocking(client_sock);
        ev.events = EPOLLIN | EPOLLRDHUP | EPOLLET; //input data and connection closing
        ev.data.fd = client_sock;

        if (epoll_ctl(epollfd, EPOLL_CTL_ADD, client_sock, &ev) == -1) {
            perror("epoll_ctl: client_socket");
            exit(EXIT_FAILURE);
        }

        accept_request(client_sock);

        } else {
            if (events[i].events & EPOLLRDHUP) {
                epoll_ctl(epollfd, EPOLL_CTL_DEL, events[i].data.fd, &ev);
            }
        }
    }
}

startup(&amp;port) 创建非阻塞套接字,与端口绑定等。我的脚本发送以下数据: GET /connect?id=1&amp;secret=1 HTTP/1.0\r\n\r\n 但有时recv 在此函数中返回-1(在accept_request 内部调用):

/**********************************************************************/
/* Get a line from a socket, whether the line ends in a newline,
 * carriage return, or a CRLF combination.  Terminates the string read
 * with a null character.  If no newline indicator is found before the
 * end of the buffer, the string is terminated with a null.  If any of
 * the above three line terminators is read, the last character of the
 * string will be a linefeed and the string will be terminated with a
 * null character.
 * Parameters: the socket descriptor
 *             the buffer to save the data in
 *             the size of the buffer
 * Returns: the number of bytes stored (excluding null) */
/**********************************************************************/
int get_line(int sock, char *buf, int size) {
    int i = 0;
    char c = '\0';
    int n;

    while ((i < size - 1) && (c != '\n')) {
        n = recv(sock, &c, 1, 0);
        //debug
        std::cout << "n = " << n << std::endl;
        if (n > 0) {
            if (c == '\r') {
                n = recv(sock, &c, 1, MSG_PEEK);
                if ((n > 0) && (c == '\n'))
                    recv(sock, &c, 1, 0);
                else
                    c = '\n';
            }
            buf[i] = c;
            i++;
        } else {
            //debug
            if (errno == EWOULDBLOCK)
                std::cout << "EWOULDBLOCK" << std::endl;
            c = '\n';
        }
    }
    buf[i] = '\0';

    return(i);
}

正如 epoll 手册页所写,我必须读/写直到我得到 EAGAIN,但我已经明白了!我确定缓冲区不是空的。我做错了什么?

UPD: 我发现了一件有趣的事情:当这种情况发生时,我在我的代码中再次使用sleep(1)recc(...),我得到了我期望的数据!这是一个肮脏的把戏。有没有更优雅的方法来解决这个问题?

【问题讨论】:

    标签: c linux sockets nonblocking epollet


    【解决方案1】:

    在这种情况下,第一个recv() 返回EAGAIN 是完全正常的。 epoll() 从未告诉你它是否可读。

    每个 recv() 应该准备好处理 EAGAIN 如果您使用的是非阻塞套接字。虚假唤醒是可能的,所以每当像 select()poll()epoll() 这样的 API 告诉您套接字是可读的时,它只会说“它可能是可读的 - 试试看” .

    【讨论】:

    • 但在epoll_wait 返回的那一刻,我当然知道缓冲区中有数据(GET /connect?id=1&amp;secret=1 HTTP/1.0\r\n\r\n)。在那种情况下,为什么recv 返回EAGAIN 以及我必须做些什么才能正确处理它?
    • @milo: 没有。epoll_wait() 只告诉你有一个新的套接字连接到accept() - 它确实没有告诉你有数据要从新的插座。如果recv() 返回EAGAIN,您要么 1) 处理您已经拥有的数据,要么 2) 返回 epoll_wait() 并等待更多时间。
    • 如果 1) 我无法处理任何数据...所以如果 2) 我将返回 epoll_wait 我会丢失我的数据。对吗?
    • @milo:您不会丢失任何数据,除非您专门将其丢弃。在recv() 将数据交给您之前,操作系统会保留它。
    猜你喜欢
    • 1970-01-01
    • 2023-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-31
    • 2013-10-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多