LINUX.ORG.RU

Вопрос по TCP


0

0

У меня такой вопрос. Есть написанный мной сервер TCP на линуксе. Есть чужая программа-клиент на QNX. Клиент устанавливает TCP соединение с нами. Протокол обмена поддерживает свою собственную проверку работоспособности связи (т.е посылается посылка на которую должен быть ответ в течении 5 секунд). В какой-то момент Х что-то происходит с клиентом (наверное он глухо падает) и он не отвечает. Мой сервер разрывает соединение посредством close(). НО сокет остается в состоянии LAST-ACK. И он находится в таком состоянии (вроде бы) неограниченно долго. Оживший клиент через некоторое время создает новое соединение и ситуация повторяется. В итоге образуется большое количество соединений, находящихся в состоянии LAST-ACK, которые доставляют мне много радости. Кто-нибудь сможет объяснить ЧТО ЧЕРТ ВОЗЬМИ ПРОИСХОДИТ? И как с этим бороться?

> И он находится в таком состоянии (вроде бы) неограниченно долго.

тюнь таймауты.

> В итоге образуется большое количество соединений, находящихся в состоянии LAST-ACK, которые доставляют мне много радости

Много это больше сотни тыщ или меньше? Если меньше то я бы забил. Ну или таймауты поправил.

true_admin ★★★★★
()
Ответ на: комментарий от true_admin

>Много это больше сотни тыщ или меньше? Если меньше то я бы забил. Ну или таймауты поправил.

Тут есть одна проблема. В силу определенных особенностей (требования заказчика)сервер должен был закрыть порт и открыть его снова. Это связано со специфическими схемами резервирования двух серверов. Но я НЕ МОГУ создать новый сервер на определенном порту, если остались соединения от предыдущего сервера, открывшего ранее этот порт. А эти соединения висят неопределенно долго.

pathfinder ★★★★
() автор топика

Читал спецификацию протокола TCP. По моему ребята основательно ганжджубаса дунули, прежде чем писать этот документ. Про номера последовательностей, привязанных к 4 микросекундному 32 битному псевдотаймеру, так это ваще жесть. Похоже в общем случае стабильную работу протокола никто не гарантирует, даже при условии полной исправности обеих сторон обмена. Просто вероятность "неудачного" стечения обстоятельств очень мала. Состояние TIME_WAIT конечного автомата TCP тоже доставляет.

pathfinder ★★★★
() автор топика
Ответ на: комментарий от pathfinder

> В силу определенных особенностей (требования заказчика)сервер должен был закрыть порт и открыть его снова. Но я НЕ МОГУ создать новый сервер на определенном порту, если остались соединения от предыдущего сервера, открывшего ранее этот порт.

Тут скорее всего поможет setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, ...). Должен сказать, заказчик знает толк в ... необычных решениях.

DrLivsy
()
Ответ на: комментарий от pathfinder

> Читал спецификацию протокола TCP. По моему ребята основательно ганжджубаса дунули, прежде чем писать этот документ.

Чувак, TCP прекрасно работает уже больше 25 лет, конечно есть у него свои тонкости, но он прекрасно справляется со своей задачей, а твой чудо-сервер как глючил, так и глючит.

Не надо гнать на TCP, если руки кривые.

vnovouhov
()
Ответ на: комментарий от vnovouhov

>ман setsockopt, SO_REUSEADDR

Обижаете. Конечно я знаю про эту опцию. Она помогает с проблемой состояния TIME_WAIT.

Тут проблема другого рода. Я её решил посредством setsockopt(sock,SOL_SOCKET,SO_LINGER,&l,sizeof(l)). Однако меня терзают сомнения в кошерности данного решения. Неспроста же операционная система удерживает сокет, когда приложение его закрыло.

Незнаю. Не приведет установка этой опции к каким-то другим проблемам.

pathfinder ★★★★
() автор топика
Ответ на: комментарий от vnovouhov

