@Vermut756

TCP: почему при отключении кабеля образуется «черная дыра», то есть какое-то время Write теряет данные, но ошибку не выдает?

Создаю сервер. В интернете, не в локалке.
Подключение персистентное.

Вот у клиента в сетевом кабеле пропадает контакт.
И как раз в это время клиент собирается отправить сообщение.
Клиент вызывает Write и... всё! Тишина!
Write успешно отрабатывает, никаких экскепшн, но данные теряются, они не отправляются даже если сразу же воткнуть кабель на место. Еще раз: данные - теряются, безвозвратно.
И более того, я опять вызываю Write, и опять то же самое!

Однако, если после вынимания кабеля выждать с пол-минуты и только потом вызывать Write, то экспешн будет.

Почему не сразу?
Почему теряются данные и (оказывается) даже есть термин "black hole", хотя все вокруг трубят о надежности TCP?

Я не знаю, что делать и как правильно.

Я бы мог после этого Write сразу вызывать Read, вот он вроде работает как следует, но дело в том, что по условию задачи я должен на клиенте в фоновом потоке постоянно крутить этот Readв цикле в режиме ожидания, и на сервере тоже цикл, поэтому я не могу использовать Read в других местах - это запутает код, ну если только с Peek, но правильное ли это решение?

Говорят, что для корректной работы поможет heartbeat/keepalive, но я не понимаю каким образом, как не понимаю и суть проблемы. Ну не должен Write терять данные.

P.S. Описанное явление очень странно. Ведь сервер в ответ всегда отправляет пакет с флагом ACK, а если нет кабеля, то этот пакет не придет, так почему же ОС клиента не проверяет пришел ли он, и не выбрасывает экскепшн?
И почему она начинает его выбрасывать при более длительном обрыве кабеля (примерно пол-минуты)?

P.P.S.
ОС - Windows 8.1 x64 клиент, Windows 2000 x64 сервер.
Язык сервера - C#.
Язык клиента - C# либо Delphi 7 (Indy 9), в обоих языках одинаково.
  • Вопрос задан
  • 513 просмотров
Пригласить эксперта
Ответы на вопрос 3
begemot_sun
@begemot_sun
Программист в душе.
В сетевом стеке есть есть буферы на отправку и прием.
Вы пакет отправили, это не значит что вы ждете подтверждение доставки с другого конца.
Вы отправляете следующий и следующий пакеты.
Система копит "такие необходимости подтверждений", и когда от другой стороны они приходят, система удаляет "эту необходимость" из памяти и отправляет дальше.

Т.о. осуществеляется поточная запись в порт, и также поточное подтвержение доставки.

Когда что-то пошло не так, подтвержения перестают ходить. Данные которые вы хотите отправить оседают в буфере отправки. В это время система ждет подтвержения от другой стороны.

Когда вы включаете кабель, буфер отправки - переполнен.
+ система не может отправить другие пакеты, потому что нет подтвержения доставки о предыдущих.
Т.о. если буфер отправки переполнен - то ваши данные теряются.

Система никогда не знает, когда вы включили кабель. Она просто может пославть пакет аля пинг, через некоторое время. В линуксе это время в районе 2х часов по-умолчанию. Т.о. неактивный сокет может жить 2 часа и ничего с ним не случится, вы в него будете писать, а происходить ничего не будет.

P.S. Это не точное моё описание, чисто "описание по понятиям" \m/
Ответ написан
Комментировать
@khrisanfov
Программист
Это решается установкой keepalive в нужное значение (на стороне сервера или клиента). Через указанный интервал отправляется пакет без данных, чтобы проверить живет соединение или нет. Если нет, то сокет закрывается.
Ответ написан
MrJeos
@MrJeos
Вот тут хорошо разобраны подобные ситуации: https://habrahabr.ru/company/mailru/blog/316128/
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы