MQTT自學筆記(二)—Message format

前言:

關於MQTT的學習我是參照的MQTT V3.1版本。
這裏有MQTT V3.1的在線版本:在線版本
官方下載地址:PDF格式版本
我就是看這個官方的英文文檔學習的,其實我這博客基本上就是翻譯和總結了這個協議文檔。

Message format

每個MQTT消息頭命令消息包含一個Fixed header (固定頭部)。一些信息還需要一個Variable header(可變頭部)和Payload(有效載荷)。消息頭每個部分的格式描述在以下部分:

1. Fixed header

每個MQTT消息頭命令消息包含一個固定頭部。2個字節(16個字)。如下表顯示:
Fixed header

Byte 1

包含消息類型和標誌(DUP、QoS級別和保留)字段。

1. Mesage Type

Postion: byte 1, bits 7-4
表示爲4位無符號值,MQTT V3.1版本的協議枚舉在下表:
Message Type
除去0和15位置屬於保留,總共14種事件類型。

2. DUP flag(打開標誌)

Postion: byte 1, bits 3
保證消息可靠傳輸,默認爲0,只佔用一個bit,表示第一次發送,不能用來檢測消息重複發送,在客戶端或者服務器端嘗試重發PUBLISH, PUBREL, SUBSCRIBE 或 UNSUBSCRIBE消息,注意需要滿足以下條件:
當QoS > 0
消息需要回復確認

此時,在可變頭部需要包含消息ID。當值爲1時,表示當前消息先前已經被傳送過。

3. QoS level ( Quality of Service)

Postion: byte 1, bits 2-1
這個標誌使用兩個2進製表示PUBLISH類型的消息:
QoS

4. RETAIN(保持)

Postion: byte 1, bits 0
這個標誌位僅針對PUBLISH消息。
僅針對PUBLISH消息。不同值,不同含義:
1:表示發送的消息需要一直持久保存(不受服務器重啓影響),不但要發送給當前的訂閱者,並且以後新來的訂閱了此Topic name的訂閱者會馬上得到推送。
備註:新來乍到的訂閱者,只會取出最新的一個RETAIN flag = 1的消息推送。
0:僅僅爲當前訂閱者推送此消息。

假如服務器收到一個空消息體(zero-length payload)、RETAIN = 1、已存在Topic name的PUBLISH消息,服務器可以刪除掉對應的已被持久化的PUBLISH消息。

Byte 2

1. Remaining Length

(至少一個字節)包含剩餘的長度字段。在以下部分中會描述這部分字段。所有數據值都在高位優先順序:高位字節在前,低位字節在後。提出了一種16位字作爲最重要的字節(MSB),其次是最低有效字節(LSB)。
在當前消息中剩餘的byte(字節)數,包含可變頭部和負荷(稱之爲內容/body,更爲合適)。單個字節最大值:01111111,16進制:0x7F,10進製爲127。
單個字節爲什麼不能是11111111(0xFF)呢?
因爲MQTT協議規定,第八位(最高位)若爲1,則表示還有後續字節存在。同時MQTT協議最多允許4個字節表示剩餘長度。那麼最大長度爲:0xFF,0xFF,0xFF,0x7F,二進制表示爲:11111111,11111111,11111111,01111111,十進制:268435455
比如:十進制的321是需要用兩個字節來表示(65 + 2*128 ),轉換成十六進制是0xFF ,0x41,(網絡字節順序)。
byte=261120KB=256MB=0.25GB 四個字節之間值的範圍:
Remaining Length
1、一個十進制編碼數轉換成Remaining Length(不確定的字節值的算法)如下:

do
{
  digit = X MOD 128
  X = X DIV 128
  // if there are more digits to encode, set the top bit of this digit
  if ( X > 0 )
    digit = digit OR 0x80
  endif
  //'output' digit
  }
while ( X> 0 )

這裏的“MOD”表示:按模運算符(% in C),
“DIV”表示:整除算法( / in C )
“OR”表示:是按位或(| in C)。
***digit是以字節爲單位的Remaining Length。***digit對val求模,最大值可能是127,一旦127 | 10000000 = 11111111 = 0xff = 255
請注意: Remaining Length(剩餘長度),只在Fixed header (固定頭部)中,無論是一個字節,還是四個字節,不能被算作Variable header(可變頭部)中。
2、對於Remaining Length解析成十進制的算法如下:

multiplier = 1 ;
value = 0 ;
do 
{
  digit = 'next digit from stream' ;
  value += (digit AND 127) * multiplier;
  multiplier *= 128;
 }
while ((digit AND 128) != 0)

這裏的”AND”表示:和操作雲算符(& inC)。
算法結束時,value就是Remaining Length,這個值是十進制的。一般最後一個字節小於127(01111111),和0x80(10000000)進行&操作,最終結果都爲0,因此計算會終止。

Variable header

固定頭部僅定義了消息類型和一些標誌位,一些消息的元數據,需要放入可變頭部中。
需要再次強調的是:
Variable header(可變頭部)字節長度 + Playload(負荷)字節長度 = Remaining Length(剩餘長度)
這個是需要牢記的。可變頭部,包含了協議名稱,版本號,連接標誌,用戶授權,心跳時間等內容,這部分和後面要講到的CONNECT消息類型,有重複,暫時略過。

Payload

消息體主要是爲配合固定/可變頭部命令而存在,(比如CONNECT可變頭部User name標記若爲1則需要在payload(消息體/負載 )中附加用戶名稱字符串。
CONNECT/SUBSCRIBE/SUBACK/PUBLISH等消息有消息體。PUBLISH的消息體以二進制形式對待。
MQTT協議只允許在PUBLISH類型消息體中使用自定義特性,不可以在固定/可變頭部想加入自定義私有特性,這也是爲了協議免於流於形式,變得很分裂也爲了兼顧現有客戶端等。比如支持壓縮等,那就可以在Playload中定義數據支持,在應用中進行讀取處理。

Message identifiers

Message identifiers(消息標識符/消息ID)存在以下的MQTT消息的可變頭部中:PUBLISH, PUBACK, PUBREC, PUBREL, PUBCOMP, SUBSCRIBE, SUBACK, UNSUBSCRIBE, UNSUBACK。並且要求Fixed header (固定頭部)中的QoS level標誌值爲1或2時。
Message identifiers是一個16位無符號位的short類型值(值不能爲 0,0做保留作爲無效的消息ID),僅僅要求在一個特定方向(服務器發往客戶端爲一個方向,客戶端發送到服務器端爲另一個方向)的通信消息中必須唯一。比如客戶端發往服務器,有可能存在服務器發往客戶端會同時存在重複,但不礙事。
Message identifiers佔用兩個字節,需要指出的是先MSB然後LSB,也就是網絡字節順序(也稱大端順序)。
message identifiers

MQTT and UTF-8

utf - 8是一個高效的編碼優化的支持ASCII字符編碼文本通信的Unicode字符串。
在MQTT中,字符串以兩個字節爲前綴,表示長度,如下表所示。
UTF-8
字符串長度是編碼字符串字符的字節數,而不是字符的數量。例如,utf - 8編碼的字符串”OTWP”如下表所示,頭兩個字節爲一個完整的無符號數字,代表字符串字節長度,後面四個字節纔是字符串真正的長度,共六個字節
UTF-8

MQTT無論是可變頭部還是消息體中,只要是字符串部分,都是採用了修改版的UTF-8編碼。

總結

總之,掌握固定頭部的QoS level、RETAIN標記、可變頭部的Connect flags作用和意義,對徹底理解MQTT很有幫助。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章