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), в обоих языках одинаково.
В сетевом стеке есть есть буферы на отправку и прием.
Вы пакет отправили, это не значит что вы ждете подтверждение доставки с другого конца.
Вы отправляете следующий и следующий пакеты.
Система копит "такие необходимости подтверждений", и когда от другой стороны они приходят, система удаляет "эту необходимость" из памяти и отправляет дальше.
Т.о. осуществеляется поточная запись в порт, и также поточное подтвержение доставки.
Когда что-то пошло не так, подтвержения перестают ходить. Данные которые вы хотите отправить оседают в буфере отправки. В это время система ждет подтвержения от другой стороны.
Когда вы включаете кабель, буфер отправки - переполнен.
+ система не может отправить другие пакеты, потому что нет подтвержения доставки о предыдущих.
Т.о. если буфер отправки переполнен - то ваши данные теряются.
Система никогда не знает, когда вы включили кабель. Она просто может пославть пакет аля пинг, через некоторое время. В линуксе это время в районе 2х часов по-умолчанию. Т.о. неактивный сокет может жить 2 часа и ничего с ним не случится, вы в него будете писать, а происходить ничего не будет.
P.S. Это не точное моё описание, чисто "описание по понятиям" \m/
Это решается установкой keepalive в нужное значение (на стороне сервера или клиента). Через указанный интервал отправляется пакет без данных, чтобы проверить живет соединение или нет. Если нет, то сокет закрывается.
keepalive позволяет установить время через которое соединение будет сброшено как мертвое.
"почему она начинает его выбрасывать при более длительном обрыве кабеля (примерно пол-минуты)?"
KeepAlive задан как 30 секунд. =)
Через указанный интервал отправляется пакет без данных, чтобы проверить живет соединение или нет.
Дело в том, что мне желательно отказаться от keepalive, вместо него реализовать heartbeat. Но почему отправка пакета с данными не дает такого эффекта? Я снова и снова вызываю Write, но никаких экскепшнов, пока не пройдут эти 30 сек.