Как обнаружить, когда будет полностью получено буферное сообщение протокола?

StackOverflow https://stackoverflow.com/questions/586819

Вопрос

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

По сути, я понял, что для эффективного использования c#beginreceive () в больших сообщениях мне нужно сначала (а) прочитать длину пакета, а затем прочитать именно столько байтов, либо (б) использовать разделитель в конце упаковки. У меня вопрос, есть ли любой из этих присутствующих в буферах протокола? Я еще не использовал их, но, проходя документацию, не кажется, что есть заголовок длины или разделитель.

Если нет, то что мне делать? Должен ли я просто создать сообщение, а затем префикс/суффикс его с помощью заголовка длины/разделителя EOP?

Это было полезно?

Решение

Вы должны включить размер или конечный маркер в свой протокол. Ничто не встроено в гнезда на основе потока (TCP/IP), кроме поддержки неопределенного потока октетов, произвольно разбитых на отдельные пакеты (и пакеты также могут быть пролиты в транзите).

Простой подход будет для каждого «сообщения», чтобы иметь заголовок фиксированного размера, включать как версию протокола, так и размер полезной нагрузки, так и любые другие фиксированные данные. Затем содержимое сообщения (полезная нагрузка).

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

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

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

Другие советы

Извиняюсь за то, что приехал поздно на вечеринку. Я автор Protobuf-Net, одной из реализаций C#. Для использования сети вы должны рассмотреть методы «[de] serializewithlengthprefix» - таким образом, он автоматически обрабатывает длины для вас. Есть примеры в источнике.

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

Я согласен с Мэттом, что заголовок лучше, чем нижний колонтитул для буферов протокола, по основной причине, что, поскольку PB является бинарным протоколом, проблематично придумать нижний колонтитул, который также не будет действительной последовательности сообщений. Многие протоколы на основе нижнего колонтитула (обычно EOL) работают, потому что содержимое сообщения находится в определенном диапазоне (обычно 0x20 - 0x7f ASCII).

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

Для согласованности вы всегда можете определить свое сообщение как сообщение Pb с тремя полями: фиксированная-в качестве длины, перечисление в виде типа и последовательность байта, которая содержит фактические данные. Это обеспечивает прозрачный протокол всего вашего сетевого протокола.

TCP/IP, а также UDP, пакеты включают в себя некоторую ссылку на их размер. А IP -заголовок Содержит 16-битное поле, которое указывает длину заголовка IP а также Данные в байтах. А Заголовок TCP Содержит 4-битное поле, которое указывает размер заголовка TCP 32-разрядными словами. А UDP заголовок Содержит 16-битное поле, которое указывает длину заголовка UDP а также Данные в байтах.

Вот в чем дело.

Используя стандартные зарядные розетки в Windows, независимо от того, используете ли вы пространство имен System.net.sockets в C# или нативном материале Winsock в Win32, вы никогда не видите заголовки IP/TCP/UDP. Эти заголовки разряжаются так, чтобы то, что вы получаете, когда читаете сокет, является фактической полезной нагрузкой, то есть данных, которые были отправлены.

Типичный шаблон от всего, что я когда-либо видел и делал с помощью сокетов, заключается в том, что вы определяете заголовок на уровне приложения, который предшествует данным, которые вы хотите отправить. Как минимум, этот заголовок должен включать размер данных для последующих. Это позволит вам прочитать каждое «сообщение» в полном объеме, не догадаясь о его размере. Вы можете стать настолько причудливым, насколько хотите, например, синхронизации, CRCS, версии, типа сообщения и т. Д., Но размер «сообщения» - это все, что вы В самом деле необходимость.

И за то, что это стоит, я бы предложил использовать заголовок вместо разделителя в конце пакета. Я не уверен, есть ли значительный недостаток для разделителя EOP, но заголовок - это подход, используемый большинством IP -протоколов, которые я видел. Кроме того, мне кажется более интуитивно понятным обработать сообщение с самого начала, а не ждать, пока какой -то шаблон появится в моем потоке, чтобы указать, что мое сообщение завершено.

РЕДАКТИРОВАТЬ: Я только что узнал о проекте буферов Google Protocol. Из того, что я могу сказать, это бинарная схема сериализации/де-сериализации для WCF (я уверен, что это грубое упрощение). Если вы используете WCF, вам не нужно беспокоиться о размере отправляемых сообщений, потому что сантехника WCF позаботится об этом за кулисами, поэтому, вероятно, вы не нашли ничего, связанного с длиной сообщения в протоколе Буферы документации. Однако в случае сокетов знание размера очень поможет, как обсуждалось выше. Я предполагаю, что вы будете сериализовать свои данные, используя буферы протокола, а затем затягивают любой заголовок приложения, который вы придумали перед отправкой. На стороне приема вы снимаете заголовок, а затем де-сетериализуйте оставшуюся часть сообщения.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top