1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/itfriday-protocol-pack

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
Внести вклад в разработку кода
Синхронизировать код
Отмена
Подсказка: Поскольку Git не поддерживает пустые директории, создание директории приведёт к созданию пустого файла .keep.
Loading...
README.md

Протокол Protocol-Pack: краткое описание

Protocol-Pack (далее PP) — это формат двоичного обмена данными, аналогичный протоколу Google Protocol Buffer (PB). Он использует формат данных TT(L)V (Tag-Type-Length-Value) и обладает характеристиками совместимости как в прямом, так и в обратном направлении. PP предоставляет реализации для нескольких языков: C, C++, Java, Object-C, причём каждая реализация стремится к минималистичному стилю кодирования, простоте, ясности и лёгкости понимания.

PP может использоваться для обмена данными между клиентом и сервером, а также для чтения и записи конфигурационных файлов. Реализации PP на разных языках предоставляют возможность кодировать данные структуры или объекты в строки формата XML. Реализация на C и C++ также предоставляет функции для обработки (или декодирования) данных элементов XML, а чтение XML-конфигурационных файлов может быть реализовано с использованием двух проектов: CXmlLoader и XmlLoader.

Особенности Protocol-Pack

  1. PP использует формат XML для определения данных структуры или объектов, после чего код для чтения и записи этих структур или объектов генерируется с помощью специального генератора кода. В настоящее время генератор кода способен генерировать код для языков C, C++, Java и Object-C.
  2. PP поддерживает структуры данных Union и Struct, а также enum. Поддерживается определение макросов или констант с помощью marco, а также массивов. Подробности о поддерживаемых структурах данных смотрите в соответствующем разделе ниже.
  3. Каждая языковая реализация PP предоставляет четыре функции: encode, decode, format и toXml. Функция encode кодирует данные структуры Union или Struct в двоичный формат (с прямым или обратным порядком байтов); функция decode выполняет декодирование двоичных данных в структуру Union или Struct; функция format кодирует структуру данных в читаемую строку, обычно используется для печати в журналах; функция toXml кодирует данные структуры в строку формата XML.
  4. Обычно PP использует формат TT(L)V для кодирования данных структур, но для числовых типов данных (например, 32-битных целых чисел), длина которых фиксирована, нет необходимости кодировать длину в поток данных, поэтому для них используется формат TTV. Подробные сведения о формате кодирования PP смотрите в соответствующем разделе ниже.
  5. Реализации PP на языках C и C++ предоставляют функции для чтения данных элементов XML, однако для чтения XML-конфигурационных файлов требуется использование проектов CXmlLoader и XmlLoader.
  6. Генератор кода PP написан на языке Python и может использоваться без компиляции на различных платформах. Для работы генератора кода требуется среда Python версии 2.4 или выше.

Объяснение путей проекта

  1. Каталог CMessage-Creator: здесь находится генератор кода. Инструкции по использованию генератора кода смотрите в соответствующем разделе ниже.
  2. Каталог CMessage: содержит базовые функции и тестовый код, поддерживающие реализацию на языке C. Если ваш проект использует язык C, необходимо включить эти коды в качестве зависимостей.
  3. Каталог JMessage: предоставляет базовые классы и тестовый код для реализации на языке Java. Если ваш проект основан на Java, вам нужно включить эти базовые классы.
  4. Каталог Message: содержит базовые классы и тестовый код для реализации на C++. Если ваш проект написан на C++, вам необходимо включить эти базовые классы.
  5. Каталог OCMessage: предлагает базовые классы и тестовый код для реализации на Object-C. Если ваш проект выполнен на Object-C, вам следует включить эти базовые классы (необходимо скопировать код из каталога pp в ваш проект).
  6. Каталог protocol-def: здесь хранится пример файла с определением структуры данных.

Руководство по быстрому старту

