Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
Форум программистов > C/C++: Сети > POLLHUP и SHUT_RD |
Автор: NightGoblin 8.9.2008, 23:08 | ||||
Даже не знаю с чего начать, но всё же попробую как-то упорядочить многомерную проблему ![]() Общая ситуация: Есть TCP мультиплексор соединений на C. Клиенты сервера посылают запрос и дальше по долговременному соединению получают данные. Основной вопрос: Как, игнорируя данные от клиентов, отследить их "корректное" отключение от сервера (TCP FIN)? О "вываливании", когда связь с клиентом просто потеряна (пропадание электричества, перегрызенный провод, упавший раутер, и т. п.) речи не идёт -- тут нужен другой механизм, и с ним я буду разбираться отдельно. Проблема: Зависит от того, с какой стороны смотреть. С одной стороны, poll(2) не возвращает POLLHUP когда отваливаются соединения. С другой стороны, посылаемые клиентами данные забивают буфер ввода или заставляют сервер читать ненужный мусор. С третьей, если сокет "прикрыть" по shutdown(fd, SHUT_RD), poll с events=POLLIN возвращается сразу, а read(2)/recv(2) читают 0 байт и, естественно, мы получаем все признаки закрытого соединения. Более литературно: Как уже, надеюсь, понятно, сокеты клиентов опрашиваются при помощи poll(2). Проблема в том, что хочется получить запрос от клиента и дальше игнорировать все данные -- тут вроде помогает shutdown на SHUT_RD. Пока всё хорошо. Читаем мануал к poll(2), в котором значится POLLHUP, который якобы возвращается при закрытии соединения. Так вот, он почему-то не возвращается. Гугление данного вопроса привело меня к тому, что разные ОС обрабатывают это событие по-разному: одни возвращают POLLHUP, другие POLLIN, который нужно проверять read(2)/recv(2) на наличие входных данных: если данных нет, то и толку от такого сокета -- тоже. Пока ожидается запрос от клиента, всё хорошо; только вот сокет, закрытый на чтение, заведомо вернёт POLLIN (ибо чтение из такого сокета блокировано не будет). Возвращаемся к прежней проблеме. Как сделать так, чтобы данные, приходящие от клиентов, игнорировались? Пока вижу три варианта -- ловить POLLIN и:
Первый вариант мне не нравится: почему процесс должен вообще дёргаться по поводу данных, приходящих от клиентов, когда они не нужны? Неужели нет способа их просто игнорировать? Второй какой-то "фошысский", хотя на данный момент самый симпатичный. Ну и третий тоже не подходит, потому что если буфер сокета -- пара килобайт, а клиентов -- пара тысяч, совершенно ненужный мусор съест 4 мегабайта памяти! Код: Естественно, весь код программы (хоть она и не дописана пока) приводить не буду, ниже с сокращениями:
Если нужна ещё какая-то информация о коде (или сам код) -- приведу с радостью по первому требованию. sockCtl и sockClient на данном этапе не имеют значения (кроме того, что они находятся в состоянии LISTEN), в дальнейшем их пути разойдутся. Сценарий: Запускаем сервер, телнетимся на нужный порт. Вводим данные -- всё хорошо; тыкаем ^], вводим q -- на стороне сервера видим:
после чего (поскольку read(sock)==0 обрабатывается в mux_read()) сокет захлопывается сервером. Если же порт клиента после первого mux_read() был подвергнут shutdown(SHUT_RD), а соответствующий poll.events установлен в 0, сервер вообще ничего не сообщает и продолжает радостно выдавать по точке в 5 секунд. Да, по идее mux_read() должен возвращать значение, по которому i будет декрементироваться в случае, если сокет был удалён, но это не критично (в крайнем случае, poll() уйдёт на второй круг), и расправа над этой багой запланирована на сегодняшний вечер. Большое спасибо за внимание! |
Автор: GrayCardinal 10.9.2008, 16:45 |
NightGoblin, Если данные идут не по протоколу, имеешь полное право убить нафиг ![]() Если у тебя "строковый" сервер, буфер сокета можно уменьшить байт до 256 ![]() ![]() |
Автор: NightGoblin 10.9.2008, 17:30 |
GrayCardinal, Размер буфера можно установить до приёма соединения через SO_RCVBUF -- но, насколько мне удалось выяснить, после установки соединения этот буфер можно только увеличить (http://docs.hp.com/en/B2355-90136/ch03s01.html). Проблема, собственно, в том, что нужно всё и сразу -- а существующие варианты работают только наполовину ![]() Что конкретно непонятно? Если нужно -- объясню подробнее. А вообще я сам всё больше склоняюсь к варианту "не умеешь вежливо разговаривать по протоколу -- ступай лесом" ![]() |
Автор: MAKCim 10.9.2008, 21:14 |
NightGoblin, сформулируй одним предложением что надо сделать? ![]() |
Автор: MAKCim 10.9.2008, 21:39 | ||
после этого, я так понимаю, сервер не принимает данных от клиента, а может только возвратить результат? |
Автор: MAKCim 10.9.2008, 22:04 | ||
проверить состояние TIME_WAIT на сокете |
Автор: NightGoblin 11.9.2008, 18:14 |
MAKCim, есть ли переносимый способ проверить состояние сокета на TIME_WAIT? В линуксовом tcp(7) есть TCP_INFO, но там написано "This option should not be used in code intended to be portable." Сервер должен работать как минимум на FreeBSD (и, желательно, на Linux, на котором ведётся разработка ![]() |
Автор: MAKCim 11.9.2008, 21:35 | ||
NightGoblin, да, TCP_INFO и POLLRDHUP не переносимы
так тут как бы POLLHUP'а достаточно ведь что просходит когда мы делаем shutdown(SHUT_RD), то клиенту посылается FIN сегмент, он отвечает на него ACK'ом на стороне сервера сокет переходит в состояние FIN_WAIT_2 после close() на сокете на стороне клиента серверу посылается FIN сегмент, сокет на сервере переходит в состояние TIME_WAIT через 2*MSL секунд сокет на сервере переходит в состояние TCP_CLOSE и poll() на нем возвращает POLLHUP так что POLLHUP достаточен для определения момента закрытия соединения |
Автор: NightGoblin 16.9.2008, 17:19 |
MAKCim, в общем, пока что я не пробовал разбираться. Проблема получилась в том, что я написал короткий тест, и там вроде бы всё работало. Потом переписал это под рабочую версию (вроде бы, ничего не поменял) -- и всё, POLLHUP перестал приходить. Пока что сделал poll на POLLIN и отрубаю клиентов при получении новых данных (которые мне не нужны). Тоже не оптимальный способ так делать, но до поры - до времени сойдёт. В ближайшее время покопаюсь, а пока другие проблемы свалились на голову (не относящиеся к теме). |