مشكلة المقبس غير المحظورة في حالة استخدامها مع 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 Page ، يجب أن أقرأ/الكتابة حتى أحصل EAGAIN, ، لكنني حصلت عليه بالفعل! وأنا متأكد من أن المخزن المؤقت لا يكون فارغًا. ما أقوم به خطأ؟

UPD: لقد اكتشفت شيئًا مثيرًا للاهتمام: عندما يحدث هذا الموقف ، أستخدمه في الكود الخاص بي sleep(1) و recc(...) مرة أخرى وأحصل على بيانات أتوقعها! إنها خدعة قذرة. هل هناك أي نهج أكثر رشيقة لحل هذه المشكلة؟

هل كانت مفيدة؟

المحلول

إنه طبيعي تمامًا لأول مرة recv() في هذه الحالة للعودة EAGAIN. epoll() لم يخبرك أبدًا إذا كان يمكن قراءته حتى الآن أم لا.

كل مفرد recv() يجب أن تكون مستعدة للتعامل معها EAGAIN إذا كنت تستخدم مآخذ غير محظورة. الاستيقاظ الزائفة ممكنة ، لذلك كلما أحب API select(), poll() أو epoll() يخبرك أن المقبس قابل للقراءة ، إنه يقول فقط "إنه قد كن قابلاً للقراءة - جربها ".

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top