Чтобы использовать PP, выполните следующие шаги:

  1. Создайте файл описания структуры или объекта, используя формат XML (также называемый файлом описания интерфейса), чтобы определить данные структуры или объект. Вы можете определить данные структуры в разных файлах. Структуры или объекты могут ссылаться друг на друга в разных файлах, и не требуется явно импортировать другие файлы при необходимости ссылки на определённые в них структуры. Вы можете обратиться к примеру в каталоге protocol-def, например, файлу msg.xml по ссылке https://github.com/itfriday/protocol-pack/blob/master/protocol-def/msg.xml для написания файла определения структуры.
  2. Используйте генератор кода для создания данных структур или объектов на выбранном языке. Генератор кода создаст отдельный файл для каждой структуры. Например, если вы определили структуру под названием Person в файле описания и выбрали генерацию кода на языке C, будут созданы два файла: Person.h и Person.c.
  3. Добавьте сгенерированный код и зависимый базовый код в свой проект. Например, если ваш проект реализован на языке C, помимо добавления кода, сгенерированного на шаге 2, вам также потребуется добавить код из каталога CMessage в свой проект.
  4. Генератор кода создаёт четыре функции (encode, decode, format, toXml) для каждой структуры или объекта. Вы можете вызвать функцию encode для кодирования структуры или объекта в двоичные данные; вызвать функцию decode для декодирования двоичных данных в структуру или объект; использовать функцию format для преобразования структуры данных в читаемую строку при выводе в журнал; вызвать функцию toXml для сохранения данных объекта в формате XML-строки и вывода в файл.

Например, в языке C кодирование структуры Person в двоичные данные осуществляется следующим образом:

PERSON stPerson;
person_field_encode(&stPerson, 1, &stBa); // Второй аргумент 1 указывает на тег Person, третий аргумент stBa хранит массив двоичных данных, его инициализацию и описание смотрите далее в инструкции

Для декодирования структуры Person из двоичных данных в языке C используйте следующий код:

// Предположим, что szBinData содержит двоичные данные для декодирования, а dwDataLen — длину двоичных данных
// Декодирование двоичных данных в структуру Person на языке C выглядит следующим образом:
PERSON stPerson;
memset(&stPerson, sizeof(stPerson), 0);
person_field_decode(&stPerson, szBinData, dwDataLen);

Примечание: языки C, C++ и Java предоставляют класс массива байтов под названием ByteArray, который отвечает за обработку двоичных данных, включая кодирование и декодирование. Инициализация массива байтов на языке C выполняется следующим образом:

char szMsg1[1024]; // Определение размера массива байтов, обратите внимание, что в языке C массив байтов не расширяется автоматически, вы должны выделить достаточно места при инициализации
BYTEARRAY stBa; // Определение массива байтов
INIT_BYTE_ARRAY(stBa, szMsg1, sizeof(szMsg1)); // Инициализация массива байтов

Инициализация массива байтов в языке C++ выполняется следующим образом:

MByteArray stBa; // Массив байтов расширяется автоматически
// Или
MByteArray stBa(1024); // При инициализации указывается начальный размер кэша, если кэш недостаточен, он будет расширен автоматически
// Поскольку при расширении размера массива байтов происходит повторное выделение памяти и копирование данных в новую память, рекомендуется использовать форму с указанием начального размера кэша для избежания частых операций с памятью.

Инициализация массива байтов в Java выполняется следующим образом:

ByteArray ba = new ByteArray(); // Массив байтов расширяется автоматически
// Или
ByteArray ba = new ByteArray(1024); // При инициализации указывается начальный размер кэша, если кэш недостаточен, он будет расширен автоматически
// Поскольку при расширении размера массива байтов происходит повторное выделение памяти и копирование данных в новую память, рекомендуется использовать форму с указанием начального размера кэша для избежания частых операций с памятью.

Демонстрация

