1. 程式人生 > >MQTT自學筆記(二)—Message format

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很有幫助。