Проблема Recv безблокирующей сокета при использовании его с EPOLL

StackOverflow https://stackoverflow.com/questions/3664537

Вопрос

У меня проблема: иногда (не регулярно) RECV возвращает -1 а также errno == EAGAIN при использовании epoll в edge-triggered режим. Кусок кода:

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(&port) Создает неблокирующее розетку, связывание с портом и так далее. Мой скрипт отправляет следующие данные:GET /connect?id=1&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 Man, я должен прочитать / писать, пока не получишь EAGAIN, но я уже получил это! И я уверен, что буфер не пуст. Что я делаю не так?

UPD: Я узнал интересную вещь: когда такая ситуация произошла, я использую в моем коде sleep(1) а также recc(...) Опять же, и я получаю данные, которые я ожидаю! Это грязный трюк. Есть ли более изящный подход к решению этой проблемы?

Это было полезно?

Решение

Это совершенно нормально для первого recv() в таком случае, чтобы вернуться EAGAIN. epoll() Никогда не говорил вам, если это было читаемо или нет.

Каждый recv() должны быть готовы обращаться EAGAIN Если вы используете не блокирующие розетки. Возможен ложные пробуждения, поэтому всякий раз, когда API вроде select(), poll() или epoll() говорит вам, что розетка читается, это только говорит «это мощь Будьте доступны - дайте ему попробовать ».

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top