epoll, неблокируемые сокеты, recv и потерянные данные

Тема в разделе "WASM.UNIX", создана пользователем milo, 8 сен 2010.

  1. milo

    milo New Member

    Публикаций:
    0
    Регистрация:
    22 мар 2009
    Сообщения:
    43
    привет. есть следующая ситуация: использую epoll в edge-triggered режиме. в этом коде:
    Код (Text):
    1. while (1) {
    2.         int nfds = epoll_wait(epollfd, events, 4096, -1);
    3.         if (nfds == -1) {
    4.             perror("epoll_wait");
    5.             exit(EXIT_FAILURE);
    6.         }
    7.  
    8.         for (int i = 0; i < nfds; i++) {
    9.             if (events[i].data.fd == server_sock) {
    10.                 client_sock = accept(server_sock,
    11.                              (struct sockaddr *)&client_name,
    12.                              (socklen_t *)(&client_name_len));
    13.  
    14.             if (client_sock == -1) //server overloaded
    15.                 continue;
    16.  
    17.             if (events[i].events & EPOLLIN) {
    18.                 std::cout << "EPOLLIN on " << client_sock << std::endl;
    19.             }
    20.  
    21.             Arch::set_nonblocking(client_sock);
    22.             ev.events = EPOLLIN | EPOLLRDHUP | EPOLLET; //input data and connection closing
    23.             ev.data.fd = client_sock;
    24.  
    25.             if (epoll_ctl(epollfd, EPOLL_CTL_ADD, client_sock, &ev) == -1) {
    26.                 perror("epoll_ctl: client_socket");
    27.                 exit(EXIT_FAILURE);
    28.             }
    29.             accept_request(client_sock);
    30.             } else {
    31.                 if (events[i].events & EPOLLRDHUP) {
    32.                     std::cout << "EPOLLRDHUP on " << events[i].data.fd << std::endl;
    33.                     epoll_ctl(epollfd, EPOLL_CTL_DEL, events[i].data.fd, &ev);
    34.                 }
    35.             }
    36.         }
    37.     }
    я руками посылаю следующий запрос "GET /connect?id=1&secret=1 HTTP/1.0\r\n\r\n" . epoll_wait возвращает управление, как бы намекая на то, что есть какие-то шевеления, далее вызываем accept, все хорошо. вызываю свой accept_request который, в свою очередь вызывает следующую функцию:
    Код (Text):
    1. int get_line(int sock, char *buf, int size) {
    2.     int i = 0;
    3.     char c = '\0';
    4.     int n;
    5.  
    6.     while ((i < size - 1) && (c != '\n')) {
    7.         n = recv(sock, &c, 1, 0);
    8.         //debug
    9.         std::cout << "n = " << n << std::endl;
    10.         if (n > 0) {
    11.             if (c == '\r') {
    12.                 n = recv(sock, &c, 1, MSG_PEEK);
    13.                 if ((n > 0) && (c == '\n'))
    14.                     recv(sock, &c, 1, 0);
    15.                 else
    16.                     c = '\n';
    17.             }
    18.             buf[i] = c;
    19.             i++;
    20.         } else {
    21.             //debug
    22.             if (errno == EWOULDBLOCK)
    23.                 std::cout << "EWOULDBLOCK" << std::endl;
    24.             c = '\n';
    25.         }
    26.     }
    27.     buf[i] = '\0';
    28.  
    29.     return(i);
    30. }
    и первый же recv возвращает -1 и errno ставит в EWOULDBLOCK. Хотя данные ТОЧНО пришли, я их сам, своими собственными руками послал туда. далее интересный момент есть: если воткнуть перед recv какой-нибудь nanosleep на одну миллионную секунды, то все начинает работать как подобает. т.е. можно предположить, что данные еще не пришли в буфер. как быть? нанослип оставлять не красиво. есть более элегантный вариант?
     
  2. Aspire

    Aspire New Member

    Публикаций:
    0
    Регистрация:
    19 май 2007
    Сообщения:
    1.028
    Это нормально и это не является ошибкой. При этом возвращенном коде, ты должен крутить цикл дальше, обрабатывать другие данные и т.д., в чем и есть фишка не блокируемых сокетов.
    Точно так, значит - не пришли.
    См. выше. Крутим цикл дальше вызывая continue. В слудующий раз recv вернет кол-во пришедших данных доступных для чтения.
     
  3. milo

    milo New Member

    Публикаций:
    0
    Регистрация:
    22 мар 2009
    Сообщения:
    43
    я понимаю этот момент, но давайте взглянем на мою get_line(), там нет данных чтобы обрабатывать, надо ждать пока не придут данные, а дальше уже смотреть что это за символ мы вытащили. хорошо, предположим я делаю цикл следующего вида:
    Код (Text):
    1. do {
    2. n = recv(...);
    3. } while (n < 0 && errno == EAGAIN)
    тогда получается активное ожидание, что не есть хорошо на высоконагруженных серверах. или я ошибаюсь и мне надо пересмотреть механизм изъятия данных из сокета?
     
  4. Aspire

    Aspire New Member

    Публикаций:
    0
    Регистрация:
    19 май 2007
    Сообщения:
    1.028
    Можно, конечно, и так, но тогда теряется смысл не блокируемых сокетов. На высоконагуженных серверах, пока данный сокет вам возвращает EWOULDBLOCK вы должны обрабатывать данные с других сокетов, т.е. крутить не тот цикл, который выше, а тот, который for (int i = 0; i < nfds; i++) {.
     
  5. milo

    milo New Member

    Публикаций:
    0
    Регистрация:
    22 мар 2009
    Сообщения:
    43
    это понятно... просто есть необходимость отправлять данные клиентам (их много) неблокируемым способом (чтобы программа не тормозилась если у клиента плохое соединение) + epoll любит неблокируемые сокеты, поэтому и принял решение использовать их.
     
  6. Aspire

    Aspire New Member

    Публикаций:
    0
    Регистрация:
    19 май 2007
    Сообщения:
    1.028
    непонятно тогда, что непонятно...
     
  7. milo

    milo New Member

    Публикаций:
    0
    Регистрация:
    22 мар 2009
    Сообщения:
    43
    уже все) просто чтобы извлечь данные из сокета надо было несколько раз его опросить, я этого не делал. в том и была моя проблема. и еще вопрос на понимание: асинхронные и неблокируемые сокеты - разные понятия? асинхронные, это когда их select'ом, poll'ом или epoll'ом опрашиваем, а неблокируемые просто не блокируются? путанница с этим возникала некоторое время назад
     
  8. r90

    r90 New Member

    Публикаций:
    0
    Регистрация:
    26 ноя 2005
    Сообщения:
    898
    Неблокируемые -- это когда твоя программа гарантированно не сможет заблокироваться на вводе/выводе с этих сокетов.
    Вообще, я не сталкивался с понятием "асинхронный сокет". Но встречал понятие асинхронный ввод/вывод. Если "асинхронный сокет" ты не придумал сам, то я думаю, что речь идёт о сокете -- участвующем в асинхронном вводе/выводе. И если так, то да, это когда select'ом, poll'ом опрашиваем прежде чем читать (а потом читаем так чтобы не заблокировать выполнение текущего потока), или когда SIGIO ловим (и опять же читаем так, чтобы не заблокироваться), или когда используем aio_* функции, или когда ещё как-нибудь извращаемся, чтобы параллельно читать из многих сокетов.
    epoll'у плевать, будут ли твои сокеты блокирующими или нет. read/write устроены таким образом, что с select/poll/epoll можно и на блокируемых сокетах не блокироваться на вызовах read/write.
     
  9. milo

    milo New Member

    Публикаций:
    0
    Регистрация:
    22 мар 2009
    Сообщения:
    43
    слово "любит" я употребил потому что epoll имеет режим edge-triggered
     
  10. r90

    r90 New Member

    Публикаций:
    0
    Регистрация:
    26 ноя 2005
    Сообщения:
    898
    milo
    "edge-triggered" и "nonblocking file descriptor" -- это перпендикулярные понятия. Оба подхода можно использовать как вместе, так и поодиночке.