Данный раздел посвящён документированию наработок по проектированию протоколов и форматов данных.

1. Когда документировать? Правильный ответ: даже если документация полностью неформальна (файл в каталоге проекта, или даже область комментариев в исходном коде), надо её писать как можно раньше, но не раньше, чем устоятся основы. Как правило, первая версия может считаться устоявшейся не раньше, чем она прошла основной цикл тестирования (по частям - юнит-тестирование, функциональное и нагрузочное), до завершения такого тестирования могут обнаруживаться проблемы, требующие тотального редизайна. Таким образом, оптимальный момент времени - сразу после "фуух..." и отмечания (включая распивание напитков) результатов успешного тестирования.

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

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

Дальнейшее изложение будет вестись по порядку: - документирование протокола как правил взаимодействия сторон - документирование формата данных в посылках

XXX участие сторон

Один стиль рисования взаимодействия:


S->C: привет, я сервер
C->S: дай миллион
S->C: а ключ от квартиры, где деньги лежат?
тут чётко видно, от какой стороны какой идёт сообщение.

Варианты:


I-> привет
T-> ты кто???
(RFC3720, iSCSI)

Другой стиль:


>>> привет, я сервер
<<< дай миллион
>>> а ключ от квартиры, где деньги лежат?

Во втором случае стрелка показывает направление, то, что "за экраном", считается удалённой стороной. Какая сторона в каком случае удалённая - решается по роли:)

XXX Стиль диаграмм SIP

Обозначения битов и байтов.

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

В области обозначения порядка битов в байтах и словах наблюдается жуткий бардак.

Стиль 1: биты называются по логарифму их числового веса - бит номер N имеет вес 2**N при интерпретации как целое беззнаковое. В этом случае самый младший бит имеет номер 0, следующий за ним - 1, и так далее; максимальный номер в одном октете - 7, в 4-октетном слове - 31. Этот стиль популярен у большинства производителей оборудования и, например, используется во всей документации Intel. Порядок битов в байте определяется однозначно (за пределами сериализации), но порядок битов в слове зависит от порядка байтов в слове - в случае 32-битного little endian слова, в байте с минимальным смещением будут биты 7-0, а с максимальным - 31-24; для big endian, очевидно, наоборот.

Коротко этот стиль можно обозначить так: 0=LSB (LSB значит Least Significant Bit).

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


+----------+--------+--------------------------------------+
| Смещение | Размер | Содержание                           |
+----------+--------+--------------------------------------+
|        0 |    2   | Тип сообщения, unsigned int          |
|        2 |    2   | Общие флаги:                         |
|          |        | Бит 15 - требуется ответ             |
|          |        | Бит 14 - допускается                 |
|          |        |   асинхронный ответ                  |
|          |        | Биты 1-0 - требуемый класс           |
|          |        |   обслуживания                       |
+----------+--------+--------------------------------------+

Пример диаграммы в этом стиле - описание команды READ(6) из SCSI Block Commands:


     Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
Byte     |                               |
---------+-------------------------------+
  0      |     OPERATION CODE (08h)      |
---------+-------------------------------+
  1      |  Reserved |(MSB)              |
  2      |     LOGICAL BLOCK ADDRESS     |
  3      |                          (LSB)|
---------+-------------------------------+
  4      |       TRANSFER LENGTH         |
---------+-------------------------------+
  5      |             CONTROL           |
---------+-------------------------------+

Здесь: OPERATION CODE, TRANSFER LENGTH и CONTROL занимают по полному байту (со смещением 0, 4 и 5 соответственно); резервное поле занимает биты 7-5 байта 1; поле LOGICAL BLOCK ADDRESS занимает 21 бит - от бита 4 байта 1 и до бита 0 байта 3. Дополнительно метки "(MSB) " и "(LSB)" показывают места размещения соответственно самого старшего и самого младшего бита данного поля.

Стиль 2: нумерация от старших к младшим битам начиная от 0, полностью обратно стилю 1. Этот стиль почему-то популярен у IETF. Например, в RFC791 (протокол IP):


    0                   1                   2                   3   
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |Version|  IHL  |Type of Service|          Total Length         |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |         Identification        |Flags|      Fragment Offset    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |  Time to Live |    Protocol   |         Header Checksum       |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
[продолжение удалено]

В этой диаграмме октет со смещением 0 от начала пакета состоит из полей Version и IHL, причём Version занимает старшие 4 бита октета, а IHL - младшие 4 бита; Identification занимает 2 октета по смещениям 4 и 5. Объяснение обозначений приводится в приложении B той же спецификации.

Коротко этот стиль можно обозначить так: 0=MSB (MSB значит Most Significant Bit).

Стиль 3: биты нумеруются от 1 начиная с самого старшего.

Короткое обозначение стиля: 1=MSB.

