@Travious

Как определить, что начался новый пакет и закончился предыдущий?

Допустим, клиент отправляет что-то серверу, а сервер читает в цикле while или асинхронно (BeginRead/EndRead).
И вот 2 разные сообщения от клиента проходят без всякого промежутка.
То есть со стороны клиента это выглядит примерно так:
var tcp = new TcpClient(...);
...
var sr = tcp.GetStream();
sr.Write("{'json_example': 'Apple'}"); //первое сообщение
sr.Write("{'json_example': 'BananaAvocado'}"); //и прямо тотчас же второе


Собственно вопрос: как серверу определить, что закончилось одно сообщение и началось второе?

Решение пришло само собой: перед отправкой самих данных сперва отправлять длину этих самых данных, чтобы сервер знал сколько ему прочитать байт и после скольки байт начнется новый пакет.
Или еще проще: просто ввести фиксированный размер пакетов, скажем 1024 байта.

Но я, как обычно, всем недоволен и опять в своем решении вижу костыль.
Есть ли какой-то способ избежать этого "костыля"? Может в .NET уже есть готовая реализация такого класса? Или все нормально?
  • Вопрос задан
  • 495 просмотров
Пригласить эксперта
Ответы на вопрос 4
@Mercury13
Программист на «си с крестами» и не только
Протокол TCP считает, что данные идут непрерывным потоком; всё, что не пришло, переспросили, всё, что пришло вне очереди, поставили на место.

Единственный способ — наладить в этом потоке данных искусственные границы пакета. Например:

opcode : word
packetLength : dword
data : byte[packetLength]
Ответ написан
Комментировать
Как Mercury13 уже отметил, TCP даёт вам "виртуальный провод", где уже нет никаких пакетов, и в этом его (TCP) удобство.
В прикладных протоколах поверх TCP всегда так или иначе определяется способ передачи сообщений известной длины, либо же способ определить конец текущего сообщения и начало следующего. Ваш метод, а именно
Решение пришло само собой: перед отправкой самих данных сперва отправлять длину этих самых данных, чтобы сервер знал сколько ему прочитать байт и после скольки байт начнется новый пакет.

является более чем стандартным для двоичных протоколов, и нисколько не костыль. Другого надёжного способа для двоичных данных и не придумаешь (именно поэтому, кстати, нуль-терминатор в Сишных строках - не очень хорошая идея в принципе).
Для текстовых или смешанных протоколов могут использоваться другие подходы. Например, в том же HTTP в простейшем случае заголовки читаются до первого двойного \r\n, а затем читается столько байт, сколько указано в Content-Length.

В вашем случае, если вы передаёте JSON, который всё равно будет подвергаться синтаксическому разбору (т.к. JSON протокол текстовый, а не двоичный, и начало-конец сообщения в нём определяется синтаксисом, а не заранее переданной длиной), то вы можете воспользоваться одним из соглашений JSON Streaming. Более удобным и безопасным является Concatenated JSON, однако тогда используемый вами парсер должен быть достаточно умён, чтобы вовремя оставиться при чтении значения из потока. Тогда вы сможете просто читать подряд из потока все переданные объекты.

P.S. Уже не уверен, что у вас JSON, т.к. у вас одинарная кавычка в сообщении.
Ответ написан
Комментировать
Если вы передаете данные поверх HTTP, используйте chunked encoding и передавайте данные HTTP-чанками, они используют ровно такую структуру (передает размер чанка, затем сам чанк).
Ответ написан
Комментировать
В .net есть такие штуки как BinaryReader и BinaryWriter. Если отправить во writer строку на одном конце, то на другом конце reader будет ждать, пока не считает всю строку целиком. Если будет несколько строк, то они считаются последовательно.
Ответ написан
Комментировать
Ваш ответ на вопрос

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

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