Каждая языковая версия предоставляет примеры, расположенные в соответствующих каталогах test. Вот примеры:

  • CMessage/test/main.c — пример программы на языке C;
  • JMessage/com/itfriday/test/Test.java — пример программы на языке Java;
  • Message/test/main.cpp — пример программы на языке C++. Чтобы запустить демонстрационную программу, сначала нужно выполнить в каталоге программу gen.bat (в Windows) или gen.sh (в Linux), чтобы сгенерировать соответствующий код. Программы gen.bat/gen.sh вызывают генератор кода PP, который преобразует файл msg.xml из каталога protocol-def в структуры или объекты.
  1. Затем вам нужно скомпилировать сгенерированный код в исполняемую программу и запустить её.
  • Для языка C в Linux перейдите в каталог CMessage/test и выполните команду Make, чтобы скомпилировать исполняемую программу под названием Message.
  • Для языка C++ в Linux перейдите в каталог Message/test и выполните команду Make, чтобы скомпилировать исполняемую программу под названием Message.
  • Для языка Java перейдите в каталог JMessage и выполните команду build для компиляции.
  • Для языка Object-C перейдите в каталог OCMessage, откройте проект в XCode и скомпилируйте его.
  1. Пример вывода после выполнения программы: // Вывод в виде кодировки структур [CsMsgResponse] Eno = 0 Cmd = 2 [RespData] [GetFriends] FriendNumber = 2 [FriendInfo] GID = 305419896 FriendName = ErisenXu FriendImage = http://www.qq.com/erisenxu.jpg [FriendInfo] GID = 2018915346 FriendName = xy FriendImage = http://www.qq.com/xy.jpg TypeNumber = 3 Types = 3430008 Types = 9004884 Types = 2464388554683811993 // Вывод в формате XML <CsMsgResponse> <Eno>0</Eno> <Cmd>2</Cmd> <RespData> <GetFriends> <FriendNumber>2</FriendNumber> <FriendInfo> <GID>305419896</GID> <FriendName>ErisenXu</FriendName> <FriendImage>http://www.qq.com/erisenxu.jpg&lt;/FriendImage> </FriendInfo> <FriendInfo> <GID>2018915346</GID> <FriendName>xy</FriendName> <FriendImage>http://www.qq.com/xy.jpg&lt;/FriendImage> </FriendInfo> <TypeNumber>3</TypeNumber> <Types>3430008</Types> <Types>9004884</Types> <Types>2464388554683811993</Types> </GetFriends> </RespData> </CsMsgResponse> // Вывод в виде двоичного потока 0000 00 01 0b 00 00 00 d3 00 01 03 00 00 00 02 03 00 ........ ........ 0010 02 00 03 0b 00 00 00 c2 00 02 0b 00 00 00 bb 00 ........ ........ ...

Описание интерфейса файла

Чтобы использовать PP, самое важное — это определить файл описания интерфейса, используя XML для определения структур или объектов. В этой главе мы подробно расскажем, как определить файл описания интерфейса.

  1. Базовая структура файла описания. Файл описания должен быть написан на стандартном XML и содержать корневой узел с именем field-config. Другие макросы, структуры, перечисления и т. д., определённые в файле, являются дочерними узлами field-config:
&lt;?xml version="1.0" encoding="utf-8" standalone="yes" ?>
&lt;field-config version="1">
    &lt;!-- Здесь определяются макросы, перечисления, структуры и т.д. -->
    ......
