1、MQTT 控制報文格式
MQTT 控制報文的結構
在MQTT協議中,一個MQTT數據包由:固定頭(Fixed header)、可變頭(Variable header)、消息體(payload)三部分構成。MQTT數據包結構如下:
(1)固定頭(Fixed header):存在於所有MQTT數據包中,表示數據包類型及數據包的分組類標識。
(2)可變頭(Variable header):存在於部分MQTT數據包中,數據包類型決定了可變頭是否存在及其具體內容。
(3)消息體(Payload):存在於部分MQTT數據包中,表示客戶端收到的具體內容。
1.1、 固定報頭
每個 MQTT 控制報文都包含一個固定報頭。固定報頭的格式如下圖所示:
MQTT固定報文頭最少有兩個字節,第一字節包含消息類型(Message Type)和QoS級別等標志位。第二字節開始是剩余長度字段,該長度是后面的可變報文頭加消息負載的總長度,該字段最多允許四個字節。
剩余長度字段單個字節最大值為二進制0b0111 1111,16進制0x7F。也就是說,單個字節可以描述的最大長度是127字節。為什么不是256字節呢?因為MQTT協議規定,單個字節第八位(最高位)若為1,則表示后續還有字節存在,第八位起“延續位”的作用。
例如,數字64,編碼為一個字節,十進制表示為64,十六進制表示為0×40。數字321(65+2*128)編碼為兩個字節,重要性最低的放在前面,第一個字節為65+128=193(0xC1),第二個字節是2(0x02),表示2×128。
由於MQTT協議最多只允許使用四個字節表示剩余長度(如表1),並且最后一字節最大值只能是0x7F不能是0xFF,所以能發送的最大消息長度是256MB,而不是512MB。
(1)MQTT 控制報文的類型
位置:byte1, bit7-bit4。
表示為 4 位無符號值, 這些值的定義見下表---控制報文的類型
名字 |
值 |
報文流動方向 |
描述 |
Reserved |
0 |
禁止 |
保留 |
CONNECT |
1 |
客戶端到服務端 |
客戶端請求連接服務端 |
CONNACK |
2 |
服務端到客戶端 |
連接報文確認 |
PUBLISH |
3 |
兩個方向都允許 |
發布消息 |
PUBACK |
4 |
兩個方向都允許 |
QoS 1 消息發布收到確認 |
PUBREC |
5 |
兩個方向都允許 |
發布收到(保證交付第一步) |
PUBREL |
6 |
兩個方向都允許 |
發布釋放(保證交付第二步)MQTT-3.1.1-CN 15 |
PUBCOMP |
7 |
兩個方向都允許 |
QoS 2 消息發布完成(保證交互第三步) |
SUBSCRIBE |
8 |
客戶端到服務端 |
客戶端訂閱請求 |
SUBACK |
9 |
服務端到客戶端 |
訂閱請求報文確認 |
UNSUBSCRIBE |
10 |
客戶端到服務端 |
客戶端取消訂閱請求 |
UNSUBACK |
11 |
服務端到客戶端 |
取消訂閱報文確認 |
PINGREQ |
12 |
客戶端到服務端 |
心跳請求 |
PINGRESP |
13 |
服務端到客戶端 |
心跳響應 |
DISCONNECT |
14 |
客戶端到服務端 |
客戶端斷開連接 |
Reserved |
15 |
禁止 |
保留 |
固定報文頭中的第一個字節包含連接標志(Connect Flags),連接標志用來區分MQTT的消息類型。MQTT協議擁有14種不同的消息類型,可簡單分為連接及終止、發布和訂閱、QoS 2消息的機制以及各種確認ACK。
(2)、用於指定控制報文類型的標志位
固定報頭第 1 個字節的剩余的 4 位 [3-0]包含每個 MQTT 控制報文類型特定的標志。
表格中任何標記為“保留”的標志位, 都是保留給以后使用的, 必須設置為表格中列出的值 。 在不使用標識位的消息類型中,標識位被作為保留位。如果收到無效的標志時,接收端必須關閉網絡連接。
控制報文 |
固定報頭標志 |
Bit 3 |
Bit 2 |
Bit 1 |
Bit 0 |
CONNECT |
Reserved |
0 |
0 |
0 |
0 |
CONNACK |
Reserved |
0 |
0 |
0 |
0 |
PUBLISH |
Used in MQTT 3.1.1 |
DUP1 |
QoS2 |
QoS2 |
RETAIN3 |
PUBACK |
Reserved |
0 |
0 |
0 |
0 |
PUBREC |
Reserved |
0 |
0 |
0 |
0 |
PUBREL |
Reserved |
0 |
0 |
1 |
0 |
PUBCOMP |
Reserved |
0 |
0 |
0 |
0 |
SUBSCRIBE |
Reserved |
0 |
0 |
1 |
0 |
SUBACK |
Reserved |
0 |
0 |
0 |
0 |
UNSUBSCRIBE |
Reserved |
0 |
0 |
1 |
0 |
UNSUBACK |
Reserved |
0 |
0 |
0 |
0 |
PINGREQ |
Reserved |
0 |
0 |
0 |
0 |
PINGRESP |
Reserved |
0 |
0 |
0 |
0 |
DISCONNECT |
Reserved |
0 |
0 |
0 |
0 |
DUP:發布消息的副本。用來在保證消息的可靠傳輸,如果設置為1,則在下面的變長中增加MessageId,並且需要回復確認,以保證消息傳輸完成,但不能用於檢測消息重復發送。
QoS:發布消息的服務質量,即:保證消息傳遞的次數
00:最多一次(QoS==0),即:<=1。消息發布完全依賴底層 TCP/IP 網絡。協議里沒有定義應答和重試,會發生消息丟失或重復。消息要么只會到達服務端一次,要么根本沒有到達。這一級別可用於如下情況,環境傳感器數據,丟失一次讀記錄無所謂,因為不久后還會有第二次發送。
01:至少一次(QoS==1),即:>=1。確保消息到達,但消息重復可能會發生。服務器的消息接收由PUBACK消息進行確認,如果通信鏈路或發送設備異常,或者指定時間內沒有收到確認消息,發送端會重發這條在消息頭中設置了DUP位的消息。
10:只有一次(QoS==2),即:=1。確保消息到達一次。這是最高級別的消息傳遞,消息丟失和重復都是不可接受的,使用這個服務質量等級會有額外的開銷。 這一級別可用於如下情況,在計費系統中,消息重復或丟失會導致不正確的結果。小型傳輸,開銷很小(固定長度的頭部是 2 字節),協議交換最小化,以降低網絡流量。
11:預留
比如目前流行的共享單車智能鎖,智能鎖可以定時使用QoS level 0質量消息請求服務器,發送單車的當前位置,如果服務器沒收到也沒關系,反正過一段時間又會再發送一次。之后用戶可以通過App查詢周圍單車位置,找到單車后需要進行解鎖,這時候可以使用QoS level 1質量消息,手機App不斷的發送解鎖消息給單車鎖,確保有一次消息能達到以解鎖單車。最后用戶用完單車后,需要提交付款表單,可以使用QoS level 2質量消息,這樣確保只傳遞一次數據,否則用戶就會多付錢了。
RETAIN: 發布保留標識,表示服務器要保留這次推送的信息,如果有新的訂閱者出現,就把這消息推送給它,如果設有那么推送至當前訂閱者后釋放。
(3)、 剩余長度
位置: 從第 2 個字節開始。
固定頭的第二字節用來保存變長頭部和消息體的總大小的,但不是直接保存的。這一字節是可以擴展,其保存機制,前7位用於保存長度,后一部用做標識。當最后一位為1時,表示長度不足,需要使用二個字節繼續保存。例如:計算出后面的大小為0
剩余長度(Remaining Length) 表示當前報文剩余部分的字節數, 包括可變報頭和負載的數據。 剩余長度不包括用於編碼剩余長度字段本身的字節數。
剩余長度字段使用一個變長度編碼方案, 對小於 128 的值它使用單字節編碼。 更大的值按下面的方式處理。低 7 位有效位用於編碼數據,最高有效位用於指示是否有更多的字節。 因此每個字節可以編碼 128 個數值和一個延續位(continuation bit) 。 剩余長度字段最大 4 個字節。
非規范評注:例如, 十進制數 64 會被編碼為一個字節, 數值是 64, 十六進制表示為 0x40,。十進制數字321(=65+2*128)被編碼為兩個字節, 最低有效位在前。 第一個字節是 65+128=193。 注意最高位為1 表示后面至少還有一個字節。 第二個字節是 2。
非規范評注:這允許應用發送最大 256MB(268,435,455)大小的控制報文。這個數值在報文中的表示是:0xFF,0xFF,0xFF,0x7F。
表格 剩余長度字段的大小
1.2、可變報頭
可變報文頭主要包含協議名、協議版本、連接標志(Connect Flags)、心跳間隔時間(Keep Alive timer)、連接返回碼(Connect Return Code)、主題名(Topic Name)等。
某些 MQTT 控制報文包含一個可變報頭部分。 它在固定報頭和負載之間。可變報頭的內容根據報文類型的不同而不同。可變報頭的報文標識符(Packet Identifier) 字段存在於在多個類型的報文里。
(1) 報文標識符
(2)遺願標志(Will Flag)
在可變報文頭的連接標志位字段(Connect Flags)里有三個Will標志位:Will Flag、Will QoS和Will Retain Flag,這些Will字段用於監控客戶端與服務器之間的連接狀況。如果設置了Will Flag,就必須設置Will QoS和Will Retain標志位,消息主體中也必須有Will Topic和Will Message字段。
那遺願消息是怎么回事呢?服務器與客戶端通信時,當遇到異常或客戶端心跳超時的情況,MQTT服務器會替客戶端發布一個Will消息。當然如果服務器收到來自客戶端的DISCONNECT消息,則不會觸發Will消息的發送。
因此,Will字段可以應用於設備掉線后需要通知用戶的場景。
(3)連接保活心跳機制(Keep Alive Timer)
MQTT客戶端可以設置一個心跳間隔時間(Keep Alive Timer),表示在每個心跳間隔時間內發送一條消息。如果在這個時間周期內,沒有業務數據相關的消息,客戶端會發一個PINGREQ消息,相應的,服務器會返回一個PINGRESP消息進行確認。如果服務器在一個半(1.5)心跳間隔時間周期內沒有收到來自客戶端的消息,就會斷開與客戶端的連接。心跳間隔時間最大值大約可以設置為18個小時,0值意味着客戶端不斷開。
1.3、有效載荷(Payload)
Payload直譯為負荷,可能讓人摸不着頭腦,實際上可以理解為消息主體(body)。
當MQTT發送的消息類型是CONNECT(連接)、PUBLISH(發布)、SUBSCRIBE(訂閱)、SUBACK(訂閱確認)、UNSUBSCRIBE(取消訂閱)時,則會帶有負荷。
某些 MQTT 控制報文在報文的最后部分包含一個有效載荷。 對於 PUBLISH 來說有效載荷就是應用消息。
下面表格包含有效載荷的控制報文 列出了需要有效載荷的控制報文。