Есть клиент-серверное приложение. С одной стороны выполняется Socket.Send, с другой — Socket.Receive. Всё это обёрнуто в NetworkStream с кучей обёрток поверх, но это уже не важно.
В один прекрасный момент пропадает связь между клиентом и сервером. В этот момент отправитель отваливается на Socket.Send и закрывает коннекцию — информация о закрытии коннекции теряется. По этой причине получатель ничего не знает о том, что данных не будет и продолжает висеть.
Спустя пару секунд сеть восстанавливается. Спустя ещё минуту начинают ходить кипалайвы. Но поскольку сеть и сокеты с обеих сторон живы, они успешно сигнализируют о том, что сетка жива.
Проблема в том, что в протокол не заложена информация об изменении состояния запроса и отсутствует подтверждение доставки. Чтобы не ломать обратную совместимость и избавить себя от необходимости вкручивать различные таймауты на каждую операцию и механизмы их анализа для ретраев, хочется заиметь способ ручного управления кипалайвами. Раз в минуту поток из пула посылает в канал KeepAlive'а пакет. Если за 10 минут по каналу KeepAlive'а не пришло ни одного пакета, соединение рубится.
И вот тут встаёт вопрос — а как получить этот второй канал передачи? Out-of-band data вычеркиваем — любой роутер без поддержки разломает столь изящное решение. Windows 95 вообще уходила от них в синьку. Не знаю, изменилось ли что-нибудь в лучшую сторону с тех пор. Что ещё можно сделать? Как получить второй, независимый канал передачи данных, имея на руках только подключённый Socket?
Повторное подключение не подходит — переделок будет столько, что проще сразу сделать хорошо. Сразу делать хорошо сейчас не хочется. Ищу адекватный костыль.
LW>В один прекрасный момент пропадает связь между клиентом и сервером. В этот момент отправитель отваливается на Socket.Send и закрывает коннекцию — информация о закрытии коннекции теряется. По этой причине получатель ничего не знает о том, что данных не будет и продолжает висеть.
Здравствуйте, LWhisper, Вы писали:
LW>В один прекрасный момент пропадает связь между клиентом и сервером. В этот момент отправитель отваливается на Socket.Send и закрывает коннекцию — информация о закрытии коннекции теряется. По этой причине получатель ничего не знает о том, что данных не будет и продолжает висеть.
Не совсем понятно, у Вас остается висеть recv на получателе? Если send отвалился, это еще не значит, что соединение оборвалось. Таймаут у TCP достаточно большой же.
LW>Спустя пару секунд сеть восстанавливается. Спустя ещё минуту начинают ходить кипалайвы. Но поскольку сеть и сокеты с обеих сторон живы, они успешно сигнализируют о том, что сетка жива.
Как сокет на отправителе жив, если Вы написали, что соединение закрывается?
LW>Проблема в том, что в протокол не заложена информация об изменении состояния запроса и отсутствует подтверждение доставки. Чтобы не ломать обратную совместимость и избавить себя от необходимости вкручивать различные таймауты на каждую операцию и механизмы их анализа для ретраев, хочется заиметь способ ручного управления кипалайвами. Раз в минуту поток из пула посылает в канал KeepAlive'а пакет. Если за 10 минут по каналу KeepAlive'а не пришло ни одного пакета, соединение рубится.
А почему нельзя тупо сделать тогда единый таймаут на получение данных? Если нет посылок, то рубить соединение?
LW>Повторное подключение не подходит — переделок будет столько, что проще сразу сделать хорошо. Сразу делать хорошо сейчас не хочется. Ищу адекватный костыль.
Почему не подходит? Открыть повторное подключение в независимом модуле/классе с событием — соединение потеряно. Если что не так — соединение перезапускается. В основную программу в трек отправления/получения данных встраивается один if и все.
Здравствуйте, LWhisper, Вы писали:
LW>Коллеги, доброго дня.
LW>Проблема в том, что в протокол не заложена информация об изменении состояния запроса и отсутствует подтверждение доставки.
Тут надо знать протокол, чтобы что-то предложить. Когда я выдумываю свои протоколы, они всегда вида Type-Length-Data (ну и я этот принцип слизал у ICQ в своё время, так что наверное все так делают). В таком случае можно просто добавить новый тип пакета.
Если такого в протокол не заложено, можно сделать VirtualStream (подменив им изначальный NetworkStream), который будет пересылать данные в виде Type-Length-Data и тем самым эмулировать второй канал передачи данных — грубо говоря текущий поток данных будет пересылаться с Type=1, а второй канал с Type=2.
всю ночь не ем, весь день не сплю — устаю
Re: Socket KeepAlive или формирование второго канала передачи данных.
Кстати, вот эти два предложения не очень между собой стыкуются: LW>...В этот момент отправитель отваливается на Socket.Send и закрывает коннекцию — информация о закрытии коннекции теряется... LW>...Но поскольку сеть и сокеты с обеих сторон живы, они успешно сигнализируют о том, что сетка жива...
с одной стороны сокет должен ведь мёртвым, нет?
когда я свой мессенждер на сокетах писал (в 2003-ем) у меня как раз не было проблемы, если при отправке обнаруживалось отсутствие связи. Проблема была в случае, если ничего не слалось, а сеть падала — от винды не было никакого сигнала об этом и обе стороны думали, что соединение живо.
Чаще всего клиент при последующей отправке фейлился и пытался переподключиться. А вот сервер по-моему пребывал в состоянии "связь жива" до следующего соединения со стороны клиента. Ну и да — решалось контрольными пакетами.