&lt;/field-config>
  1. Как определить макрос Используйте определение macro для определения макроса. В настоящее время поддерживается только определение макросов целочисленного типа. Атрибуты name и value определяют имя и значение макроса соответственно, а атрибут desc определяет описание макроса: ``` astTypes[MAX_TYPE_NUMBER]; // 类型列表 }; typedef struct tagFriendInfoList FRIENDINFOLIST; typedef struct tagFriendInfoList* LPFRIENDINFOLIST;
  1. Как определить объединение Определение объединения и определение структуры аналогичны, только для определения объединения используется union, свойство name задаёт имя объединения, свойство desc задаёт описание объединения, дочерний узел field определяет элементы, которые содержит объединение. Объединение может содержать один или несколько дочерних узлов field. Дочерний узел field содержит те же свойства, что и атрибуты узла field в структуре, см. таблицу выше.

Пример объединения:

<union name='CsRequestData' desc='客户端响应协议消息结构体'>
    <field    name="Login"         type="LoginRequest"            tag='CS_MSG_LOGIN'                    desc='客户端登录请求' />
    <field    name="GetFriends"    type="char"                    tag='CS_MSG_GET_FRIEND_LIST'          desc='获取好友列表请求' />
</union>

Если преобразовать в язык C, то соответствующий код будет выглядеть следующим образом:

struct tagCsRequestData
{
    U16 nSelector;  // Выборщик объединения, автоматически добавляется генератором кода.
    union
    {
        LOGINREQUEST stLogin; // Клиентский запрос на вход
        S8 chGetFriends;     // Запрос на получение списка друзей
    };
};
typedef struct tagCsRequestData  CSREQUESTDATA;
typedef struct tagCsRequestData* LPCSREQUESTDATA;

Здесь выборщик объединения автоматически добавляется генератором кода. Когда его значение совпадает с тегом какого-либо поля в объединении, объединение будет использоваться для сохранения значения этого поля. То есть:

if (nSelector == CS_MSG_LOGIN) {
    // tagCsRequestData представляет stLogin
} else if (nSelector == CS_MSG_GET_FRIEND_LIST) {
    // tagCsRequestData представляет chGetFriends
}

Как и структура, после определения объединение становится новым типом, на который могут ссылаться другие структуры. Его можно сослаться с помощью type или subtype, но необходимо указать атрибут select, пример:

<struct name="CsMsgRequest"    version="1"        desc="客户端请求协议" >
    <field name="GID"            type="ulong"            tag="1"        desc="玩家GID" />
    <field name="Cmd"            type="short"            tag="2"        desc="消息命令字" />
    <field name="ReqData"        type="CsRequestData"    tag="3"        desc="消息结构体"    select="Cmd"/>
</struct>

Соответствующий код на языке C:

struct tagCsMsgRequest
{
    U64 ullGID;              // Игрок GID
    S16 shCmd;               // Команда сообщения
    CSREQUESTDATA stReqData; // Структура сообщения
};
typedef struct tagCsMsgRequest  CSMSGREQUEST;
typedef struct tagCsMsgRequest* LPCSMSGREQUEST;

В этом примере определена структура CsMsgReuqest, тип подполя ReqData — CsRequestData, это объединение, его селектор указан как Cmd, поэтому когда Cmd==CS_MSG_LOGIN, ReqData будет представлять поле stLogin, а когда Cmd==CS_MSG_GET_FRIEND_LIST, ReqData будет представлять поле chGetFriends.

Использование генератора кода

Генератор кода находится в каталоге CMessage-Creator, это инструмент, написанный на Python, не требующий компиляции и работающий на разных платформах. Для него требуется среда Python версии 2.4 и выше.

Основная программа генератора кода — generator.py, выполнение команды python generator.py --help позволяет просмотреть справку.

Description: генератор сообщений.
Version: 1.0.0.0
Usage: python generator.py [-?] [-h] [-s DIRECTORY] [-d DIRECTORY]
                           [-l {c, cpp, java, oc}] [-m FILE_NAME] [-p PACKAGE_NAME]
optional arguments:
  -?, -h, --help    Показать эту справочную информацию
  -s DIRECTORY, --srcpath DIRECTORY
            Установить каталог поиска файла определения протокола
  -l {c, cpp, java, oc}, --language {c, cpp, java, oc}  
            Основной язык программирования, который вы хотите использовать, должен быть
            [c|cpp|java|oc]
  -d DIRECTORY, --directory DIRECTORY
            Установите каталог генерации проекта для проекта
  -m FILE_NAME
            Задайте имя файла макроса
  -p PACKAGE_NAME
            Установите пакет для java
Example:
  python generator.py --help
  python generator.py -s . -d ../test -l c -m StarMacro

Описание необязательных входных параметров:

Необязательные параметры:
  -?, -h, --help    Отображение справочной информации этой программы
  -s DIRECTORY, --srcpath DIRECTORY
            Укажите путь к файлу описания интерфейса
  -l {c, cpp, java, oc}, --language {c, cpp, java, oc}  
            Язык кода, генерируемого генератором кода, в настоящее время доступны варианты c, cpp, java и oc
  -d DIRECTORY, --directory DIRECTORY
            Установка пути генерации кода
  -m FILE_NAME
            Установка имени файла определения макроса
  -p PACKAGE_NAME
            Установка имени пакета, полезно только для языка java

Однако для данных числового типа (например, 32-битное целое число), их длина фиксирована, и нет необходимости кодировать длину в потоке данных, поэтому формат кодирования для них использует TTV.

Объяснение правил кодирования

PP использует формат TT(L)V для кодирования структуры данных, где:     T(Tag): метка поля, используемая для уникальной идентификации или различения поля. При кодировании в двоичном формате занимает 2 байта.     T(Type): тип поля, используемый для обозначения типа поля. Занимает 1 байт при кодировании в двоичном виде.     L(Length): длина значения поля (Value). Не включает длину, когда тип поля (Type) является целым числом. Занимает 4 байта при кодировании в двоичном виде.     V(Value): значение поля. Количество занимаемых байтов определяется параметром L(Length).

Типы полей, поддерживаемые PP:

``` | | | | | :--: | :--:| :--:| | 1 | 1 | 有符号整数,占1个字节 | | uchar | 2 | 1 | | short | 3 | 2 | | ushort | 4 | 2 | | int | 5 | 4 | | uint | 6 | 4 | | long | 7 | 8 | | ulong | 8 | 8 | | string | 9 | 不固定 | | bytes | 10 | 不固定 | | 结构体类型 | 11 | 不固定 | | array | 12 | 不固定 |

举个例子,Tag为1,类型为ushort,16进制值为0x1234的整数,编码为二进制为:

