Структура данных пакета? - PullRequest
       33

Структура данных пакета?

4 голосов
/ 27 декабря 2008

Я проектирую игровой сервер и никогда раньше такого не делал. Мне просто интересно, какая хорошая структура для пакета будет с точки зрения данных? Я использую TCP, если это имеет значение. Вот пример и то, что я собирался использовать сейчас:

(каждое значение в скобках является байтом)

[Packet length][Action ID][Number of Parameters]
[Parameter 1 data length as int][Parameter 1 data type][Parameter 1 data (multi byte)]
[Parameter 2 data length as int][Parameter 2 data type][Parameter 2 data (multi byte)]
[Parameter n data length as int][Parameter n data type][Parameter n data (multi byte)]

Как я уже сказал, я действительно никогда не делал ничего подобного раньше, поэтому то, что у меня есть выше, может быть полным быком, вот почему я спрашиваю;) Кроме того, нужно ли передавать общую длину пакета?

Ответы [ 5 ]

3 голосов
/ 27 декабря 2008

Чтобы избежать повторного изобретения колеса, любой протокол сериализации будет работать для проводных данных (например, XML, JSON), и вы могли бы рассмотреть поиск BEEP для основы базового протокола.

BEEP хорошо описан в документе FAQ как « альбом« лучших хитов »трюков, используемых опытными дизайнерами прикладных протоколов с начала 80-х годов. '

3 голосов
/ 27 декабря 2008

Начните с рассмотрения гораздо более простой базовой оболочки: Tag, Length, Value (TLV). Ваш основной пакет будет выглядеть так:

[Tag] [Length] [Value]

Тег - это идентификатор пакета (например, идентификатор вашего действия).

Длина - длина пакета. Это может понадобиться, чтобы узнать, есть ли у вас полный пакет. Это также позволит вам выяснить, какова длина значения.

Значение содержит фактические данные. Формат этого может быть любым.

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

Как уже говорили другие, я бы сначала поставил идентификатор пакета (Tag). Если у вас нет кроссплатформенных проблем, я бы подумал обернуть сериализованный объект вашего приложения в TLV и отправить его по проводам вот так. Если вы допустили ошибку или хотите изменить позже, вы всегда можете создать новый тег с другой структурой.

См. Википедию для более подробной информации о TLV .

3 голосов
/ 27 декабря 2008

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

В целом, я согласен с Brazzy, механизм сериализации, предоставляемый языком, предпочтительнее любого самодельного.

Кроме этого (я думаю, что вы используете язык C-ish без сериализации), я бы поставил идентификатор пакета в качестве первых данных в структуре данных пакета. ИМХО, это своего рода соглашение, потому что первый элемент данных структуры всегда находится в позиции 0, и любая структура может быть уменьшена до этого, идентифицируя в противном случае анонимные данные.

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

Endiannes является фактором, особенно в C-подобных языках. Обязательно уточните, что пакеты всегда имеют одинаковый порядковый номер или что вы можете определить другой порядковый номер на основе сигнатуры или чего-то еще. Очень странная вещь: C # и .NET, кажется, всегда хранят данные в порядке байтов, когда вы обращаетесь к ним, как описано в этом посте. Обнаружил это при портировании такого приложения на Mono на СОЛНЦЕ. Круто, но если у вас есть такая настройка, вы все равно должны использовать средства сериализации C #.

Кроме этого, ваши настройки выглядят очень хорошо!

2 голосов
/ 27 декабря 2008

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

Для каждого действия вы определяете структуру данных, а затем помещаете каждое из этих значений в структуру. Чтобы отправить его по сети, вы просто выделяете сумму (sizeof (struct.i)) байтов для каждого элемента в вашей структуре. Итак, ваш пакет будет выглядеть так:

[action ID][item 1 (sizeof(item 1 bytes)][item 1 (sizeof(item 2 bytes)]...[item n (sizeof(item n bytes)]

Идея в том, что вы уже знаете размер и тип каждой переменной на каждой стороне соединения, поэтому вам не нужно отправлять эту информацию.

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

-

Другой вариант - использовать \ r \ n для разграничения ваших переменных. Это потребует некоторых накладных расходов, и вам придется использовать текст, а не двоичные значения для чисел. Но таким образом вы можете просто использовать readline для чтения каждой переменной. Ваши пакеты будут выглядеть так

[action ID]
[item 1 (as text)]
...
[item n (as text)]

-

Наконец, простой сериализация объектов и передача их по проводам - ​​это тоже хороший способ сделать это с наименьшим количеством кода для записи. Помните, что вы не хотите преждевременно оптимизировать, в том числе и сетевой трафик. Если окажется, что позже вам нужно немного повысить производительность, вы можете вернуться и найти более эффективный механизм.

И проверьте буферы протокола Google , которые, предположительно, являются чрезвычайно быстрым способом сериализации данных нейтральным от платформы способом, вроде двоичного XML, но без вложенных элементов. Также есть JSON , который является еще одной платформно-нейтральной кодировкой. Использование буферов протокола или JSON означало бы, что вам не придется беспокоиться о том, как конкретно кодировать сообщения.

0 голосов
/ 27 декабря 2008

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...