Я должен вручную встроить информацию о размере данных в передачу TCP?

Предположите, что Вы и я отправляем, довольно длинное предложение (скажите, 1 024 000 байтов) через TCP.

Если Вы пишете 1 024 000-байтовое предложение мне, Вы на самом деле используете NetworkStream для записи тех байтов в.

Когда я получаю, я должен знать заранее размер предложения, которое Вы отправили?

В противном случае, как я могу проверить, когда я должен остановить stream.read?

Если да, программа должна иметь средства, которые встраивают размер данных в главу данных? Таким образом, я получаю 4 байта сначала для наблюдения, сколько общего количества я должен считать?

.NET имеет что-нибудь для автоматического встраивания размера данных в передачу?

7
задан Mike Dinescu 16 February 2010 в 14:45
поделиться

9 ответов

Ни в .NET, ни в протокол TCP нет ничего встроенного, чтобы заранее определить размер сообщения.Протокол TCP только указывает, что все данные будут переданы в принимающую конечную точку (или, по крайней мере, для этого будут приложены все усилия).

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

EDIT

Это началось как комментарий, но это нечто большее, чем соответствует этому пределу.

К добавить NULL к потоку просто означает добавление символа, имеющего двоичное значение 0 (не путать с символом 0 ). В зависимости от кодировки, которую вы используете для передачи (например, ASCII, UTF-8, UTF-16 и т. Д.), Которая может переводиться в отправку одного или нескольких байтов 0, но если вы используете соответствующий перевод, вам просто нужно указать что-то вроде \ 0 в вашей строке. Вот пример:

string textToSend = "This is a NULL Terminated text\0";
byte[] bufferToSend = Encoding.UTF8Encoding.GetBytes(textToSend);

Конечно, все вышеперечисленное предполагает, что все остальные данные, которые вы отправляете, не содержат никаких других значений NULL. Это означает, что это текст, а не произвольные двоичные данные (например, содержимое файла). Это очень важно! В противном случае вы не сможете использовать NULL как терминатор сообщения, и вам придется придумать другую схему.

4
ответ дан 7 December 2019 в 01:20
поделиться

Вообще говоря, лучше использовать заголовок с размером данных, чем символ завершения. Метод завершающего символа восприимчив к атаке отказа в обслуживании. Я могу просто продолжать отправлять данные в вашу службу, и пока я не включу терминатор, вам нужно продолжать обработку (и, возможно, выделять память), пока не произойдет сбой.

Используя заголовок, содержащий общий размер, если передача слишком велика для вас, вы можете проигнорировать ее или отправить обратно ошибку. Если злоумышленник попытается отправить больше данных, чем указано в заголовке, вы заметите поврежденный заголовок в начале следующего потока и проигнорируете его.

2
ответ дан 7 December 2019 в 01:20
поделиться

Суть в том, что с TCP не существует соответствия между количеством и размером записи сокета на стороне передачи с количеством / размером считывания сокета на сторона приемника.

Если поток данных имеет какую-то структуру, вам придется добавить какие-то метаданные / данные оболочки вокруг полезной нагрузки.

Каждый раз, когда мне приходилось решать эту проблему, я использовал некоторую комбинацию из:

a) использовать магическое число, чтобы указать начало или конец вашего сообщения данных (или обоих)

b) использовать контрольную сумму в конец сообщения для проверки правильности содержимого (я знаю, что TCP выполняет проверку ошибок и повторную передачу, но контрольная сумма полезна в случае, когда получатель обнаруживает случайное появление начального / конечного магического числа / последовательности в потоке)

c) использовать поле длины после начального магического числа (при условии, что передающая сторона знает длину данных до того, как начнется передача ).

Но прежде чем идти, посмотрите, какой более высокий уровень библиотеки протоколов реализованы для используемого вами языка / платформы. NetworkStream? это тот Windows API / MFC или что-то в этом роде.

Например, мне недавно пришлось установить систему клиент / сервер. Функциональность клиента и сервера уже была написана на python, поэтому простое использование python xmlrpclib / server упростило объединение двух программ вместе - буквально скопируйте пример, и я закончил за 30 минут. Если бы я сам написал какой-нибудь выдуманный протокол прямо на tcp, это заняло бы 5 дней!

1
ответ дан 7 December 2019 в 01:20
поделиться

Поскольку TCP является надежным протоколом, вы можете либо структурировать свой протокол, чтобы указать количество приходящих байтов, либо использовать какой-то терминатор для обозначения конца передачи. Если бы вы использовали UDP, надежность которого не гарантируется, было бы гораздо важнее либо создать протокол, который выдержит отброшенные байты, либо указать, сколько байтов ожидается (и иметь механизм повторной передачи), поскольку пакет, содержащий завершение может быть потеряно. Максимальное время передачи данных и таймауты также могут быть полезны, но только если вы можете определить разумный максимум.

0
ответ дан 7 December 2019 в 01:20
поделиться

Я бы ответил отрицательно. Особенно для больших наборов данных. Причина в том, что отправка размера сначала увеличивает задержку в вашей системе.

Если вы хотите сначала отправить размер, вам нужно вычислить весь ответ, прежде чем начинать его отправку.

С другой стороны, если вы используете маркер завершения, вы можете начать отправку первых битов данных, как только они будут готовы, при вычислении следующих данных.

0
ответ дан 7 December 2019 в 01:20
поделиться

Когда я получаю, должен ли я заранее знать размер предложения, которое вы отправили?

Это может быть полезно (для таких вещей, как рендеринг прогрессбаров), но это не обязательно.

Если нет, то как я могу проверить, когда мне следует остановить stream.read?

Это определяется содержимым вашего потока. Например, многие сообщения кодируют некоторую информацию, которая говорит о том, что это сообщение закончилось (например, нулевой байт для представления конца строки, или для представления конца HTML-документа).

1
ответ дан 7 December 2019 в 01:20
поделиться

Если вы знаете или можете легко узнать общую длину сообщения, предлагаю передать его заранее. Если это невозможно или очень дорого определить, вы можете использовать что-то подобное кодировке фрагментированной передачи в HTTP.

1
ответ дан 7 December 2019 в 01:20
поделиться

Это можно сделать двумя способами: один - описанным вами способом - поместить размер сообщения в заголовок - и другой - указать завершающего маркера на потоке. Например, если ваше сообщение гарантированно не содержит встроенных символов NUL , вы можете завершить его с помощью NUL .

1
ответ дан 7 December 2019 в 01:20
поделиться

Возможно, вы также захотите изучить классы BinaryReader/BinaryWriter, которые можно обернуть вокруг любого потока, TCP или иного.

Они поддерживают, среди прочих функций, чтение/запись строк (в кодировке по вашему выбору), заботясь при этом о включении длины строки.

0
ответ дан 7 December 2019 в 01:20
поделиться
Другие вопросы по тегам:

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