Итак, приступил к написанию. Читать из сокета в таком случае стоит до тех пор, пока мы не получим EAGAIN. Хорошо, вроде все понятно, написал следующий код:
do { n = recv(sock, temp, sizeof(temp), 0); } while (n < 0 && errno == EAGAIN);
До поры до времени решение работало нормально, но, когда появились клиенты с медленным (я тогда еще не знал, что соединение у них медленное), то этот цикл становился уж больно долгим и я не мог дождаться ответа... В голову пришло два решения:
1. Не выпендриваться, поставить свою самооценку на место и сделать все в LT через обычные блокируемые сокеты;Ладно, выбрал первый вариант. Но, через какое-то время опять столкнулся с аналогичной ситуацией - клиент вроде как подключился, но поток все равно блокировался на довольно большой промежуток времени. Хм... Взял в руки wireshark и начал смотреть какие пакеты ко мне приходят в надежде выяснить причину. Беру клиента, подключаюсь наблюдаю за пакетами: syn, syn-ack, ack, пауза ... и ... у меня в голове загорается лампочка! Ну конечно же! epoll_wait срабатывает как только проходит трехэтапное квитирование (handshaking)! Возвращаюсь к первоначальному варианту (неблокируемые сокеты в ЕТ) и повторяю процедуру заново. Вот оно! Как только tcp-соединение физически установлено начинает работать этот цикл и крутится до тех пор, пока не получит хотя бы первую порцию данных (а потом опять крутится, пока не получит все данные), а не получать он ее может довольно долго, если у клиента медленное соединение (c gprs вобще мрак).
2. Вставить счетчик цикла и при достижении какого-то значения посылать клиента подальше.
В итоге пришла в голову идея - если использовать второе решение (со счетчиком цикла), то по достижении некоего порога клиентов с медленным соединением можно отбрасывать (если у вас сервер обрабатывает подключения в один поток, а из-за медленных клиентов вам не хочется задерживать отсальных). Однако тут есть и минус - мы получаем активное ожидание, которое, при достаточно высокой нагрузке может положить сервер любой мощности (были прецеденты на прошлой работе).
Традиционная часть, которую всегда пишут в начале - для чего писал статью? Во-первых, поделиться опытом с коллегами, если тем самым ответил на чей-то вопрос или помог понять что-то более глубоко, то я достиг своей цели. Во-вторых, данная статья может послужить поводом для дискуссии и, что будет просто замечательно, целью для критики со стороны более опытных коллег, ведь ничто не учит нас жить так хорошо, как хорошая порка. Спасибо, что не ушли с этой страницы сразу ;)