背景
當有訂閱者訂閱了有關的主題以后,通過發布消息的消息的動作,可以讓訂閱者收到對應主題的消息。
根據不同的QoS 等級,通信的動作也略有不同。
PUBLISH – 發布消息 報文
PUBLISH控制報文是指從客戶端向服務端或者服務端向客戶端傳輸一個應用消息。
- 客戶端使用PUBLISH報文發送應用消息給服務端,目的是分發到其它訂閱匹配的客戶端。
- 服務端使用PUBLISH報文發送應用消息給每一個訂閱匹配的客戶端。
客戶端使用帶通配符的主題過濾器請求訂閱時,客戶端的訂閱可能會重復,因此發布的消息可能會匹配多個過濾器。對於這種情況,服務端必須將消息分發給所有訂閱匹配的QoS等級最高的客戶端。服務端之后可以按照訂閱的QoS等級,分發消息的副本給每一個匹配的訂閱者。
收到一個PUBLISH報文時,接收者的動作取決於QoS等級。
如果服務端實現不授權某個客戶端發布PUBLISH報文,它沒有辦法通知那個客戶端。它必須按照正常的QoS規則發送一個正面的確認,或者關閉網絡連接。
PUBLISH 固定頭
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | MQTT控制報文類型 (0x3) | DUP | QoS | RETAIN | ||||
0 | 0 | 1 | 1 | X | X | X | X | |
byte 2... | 剩余長度 |
下面我們直接看各個報文類型標志位
重發標志 DUP
保證消息可靠傳輸,默認為0,只占用一個字節,表示第一次發送。不能用於檢測消息重復發送等。
只適用於客戶端或服務器端嘗試重發PUBLISH, PUBREL, SUBSCRIBE 或 UNSUBSCRIBE消息,注意需要滿足以下條件:
1)當QoS > 0
2)消息需要回復確認
位置:byte1[3]
如果DUP標志被設置為0:表示這是客戶端或服務端第一次請求發送這個PUBLISH報文。對於QoS 0的消息,DUP標志必須設置為0。
如果DUP標志被設置為1:表示這可能是一個早前報文請求的重發。
客戶端或服務端請求重發一個PUBLISH報文時,必須將DUP標志設置為1。
服務端發送PUBLISH報文給訂閱者時,收到(入站)的PUBLISH報文的DUP標志的值不會被傳播。發送(出站)的PUBLISH報文與收到(入站)的PUBLISH報文中的DUP標志是獨立設置的,它的值必須單獨的根據發送(出站)的PUBLISH報文是否是一個重發來確定。
非規范評注
接收者收到一個DUP標志為1的控制報文時,不能假設它看到了一個這個報文之前的一個副本。
需要特別指出的是,DUP標志關注的是控制報文本身,與它包含的應用消息無關。當使用QoS 1時,客戶端可能會收到一個DUP標志為0的PUBLISH報文,這個報文包含一個它之前收到過的應用消息的副本,但是用的是不同的報文標識符。
服務質量等級 QoS
這個字段表示應用消息分發的服務質量等級保證。
關於 QoS 等級 與 流程可以參考 :《Qos等級 與 會話》
位置:byte1[2:1] 。
QoS值 | Bit 2 | Bit 1 | 描述 |
---|---|---|---|
0 | 0 | 0 | 最多分發一次 |
1 | 0 | 1 | 至少分發一次 |
2 | 1 | 0 | 只分發一次 |
- | 1 | 1 | 保留位 |
PUBLISH報文不能將QoS所有的位設置為1。如果服務端或客戶端收到QoS所有位都為1的PUBLISH報文,它必須關閉網絡連接。
當QoS設置為1時,客戶端或服務器發布消息時,需要得到對方的確認(PUBACK),如果一段時間后沒收到PUBACK,那么會再次發送當前消息,並將DUP字段標記為1。
保留標志 RETAIN
用來設定一條消息是否為保留消息;如果是,那么 服務端必須存儲這個應用消息和它的服務質量等級(QoS),以便它可以被分發給未來的主題名匹配的訂閱者。一個新的訂閱建立時,對每個匹配的主題名,如果存在最近保留的消息,它必須被發送給這個訂閱者。
參考:《Retained(保留消息) 和LWT(最后遺囑)》
位置:byte1[0] 。
如果服務端收到一條保留(RETAIN)標志為1的QoS 0消息,它必須丟棄之前為那個主題保留的任何消息。它應該將這個新的QoS 0消息當作那個主題的新保留消息,但是任何時候都可以選擇丟棄它 — 如果這種情況發生了,那個主題將沒有保留消息。有關存儲狀態的更多信息見 4.1節。
服務端發送PUBLISH報文給客戶端時,如果消息是作為客戶端一個新訂閱的結果發送,它必須將報文的保留標志設為1 [MQTT-3.3.1-8]。當一個PUBLISH報文發送給客戶端是因為匹配一個已建立的訂閱時,服務端必須將保留標志設為0,不管它收到的這個消息中保留標志的值是多少。
保留標志為1且有效載荷為零字節的PUBLISH報文會被服務端當作正常消息處理,它會被發送給訂閱主題匹配的客戶端。此外,同一個主題下任何現存的保留消息必須被移除,因此這個主題之后的任何訂閱者都不會收到一個保留消息。當作正常 意思是現存的客戶端收到的消息中保留標志未被設置。服務端不能存儲零字節的保留消息。
如果客戶端發給服務端的PUBLISH報文的保留標識為0,服務端不能存儲這個消息也不能移除或替換任何現存的保留消息。
通俗來講:之前發的消息A是帶有保留標識位(值1)的,如果此后又發了消息B(沒有帶保留標志位的),那么保留消息還是A(而不是B)。
對於發布者不定期發送狀態消息這個場景,保留消息 常用在 新的訂閱者希望會收到某主題最近的狀態。
PUBLISH 的 可變頭
可變報頭按順序包含主題名(Topic Name)
和報文標識符(Packet Identifier)
。
主題名 Topic Name
主題名(Topic Name)用於識別有效載荷數據應該被發布到哪一個信息通道。
主題名必須是PUBLISH報文可變報頭的第一個字段。
二進制位 | 7-0 |
---|---|
byte 1 | 字符串長度的最高有效字節(MSB) |
byte 2 | 字符串長度的最低有效字節(LSB) |
byte 3 … | 如果長度大於0,這里是UTF-8編碼的字符數據。 |
PUBLISH報文中的主題名不能包含通配符。
服務端發送給訂閱客戶端的PUBLISH報文的主題名必須匹配該訂閱的主題過濾器。
報文標識符 Packet Identifier
只有當QoS等級是1或2時,報文標識符(Packet Identifier)字段才能出現在PUBLISH報文中。
報文標識符用來區分報文,特別是在重發的報文中用來標識是否是同一個報文,並在需要應答的場景中用於確定是對哪個發送報文的應答。可變報頭的報文標識符(Packet Identifier)字段存在於在多個類型的報文里(占用2個字節)。這些報文是:
PUBLISH(QoS > 0時)
, PUBACK
,PUBREC
,PUBREL
,PUBCOMP
,SUBSCRIBE,
SUBACK
,UNSUBSCRIBE
,UNSUBACK
。
Bit | 7 - 0 |
---|---|
byte 1 | 報文標識符 MSB |
byte 2 | 報文標識符 LSB |
Packet ID默認是從1(0x01)開始並自增,最大為255(0xff)。
SUBSCRIBE
,UNSUBSCRIBE
和PUBLISH(QoS大於0)
控制報文必須包含一個非零的16位報文標識符(Packet Identifier)。
- 客戶端每次發送一個新的這些類型的報文時都必須分配一個當前未使用的報文標識符。
- 如果一個客戶端要重發這個特殊的控制報文,在隨后重發那個報文時,它必須使用相同的標識符。
當客戶端處理完這個報文對應的確認(ACK, CMP)后,這個報文標識符就釋放可重用。
例如:QoS 1的PUBLISH對應的是
PUBACK
,QoS 2的PUBLISH對應的是PUBCOMP
,與SUBSCRIBE或UNSUBSCRIBE對應的分別是SUBACK
或UNSUBACK
。
發送一個QoS 0的PUBLISH報文時,相同的條件也適用於服務端。
QoS等於0的PUBLISH報文不能包含報文標識符。
PUBACK
, PUBREC
, PUBREL
報文必須包含與最初發送的PUBLISH報文相同的報文標識符。類似地,SUBACK
和UNSUBACK
必須包含在對應的SUBSCRIBE和UNSUBSCRIBE報文中使用的報文標識符。
PUBLISH 的 有效載荷
有效載荷包含將被發布的應用消息
,即:數據的內容和格式是應用特定的。
包含零長度有效載荷的PUBLISH報文是合法的。
有效載荷的長度這樣計算:用固定報頭中的剩余長度字段的值減去可變報頭的長度。
實際上,應該說是
固定報頭中的剩余長度字段的值 = 可變報頭的長度 + 有效荷載
因為只有確定了可變頭與有效荷載的長度,才可以計算固定報頭中的剩余長度的值。
PUBLISH報文的 響應
PUBLISH 報文的接收者必須按照根據PUBLISH報文中的QoS等級發送響應,見下面表格的描述。
表格 3.4 – PUBLISH報文的預期響應
服務質量等級 | 預期響應 |
---|---|
QoS 0 | 無響應 |
QoS 1 | PUBACK報文 |
QoS 2 | PUBREC報文 |
PUBLISH 響應
不同的Qos 等級會導致不同的通信流程。
關於 QoS 等級 與 流程可以參考 :《Qos等級 與 會話》
PUBLISH報文 中的 Packet Identifier 是什么,下面 的 Packet Identifier便是什么。
PUBACK - 發布確認報文 (QoS 1)
PUBACK 報文 比較簡單,它是對QoS 1等級的PUBLISH報文的響應。
PUBACK 報文的 組成 (沒有 有效載荷) = 一個固定頭(0x40 0x02) + Packet Identifier (from PUBLISH's Packet Identifier)。
PUBREC – 發布收到報文 (QoS 2,第一步)
PUBREC報文是對QoS等級2的PUBLISH報文的響應。
PUBREC 報文的 組成 (沒有 有效載荷) = 一個固定頭(0x50 0x02) + Packet Identifier (from PUBLISH's Packet Identifier)。
PUBREL – 發布釋放(QoS 2,第二步)
PUBREL報文是對PUBREC報文的響應。
PUBREL 報文的 組成 (沒有 有效載荷) = 一個固定頭(0x52 0x02) + Packet Identifier (from PUBLISH's Packet Identifier)。
PUBREL控制報文固定報頭的第3,2,1,0位是保留位,必須被設置為0,0,1,0。
PUBCOMP – 發布完成(QoS 2,第三步)
PUBCOMP報文是對PUBREL報文的響應。
PUBCOMP 報文的 組成 (沒有 有效載荷) = 一個固定頭(0x70 0x02) + Packet Identifier (from PUBLISH's Packet Identifier)。