Хотя изучение протокола утомительно, важно знать сам протокол. Если вы сможете разобраться в деталях и научиться с помощью некоторых экспериментов, это будет не так скучно.
формат сообщения
Протокол MQTT является протоколом прикладного уровня и должен передаваться с помощью протокола TCP/IP, аналогичного протоколу HTTP. Протокол MQTT также имеет собственный формат, как показано в следующей таблице:
[ Fixed Header | Variable Header | Payload]
Fixed Header:Фиксированный заголовок, протокол MQTT делится на множество типов, таких как соединение, публикация, подписка, сердцебиение и т. д. Требуется фиксированный заголовок, а фиксированный заголовок должен быть включен во все типы протоколов MQTT.
**Заголовок переменной:** Заголовок переменной, заголовок переменной не является обязательным, но означает, что эта часть существует в некоторых типах протоколов и не существует в некоторых протоколах.
**Полезная нагрузка:** Носителем сообщения является содержимое сообщения. Как и в случае с переменными заголовками, некоторые типы протоколов имеют содержимое сообщения, а некоторые типы протоколов не имеют содержимого сообщения.
фиксированная голова
Фиксированный заголовок состоит из двух частей: первого байта (байт 1) и длины оставшегося сообщения сообщения (1-4 байта).
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
Byte 1 | MQTT | Control | Packet | type | Flags specific | to each | MQTT Control | Packet type |
Byte 2... | Remaining | Length |
Во избежание неточных переводов здесь используется официальная оригинальная терминология. Тип управляющего пакета MQTT можно просто понять, поскольку байт Bit[7-4] используется для определения типа пакета. Флаги, специфичные для каждого типа управляющего пакета MQTT, означают, что байтовый бит Bit[3-0] используется в качестве специального флага для определенных пакетов.
первый байт
Первый байт используется для указания типа сообщения MQTT и некоторых типов управляющих флагов, как показано на рисунке выше. Старшие 4 бита (бит7~бит4) представляют тип протокола, который может представлять в общей сложности 16 типов протоколов, из которых 0000 и 1111 являются зарезервированными полями. Типы пакетов сообщений MQTT следующие.
тип сообщения | значение поля | направление данных | описывать |
---|---|---|---|
резерв | 0 | инвалид | резерв |
CONNECT | 1 | Client ---> Server | Клиент подключается к серверу |
CONNACK | 2 | Server ---> Client | подтверждение соединения |
PUBLISH | 3 | Client <--> Server | опубликовать новость |
PUBACK | 4 | Client <--> Server | Не подтверждено |
PUBREC | 5 | Client <--> Server | Сообщение получено (QoS2, фаза 1) |
PUBREL | 6 | Client <--> Server | Выпуск сообщения (QoS2, фаза 2) |
PUBCOMP | 7 | Client <--> Server | Конец выпуска (QoS2, фаза 3) |
SUBSCRIBE | 8 | Client ---> Server | Запрос клиентской подписки |
SUBACK | 9 | Server ---> Client | Подтверждение подписки на сервер |
UNSUBACRIBE | 10 | Client ---> Server | Клиент отписаться |
UNSUBACK | 11 | Server ---> Client | Подтверждение отписки сервера |
PINGREQ | 12 | Client ---> Server | Клиент отправляет сердцебиение |
PINGRESP | 13 | Server ---> Client | Сервер отвечает на сердцебиение |
DISCONNECT | 14 | Client ---> Server | Запрос на отключение клиента |
резерв | 15 | инвалид | резерв |
Младшие 4 бита (bit3~bit0) первого байта используются для представления управляющих полей некоторых типов сообщений.На самом деле, только несколько типов сообщений имеют управляющие биты, как показано на рисунке ниже.
тип сообщения | маркер с фиксированной головкой | Bit 3 | Bit 2 | Bit 1 | Bit 0 |
---|---|---|---|---|---|
CONNECT | резерв | 0 | 0 | 0 | 0 |
CONNACK | резерв | 0 | 0 | 0 | 0 |
PUBLISH | Used in MQTT 3.1.1 | DUP | QoS | QoS | RETAIN |
PUBACK | резерв | 0 | 0 | 0 | 0 |
PUBREC | резерв | 0 | 0 | 0 | 0 |
PUBREL | резерв | 0 | 0 | 1 | 0 |
PUBCOMP | резерв | 0 | 0 | 0 | 0 |
SUBSCRIBE | резерв | 0 | 0 | 1 | 0 |
SUBACK | резерв | 0 | 0 | 0 | 0 |
UNSUBACRIBE | резерв | 0 | 0 | 1 | 0 |
UNSUBACK | резерв | 0 | 0 | 0 | 0 |
PINGREQ | резерв | 0 | 0 | 0 | 0 |
PINGRESP | резерв | 0 | 0 | 0 | 0 |
DISCONNECT | резерв | 0 | 0 | 0 | 0 |
При публикации сообщения PUBLISH, если в поле DUP (бит 3) установлено значение 1, это указывает на то, что это дублирующее сообщение, в противном случае сообщение публикуется впервые. Чтобы обеспечить надежную доставку сообщения, когда QoS установлено на 1, когда клиент или сервер публикует сообщение, ему необходимо получить подтверждение (PUBACK) другой стороны.Если PUBACK не получен после период времени текущее сообщение будет отправлено снова, а поле DUP помечено как 1.
QoS используется для указания уровня QoS.Если бит 1 и бит 2 равны 0, это означает QoS 0. Если бит 1 равен 1, это означает QoS 1. Если бит 2 равен 1, это означает QoS 2. Если бит 1 и бит 2 одновременно установлены в 1, клиент или сервер сочтет это недопустимым сообщением и закроет текущее соединение.
В настоящее время бит [3-0] действителен только в протоколе PUBLISH, а в таблице указана версия MQTT 3.1.1. Для других версий протокола MQTT содержимое может быть другим. Для всех типов протоколов, помеченных как «зарезервированные» в фиксированном заголовке, бит [3-0] должен соответствовать таблице, например, для протокола SUBSCRIBE его бит 1 должен быть равен 1. Если получатель получит недопустимое сообщение, он принудительно закроет текущее соединение.
Remaining Length
Оставшаяся длина означает оставшуюся длину, то есть длину заголовка переменной + полезную нагрузку. Оставшаяся длина начинается с байта 2 и может достигать 4 байтов. Таким образом, оставшийся диапазон длин составляет Byte[2-5]. Так как же определить его длина 1 или 4?Это зависит от старшего бита байта,Бита 7 (по умолчанию старший байт стоит первым).Если значение равно 1,то продолжаем вычислять длину байта , Если он равен 0, то длина в байтах больше не рассчитывается.
Длина сообщения может быть просто понята как 128-разрядные данные, а максимальная длина в 4 бита может представлять 128*128*128*128 байт = 256 МБ. Однако вычисление этой длины несколько специфично, т. е. младший разряд находится впереди, а старший — сзади (поскольку нормальный метод представления состоит в том, что старший порядок находится впереди, а младший порядок — сзади). сзади), а старший байт Bit7 используется для обозначения необходимости продолжения вычисления длины сообщения. Ниже приведены диапазоны длин сообщений:
байт | минимум | максимальное значение |
---|---|---|
1 | 0(0x00) | 127(0x7F) |
2 | 128 (0x80, 0x01) | 16 383 (0xFF, 0x7F) |
3 | 16 384 (0x80, 0x80, 0x01) | 2 097 151 (0xFF, 0xFF, 0x7F) |
4 | 2 097 152 (0x80, 0x80, 0x80, 0x01) | 268 435 455 (0xFF, 0xFF, 0xFF, 0x7F) |
Обратите внимание, что 0x80=1000 0000, а не 1000. Сначала я подумал, что это 1000, поэтому я не понял.
Например.
В сообщении предполагается, что длина равна [0X60], его двоичное значение равно 01100000, а старший байт Bit7 (0-й бит слева) равен 0, поэтому нет необходимости продолжать вычисление. Тогда длина сообщения будет 0X60, а десятичное число — 96.
Если длина сообщения [0XC1, 0XC2, 0X33], то их двоичные представления следующие:
0xC1=1100 0001
0xC2=1100 0010
0x33=0011 0011,
Старший бит первого байта равен 1, далее нужно продолжить считать в обратном порядке, убрать бит флага (0xC1%128) и получить 100 0001=41
Старший бит второго байта равен 1, тогда нужно продолжить считать в обратном порядке, убрать бит флага (0xC2%128) и получить 100 0010=42
Старший бит третьего байта равен 0, нет необходимости считать в обратном направлении, результат 0x33=51
Поскольку младший разряд находится впереди, а старший — сзади, длина вычисляется как длина = 41 + 42 * 128 + 51 * 128 * 128 = 841001 B = 821 КБ.
Следует отметить, что длина сообщения = длина переменного заголовка + длина содержимого сообщения. Без учета первого байта и длины самого сообщения, если длина сообщения равна 5, это означает, что после сообщения следует 5 байт, а длина всего сообщения равна 7 (первый байт + 1-битная длина байт + 5).
Кроме того, если длина сообщения составляет 4 байта, последний бит не может превышать 0X7F=127, потому что, если он превышает это значение, его старший бит Bit7 равен 1, и его необходимо вычислить позже, что противоречит максимальной длине сообщения 4. байт. Таким образом, если появляется сообщение длиной [0XFF, 0XFF, 0XFF, 0XFF], то получатель считает это недопустимым сообщением.
Variable Header
Переменный заголовок означает переменный заголовок сообщения. Некоторые типы пакетов содержат переменные заголовки, такие как PUBLISH, SUBSCRIBE, CONNECT и т. д. Переменный заголовок находится между фиксированным заголовком и содержимым сообщения, и его содержимое зависит от типа сообщения.
Идентификатор пакета (идентификатор сообщения) представляет собой общий заголовок переменной.Идентификатор сообщения содержит 2 байта, причем старший байт идет первым, а младший байт последним. Типы протоколов, которые содержат идентификатор пакета, включают:
тип сообщения | Включить заголовки переменных |
---|---|
PUBLISH | YES(QoS > 0) |
PUBACK | YES |
PUBREC | YES |
PUBREL | YES |
PUBCOMP | YES |
SUBSCRIBE | YES |
SUBACK | YES |
UNSUBSCRIBE | YES |
UNSUBACK | YES |
По умолчанию идентификатор сообщения начинается с 1 и увеличивается автоматически.Если идентификатор сообщения израсходован, его можно использовать повторно. Для PUBLISH (QoS 1), если отправитель получает PUBACK, идентификатор сообщения используется. Для PUBLISH (QoS 2), если получатель получает PUBCOMP, этот идентификатор сообщения используется. Для SUBSCRIBE и UNSUBSCRIBE завершение использования идентификатора сообщения указывается отправителем, получающим соответствующие SUBACK и UNSUBACK.
Кроме того, идентификаторы сообщений клиента и сервера назначаются независимо друг от друга, и клиент и сервер могут одновременно использовать один и тот же идентификатор сообщения. Например
Client Server
PUBLISH Packet Identifier=0x1234--->
<--PUBLISH Packet Identifier=0x1234
PUBACK Packet Identifier=0x1234--->
<--PUBACK Packet Identifier=0x1234
Приведенный выше клиент сообщений отправляет сообщение на сервер, используется идентификатор сообщения 0x1234, а сервер отправляет сообщение клиенту, который также использует идентификатор сообщения 0x1234. Затем клиент отвечает серверу, отправляет PUBACK и, наконец, клиент получает ответ PUBACK от сервера.
Кроме того, другие протоколы, такие как CONNECT и CONNACK, также имеют переменные заголовки.MQTT-Packet CONNECT Variable Header
Payload
Некоторые типы пакетов содержат Payload, что означает носитель сообщения.Например, Payload of PUBLISH означает содержимое сообщения. Полезная нагрузка CONNECT содержит идентификатор клиента, тему завещания, сообщение завещания, имя пользователя, пароль и другую информацию. Для получения подробной информации см.MQTT-Packet CONNECT Payload
Типы сообщений, которые содержат полезную нагрузку, следующие:
тип сообщения | Включать ли полезную нагрузку |
---|---|
CONNECT | YES |
PUBLISH | по желанию |
SUBSCRIBE | YES |
SUBACK | YES |
UNSUBSCRIBE | YES |
За исключением перечисленных выше типов сообщений, все другие типы сообщений не имеют полезной нагрузки.
Тест захвата пакетов
Мы используем Wire Shark для захвата пакетов и определения содержимого сообщений MQTT.
1. Откройте Wireshark, выберите свою сетевую карту, добавьте следующие условия фильтра и нажмите, чтобы начать захват.
tcp.port==1883
2. Откройте терминал и введите следующую команду, чтобы опубликовать сообщение. Если вы не понимаете команду, см. мою предыдущую статьюБыстрый старт MQTT
$ mosquitto_pub -d -p 1883 -h 10.69.94.176 -q 1 -t topic1 -m "Hello MQTT"
Client mosqpub|2052-SCNWCL0121 sending CONNECT
Client mosqpub|2052-SCNWCL0121 received CONNACK (0)
Client mosqpub|2052-SCNWCL0121 sending PUBLISH (d0, q1, r0, m1, 'topic1', ... (10 bytes))
Client mosqpub|2052-SCNWCL0121 received PUBACK (Mid: 1)
Client mosqpub|2052-SCNWCL0121 sending DISCONNECT
3. Войдите в окно захвата Wireshark и обнаружите, что некоторые протоколы TCP и MQTT были захвачены, как показано ниже:
4. Чтобы просмотреть одно из сообщений MQTT, например «Опубликовать сообщение», щелкните строку. Просмотр содержимого сообщения MQTT. Его байт-код
32 14 00 06 74 6f 70 69 63 31 00 01 48 65 6c 6c 6f 20 4d 51 54 54
5. Давайте подробно рассмотрим содержание сообщения
-
Первый байт 0x32=0011 0010, по сравнению с таблицей в первом байте, 4-битный старший байт равен 0011=3, что означает ПУБЛИКАЦИЯ, а 4-битный младший байт равен 0010, что соответственно означает DUP 0, QoS 1 (два бита), сохранить 0.
-
Оставшаяся длина 0x14=20, что указывает на то, что оставшаяся длина сообщения равна 20.
-
Пакетное сообщение PUBLISH (QoS>0) содержит переменный заголовок, а переменный заголовок содержит имя раздела и идентификатор пакета. Его формат:
-
00 06 представляет длину имени темы, поэтому длина имени темы равна 6.
-
74 6f 70 69 63 31 представляет собой строку UTF8 имени темы, и ее значение равно «topic1».
-
00 01 — это идентификатор пакета, поэтому идентификатор сообщения равен 1.
-
48 65 6c 6c 6f 20 4d 51 54 54 — это полезная нагрузка, представляющая байт-код UTF8 «Hello MQTT».
Суммировать
Мы представили формат сообщения протокола MQTT. Формат сообщения MQTT включает фиксированный заголовок, вариабельный заголовок и полезную нагрузку. Поскольку формат сообщения MQTT очень компактный, данные могут быть эффективно переданы.
Фиксированный заголовок содержит первый байт, старшие 4 бита используются для указания типа сообщения, а младшие 4 бита используются для управления типом. В настоящее время только PUBLISH использует поле управления типом. Другие поля управления зарезервированы и должны соответствовать определению протокола.
Фиксированный заголовок также включает оставшуюся длину, то есть оставшуюся длину сообщения, с максимальной длиной байтов 4. Теоретически один MQTT может передавать максимум 256 МБ данных. Оставшаяся длина = переменный заголовок + длина полезной нагрузки.
Переменный заголовок — это переменный заголовок. Некоторые типы сообщений должны включать переменные заголовки. Переменные заголовки различаются в зависимости от типа сообщения. Например, идентификатор пакета используется в сообщениях публикации, подписки/отписки и других сообщениях.
Полезная нагрузка — это содержимое сообщения, и оно появляется только в некоторых типах сообщений, а его содержимое и формат зависят от типа сообщения.
Все статьи вGithubСинхронизируйтесь, вы также можете посетить мойличный блогНажмите, чтобы просмотреть