Стиль 4: биты нумеруются от 1 начиная с самого младшего. Пример использования: ISO спецификация X.690; ГОСТы (например, 26.201.2.94 - КАМАК).

Короткое обозначение стиля: 1=LSB.

В принципе этот набор 4 вариантов покрывает все возможности, как можно нумеровать биты в порции данных без неестественных подходов.

В общем случае мы рекомендуем использовать стиль 1 (0=LSB) как имеющий наиболее "естественное" обоснование и соответствующий традициям гигантов типа Intel и Microsoft. Однако, при необходимости адаптации описания под определённую традицию (например, для IETF) предпочтительнее выбирать стиль по обстановке.

Для нумерации байтов в посылке или её части первым общепринятым стилем является указание минимального смещения объекта от начала посылки. Например, если 32-битное число занимает 4 байта по смещениям 4,5,6,7 от начала посылки, то говорится, что оно находится по смещению 4. Тогда состав посылки рисуется таблицей.

Пример таблицы:


+----------+---------+-----------------------------------------+
| Смещение |   Тип   |  Содержание                             |
+----------+---------+-----------------------------------------+
|    0     |  U32BE  |  Тег запроса                            |
|    4     |  U16BE  |  Код типа запроса                       |
|    6     |  U16BE  |  Флаги - битовое поле                   |
|          |         |  * биты 15-2 зарезервированы и          |
|          |         |    должны быть равны 0                  |
|          |         |  * бит 1 - запрос должен быть записан   |
|          |         |    в специальный журнал                 |
|          |         |  * бит 0 - запрос требует               |
|          |         |    квалифицированной обработки          |
| 8...last |         |  Данные запроса, зависят от типа        |
+----------+---------+-----------------------------------------+

(Интересно, что Intel в части документации по процессорам линии x86 в таких таблицах использует противоположное направление роста - минимальные смещения у них внизу. Но это не общее правило даже для процессоров.)

Несколько другой стиль таблицы был применён ранее - там вместо типа значения явно писался его размер, а интерпретация (целое/другое, со знаком или без знака...) определялось в содержании.

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

Другой вариант, применяемый IETF - диаграмма полей посылки, как в примере выше для заголовка IP пакета. Её преимущество - в наглядности размещения, включая биты. Однако, чтобы вычислить, на каком смещении находится поле, надо высчитывать его по диаграмме, для удобства тыкая пальцем в экран:) Почему-то у IETF не принято рисовать слева от такой таблицы смещения начала строк (было бы 0, 4, 8, 12...)

Ещё достаточно часто используется формат описания записи (тип struct) в Си. При рассмотрении записи в этом формате следует не забывать учитывать выравнивание, которое может быть явно не указано соответствующими полями, а также подразумеваемые размеры полей; тут могут быть тонкости (например, если поле просто обозначено как long, это 4 байта, 8 или что-то совсем другое?), их следует решать по контексту, а при формализации - требовать обязательного уточнения. Желательным вариантом является явное указание всех полей, не заполненных данными и размещённых для выравнивания.

Как правило, такие свойства формата, как порядок байт, едины для всей посылки. (Заметные исключения: ISO9660, где ключевые поля записываются в двух копиях; каждая для своего порядка; IPMI, где ASF транспорт использует big-endian, а вложенный в него IPMI - little-endian.) Поэтому их опускают при указании каждого отдельного элемента. А вот размеры отдельных полей и интерпретация числа (как минимум - signed или unsigned) выбирается под задачу, поэтому совершенно нормально в одной посылке видеть одновременно поля в 8, 16, 32 и 64 бита и разной знаковости.


Документирование синтаксиса (грамматики).

В этой области в основном применяются вариации на тему формы Бэкуса-Наура (БНФ; англ. - Backus-Naur Form). Фактически такая форма представляет собой описание контекстно-свободной формальной грамматики. В основном БНФ разрабатывались для текстовых форматов, однако применимы (особенно ABNF) к двоичным форматам.

ABNF определяется IETF RFC5234 (STD68). Она достаточно удобна тем, что определяет синтаксис с точностью до конкретных значений октетов, и удобнее остальных задаёт правила повторений. Самым известным мне на сейчас неудобством является злоупотребление игнорированием регистра символов (нельзя потребовать написать слово hello просто написав "hello", надо написать %x68.65.6c.6c.6f).

Контекстные ограничения вводятся отдельными средствами, как правило это текстовые комментарии к документированию. Именно контекстные ограничения делают БНФ менее пригодными для двоичных, нежели для текстовых протоколов: например, задача описания того, что перед значением элемента передаётся его длина (неважно, в каком формате), уже недоступна таким формальным средствам. Публичные стандарты на двоичные форматы данных используют разные, но всегда жёсткие стили объяснения, как именно следует заполнять поля данных.

XXX развернуть


Copyright (C) 2006-2012 Valentin Nechayev. All rights reserved.