>Чувак, TCP прекрасно работает уже больше 25 лет

Правильно мыслишь. В том вся и проблема, что я считал TCP непогрешимым, правильным и абсолютно корректным протоколом, но который можно положиться. А с ним тоже все не так просто. Что-бы правильно писать сервера надо знать все его "тонкости" и "слабые места". Иначе лажа получится.

pathfinder ★★★★
() автор топика
Ответ на: комментарий от pathfinder

Как бы не соврать то по старой памяти…

Если удаленная сторона пропала, а в буфере сокета что то есть. А вы его нагло пытаетесь закрыть, то оно свято верит что удаленная сторона может появиться, тогда остатки буфера будут отправлены на удаленную стотону.

Попробуйте закрыть сокет с пустым буфером при отрыве удаленной стороны – должен просто закрыться.

Aleks_IZA
()
Ответ на: комментарий от pathfinder

Ещё одна интересная особенность TCP. Кам мы знаем TCP является протоколом, ориентированным на соединение. Можно высказать предположение, что протокол должен обеспечивать контроль работоспособности соединения и в случае пропадания связи разрывать соединение.

Однако при стандартных настройках соединения это не так.

Если было установлено соединение между двумя точками. Прикладной уровень так устроен, что может обходится без постоянного обмена данными. Если один из участников обмена падает (например мы выключили питание компьютера), и при этом не будет необходимости живой стороне отсылать какие-то данные, то соединение не разорвется никогда и сокет будет находится в состоянии ESTABLISHED.

Вывод: В TCP при определенных условиях возможна ситуация, когда реально мертвое соединение будет считаться живым НЕОГРАНИЧЕННО ДОЛГО.

Проблема решается двумя способами: 1. Установка setsockopt(sock,SOL_SOCKET,SO_KEEPALIVE,&b,sizeof(b)). Тогда через несколько часов после прекращения обмена данными TCP всеже додумается проверить работоспособность линии. Некошерно, зато быстро реализуется. 2. Собственоручная проверка работоспособности соединения на прикладном уровне. Кошерно, но требует времени на реализацию

pathfinder ★★★★
() автор топика
Ответ на: комментарий от Aleks_IZA

>Попробуйте закрыть сокет с пустым буфером

Вы не поняли. Сервер уже догадался что связь пропала и ЗАКРЫЛ СОКЕТ. Для приложения этого соединения уже не существует. Файловый дескриптор сокета благополучно освобожден. Просто где-то в потрохах операционной системы сокет ещё занят. И операционная система отказывает в создании нового сервера на определенном порту, пока старые соединения старого сервера не исчезнут.

pathfinder ★★★★
() автор топика
Ответ на: комментарий от Aleks_IZA

>Попробуйте закрыть сокет с пустым буфером

А можно как-то очистить буфер сокета перед закрытием?

pathfinder ★★★★
() автор топика
Ответ на: комментарий от Aleks_IZA

>Попробуйте закрыть сокет с пустым буфером

Да, да все верно. Сокет будет сразу уничтожен если отсылочный буфер будет пустым. Но как очистить этот буфер?

pathfinder ★★★★
() автор топика
Ответ на: комментарий от pathfinder

>Вывод: В TCP при определенных условиях возможна ситуация, когда реально мертвое
Юсоединение будет считаться живым НЕОГРАНИЧЕННО ДОЛГО.

Позвольте с вами не согласиться. Эти таймауты заданы на уровне настройки TCP/IP стека операционной системы (у каждой свои значения). Да эти таймауты довольно большие. Но мне когда то нравилось, при пропадании соединения на пару часов, с восстановлением соединения сессия ssh благополучно восстанавливалась… мелочь а приятно. В далеких 90-ых, на модемах можно было работать без потерь окружения.

>Но как очистить этот буфер?

Не подскажу – сокетами давно не занимался. Возможно никак – ждать…