00 01 04 12 34

Tag为4,类型为string的字符串http://www.qq.com/xy.jpg,编码为二进制为:

00 04 09 00 00 00 18 68 74 74 70  3a 2f 2f 77 77 77 2e 71 71 2e 63 6f 6d 2f 78 79  2e 6a 70 67

其中:

00 04 为Tag
09 为类型
00 00 00 18 为字符串长度,这里长度是0x18,即24
68 74 74 70  3a 2f 2f 77 77 77 2e 71 71 2e 63 6f 6d 2f 78 79  2e 6a 70 67为字符串的值

而复合结构体的编码为各子字段编码的拼接,例如,假定一个结构体包含以上两个字段(即一个ushort一个string),则结构体编码为二进制为:

00 02 0b 00 00 00 24 00 01 04 12 34 00 04 09 00 00 00 18 68 74 74 70  3a 2f 2f 77 77 77 2e 71 71 2e 63 6f 6d 2f 78 79  2e 6a 70 67

其中:

00 02 为结构体Tag
0b 为类型
00 00 00 24 为结构体长度,这里长度是0x24,即36个字节
00 01 为第一个子字段的Tag
04 为类型
12 34为第一个字段的值
00 04 为第二个子字段的Tag
09 为类型(字符串)
00 00 00 18 为字符串长度,这里长度是0x18,即24
68 74 74 70  3a 2f 2f 77 77 77 2e 71 71 2e 63 6f 6d 2f 78 79  2e 6a 70 67为字符串的值

数组字段的编码,编码Value前,会编码数组数量,数组数量会占用2字节。例如,假定一个数组元素为上面定义的结构体,且数量为2,编码为二进制为:

00 03 0c 00 00 00 58 00 02
00 03 0b 00 00 00 24 00 01 04 12 34 00 04 09 00 00 00 18 68 74 74 70  3a 2f 2f 77 77 77 2e 71 71 2e 63 6f 6d 2f 78 79  2e 6a 70 67
00 03 0b 00 00 00 24 00 01 04 12 34 00 04 09 00 00 00 18 68 74 74 70  3a 2f 2f 77 77 77 2e 71 71 2e 63 6f 6d 2f 78 79  2e 6a 70 67

其中:

00 03 为数组Tag
0c 为类型
00 00 00 58 为数组长度,这里长度是0x58,即88个字节
00 02 为数组元素数量,这里数量为2
其他两行编码为数组元素结构体的编码。注意这里数组元素结构体的Tag与数组的Tag相同,都为0x03

联合(union)的编码规则和符合结构体的编码规则一样,只是要注意一点的是,union的标签Tag即是它的选择器selector,也即是它的有效子字段的标签。只有Tag对应的union子字段会被编码,其他无效的子字段不会被编码。

例如,对于联合CsRequestData

&lt;union name='CsRequestData' desc='客户端响应协议消息结构体'>
    &lt;field    name="Login"         type="LoginRequest"            tag='CS_MSG_LOGIN'                    desc='客户端登录请求' />
    &lt;field    name="GetFriends"    type="char"                    tag='CS_MSG_GET_FRIEND_LIST'          desc='获取好友列表请求' />
&lt;/union>

若其字段Login有效,则编码为二进制时,CsRequestData的tag将为CS_MSG_LOGIN,只有Login子字段会被编码;

若其字段GetFriends有效,则编码为二进制时,CsRequestData的tag为CS_MSG_GET_FRIEND_LIST,只有GetFriends子字段会被编码

Тип Значение Type Длина в байтах Описание типа
char

Комментарии ( 0 )

Вы можете оставить комментарий после Вход в систему

Введение

Protocol-Pack (далее — PP) — это формат обмена двоичными данными, аналогичный протоколу буфера протокола Google (сокращённо — PB). Он кодирует данные в формате TT(L)V (Tag-Type-Length-Value), обеспечивая совместимость вперёд и назад. PP имеет реализации на нескольких языках: C, C++, Java, Objective-C. Каждая реализация стремится к минималистичн... Развернуть Свернуть
LGPL-2.1
Отмена

Обновления

Пока нет обновлений

Участники

все

Недавние действия

Загрузить больше
Больше нет результатов для загрузки
1
https://api.gitlife.ru/oschina-mirror/itfriday-protocol-pack.git
git@api.gitlife.ru:oschina-mirror/itfriday-protocol-pack.git
oschina-mirror
itfriday-protocol-pack
itfriday-protocol-pack
master