TCP/IP: что делать, если в процессе чтения сервером пакета от клиента пропадает интернет?

TCP/IP: что делать, если в процессе чтения сервером пакета от клиента пропадает интернет?

Ситуация проста как 3 копейки.
Код на клиенте:
...
var bytes = new byte[30000000];
...
server.Write(bytes, 0, bytes.Length); //шлём пакет серверу
...

Код на сервере:
...
var client = listener.AcceptTcpClient();
...
var bytes = new byte[30000000];
client.Read(bytes, 0, bytes.Length); //читаем пакет от клиента
...


И вот, если на клиенте элементарно пропадет сеть, причем уже тогда, когда клиент начнет писать пакет, а сервер - читать, то ни на клиенте, ни на сервере не будет выброшено никакое исключение, а ведь массив bytes у сервера будет недозаполнен, с какого-то места в массиве будут нули, то есть пакет поврежден, а сервер спокойно продолжит работу, попытается обработать этот массив (распарсить JSON), и упадет.

И, что еще хуже: то же самое (наверно) случится и с клиентом, если наоборот клиент начнет читать ответ от сервера, и в этот момент пропадет связь.

Как решить проблему?
Надо с передающей стороны (клиента) как-то узнать, что сервер получил что-то не то, и отправить повторно в надежде что на этот раз передача пройдет успешно, и так по кругу в бесконечном цикле с задержкой.
Но, наверно, нельзя просто так узнать передались ли данные серверу. Не приходит в голову ничего лучше, чем сделать так, чтобы сервер обрабатывал такие поврежденные пакеты и возвращал клиенту ответ с успехом или ошибкой, но тогда на каждый пакет придется делать ответ... И еще: а вдруг поврежденным окажется пакет с ответом?)) Значит, клиент должен на этот ответ тоже давать ответ... А если и это ответ поврежден будет, а если следующий, а если следующий, замкнутый круг из ответов...

А еще у меня же keep-alive'ы есть, кастомной реализации... Что при этом произойдет с ними, и нельзя ли их как-то использовать для этой проверки...

Короче, запутался. Это бородатое поделие TCP - реальный вынос мозга. Врагу не пожелаешь.

P.S.
Конечно, на самом деле размеры пакетов у меня не такие огромные, я нарочно взял почти что предельно большой размер, для удобства имитации разрыва связи (выдергиваю сетевую кабель из компа).
Но будь пакет хоть 2 байта, все равно - вероятность есть, и ее надо учесть.
  • Вопрос задан
  • 1540 просмотров
Пригласить эксперта
Ответы на вопрос 5
Smithson
@Smithson
20+ лет админю
Во-первых, давайте отделим мух от котлет. Пакеты в TCP, у вас - буфер.
Во-вторых, TCP гарантирует доставку пакета (в заданном вами при отправке порядке). Пока пакет не будет передан целиком и полностью, стек TCP/IP будет его повторять. Алгоритм протокола такой. Однако, бывают обстоятельства сильнее TCP (пропадание коннекта из этой серии) и любая программа должна закладываться на то, что данные могут не дойти или дойти не полностью.
В-третьих, если у вас пришли не все данные в буфер, которые вы ждёте, то надо либо передавать контрольную сумму (что делать, если не [вся] пришла она?), либо, что правильнее, передавать не плоский буфер, а некую структуру данных, у которой вы можете проверить, что значения полей изменились (были заполнены при приёме данных) по сравнению с начальными. И исходя из этого решить, что структура пришла вся (последнее поле, например - контрольная сумма, по-умолчанию - 0) или её надо запрашивать заново.

А вы как хотели? Как в сказке? Как в сказке можно писать, если ваша программа 101% работает только и исключительно в локальной сети. В этом случае отрыв коннекта или сегмента - аварийная ситуация и не вам её исправлять.
Ответ написан
Комментировать
@res2001
Developer, ex-admin
В общем целиком поддерживаю Smithson и Олег Цилюрик
Проверяйте коды ошибок, возвращаемые методами передачи/приема. При работе с сетью всегда нужно закладываться на то, что передача или прием пройдет с ошибкой или будут приняты/переданы не все данные.
Т.е. фактически вы должны писать программу исходя из того что ошибки приема/передачи не то что возможны, а они точно будут всегда.
И да, кастомные keep-alive для TCP - это полная фигня - в протоколе уже все реализовано.
Ответ написан
@koronabora
Человек
Надо либо самому писать при отправке номер сообщения и размер. Раз это TCP, то клиент будет отправлять нечетные, получать четные номера, сервер получать нечетные, а отправлять четные. Если вдруг разрыв или еще что - ждем своего пакета. Если пошли пакет пропущен, просим прислать свой номер. Тоесть необходимо также реализовать своеобразные служебные запросы.

Обычно используют уже готовые NetworkManager'ы.
Ответ написан
Olej
@Olej
инженер, программист, преподаватель
Короче, запутался. Это бородатое поделие TCP - реальный вынос мозга. Врагу не пожелаешь.

Ага...

причем уже тогда, когда клиент начнет писать пакет

... особенно когда не понимаешь, что TCP - потоковый протокол надёжной передачи, что там (с точки зрения кода, сокета) нет и быть не может никаких пакетов, и начинаешь изобретать собственное видение действительности и строить влосипеды из keep-alive'ы в "кастомной реализации"... ;-)

Как решить проблему?

Брать книги У.Стивенса по работе TCP/IP и разработке сетевых приложений, и читать, читать, читать ... до просветления.
Ответ написан
@Melz
Если я правильно понял проблему, то или делать Heartbeat (модное название велосипеда) или ждать пока сервер затаймаутится через неопределенный период (ну там часик).

Классика жанра по этому линку. Внимательно читать раздел "как не надо".
Detection of Half-Open (Dropped) Connections
Ответ написан
Комментировать
Ваш ответ на вопрос

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

Войти через центр авторизации
Похожие вопросы