Вообще не заворачивайтесь сокетами которые не качественно закрыты, это нормальная работа TCP/IP. Пусть админ сам настраивает операционку под свои особенные режимы работы или свое глюканутое окружение.

Aleks_IZA
()
Ответ на: комментарий от Aleks_IZA

>Юсоединение будет считаться живым НЕОГРАНИЧЕННО ДОЛГО. >Позвольте с вами не согласиться

Позвольте в ответ не согласится. Я проверял. Это действительно так. Вот ссылка http://www.rsdn.ru/article/net/keep_alive.xml

pathfinder ★★★★
() автор топика
Ответ на: комментарий от pathfinder

> И операционная система отказывает в создании нового сервера на определенном порту, пока старые соединения старого сервера не исчезнут.

Аааа, чувак, я понял о чём ты пишешь. Это же во всех учебниках сказано, SO_REUSEADDR тебя спасёт.

if ( setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr)) ) {
log_crit("SO_REUSEADDR failed");
goto TEMP_FAIL; }

Сначала, создаёшь сокет, потом делаешь SO_REUSEADDR, потом биндишься.

> Никто не подскажет какие именно таймауты надо настраивать?


sysctl -a | grep tcp

> соединение не разорвется никогда и сокет будет находится в состоянии ESTABLISHED.


По дефолту через 5 дней закроетсся, смотри сиськтльки. Чтобы такой байды не было ставишь keep alive сиськтлькой, крутишь таймауты и всё будет зашибись.

true_admin ★★★★★
()
Ответ на: комментарий от true_admin

>Аааа, чувак, я понял о чём ты пишешь. Это же во всех учебниках сказано, SO_REUSEADDR тебя спасёт.

Повторяю ещё раз SO_REUSEADDR в моем случае не спасет. Эта опция решает совершенно другую проблему. В моем случае слушающий сокет сервера и так освобожден. Неосвобожденными остаются сокеты соединений старого сервера.

>sysctl -a | grep tcp

Согласен. Я тоже думаю что копать надо в этом направлении. Но мне так и не удалось выяснить где настраивается интервал ожидания ответа в состоянии LAST_ACK.

pathfinder ★★★★
() автор топика
Ответ на: комментарий от pathfinder

> Повторяю ещё раз SO_REUSEADDR в моем случае не спасет.

сорри, запутался.

> Но мне так и не удалось выяснить где настраивается интервал ожидания ответа в состоянии LAST_ACK.

on, вроде, равен timewait, но это не точно. Ещё попробуй net.ipv4.tcp_fin_timeout и net.ipv4.tcp_tw_reuse.

Ты точно уверен что у тебя висят сокеты в LAST_ACK? Сколько ты ждал их исчезновения?

true_admin ★★★★★
()

>и (т.е посылается посылка на которую должен быть ответ в течении 5
>секунд)



А ты по истечении 5-и секунд, сразу закрываешь сокет?

А если подождать пока сокет у падет в TIME_WITE и пробудет там икс минут (зависит от платформы).

PS: Инет закрыт, надо бы гайд по сокетам открыть.

Aleks_IZA
()
Ответ на: комментарий от Aleks_IZA

Вроде народ рекомендует юзать SO_LINGER - чтоб разорвать соединение насильно, вместо ожидания нормального закрытия соединения.

PS: Все параметры TCP стека сильно завист от платформ и версий операционок.

Aleks_IZA
()

Ты какой сокет закрываешь клиентский или серверный? Да, состояние TIME_WAIT висит до 15 минут в зависимости от системы зачем оно нужно написано у Стивенса. В чем проблема не понимаю, номер клиентского сокета всегда меняется. Если ты делаешь close() раз в 5 секунд, то 65535 сокетов ты исчерпаешь за 91 час, а это намного больше чем время в течении которого висит TIME_WAIT.

Reset ★★★★★
()
Ответ на: комментарий от Valeriy_Onuchin

man shutdown, man стивенс раздел 6.6. TIME_WAIT после shutdown остается.

Reset ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.