MQTT協議的基礎概念

1、MQTT協議簡介

MQTT 是什么

MQTT 的全稱為 Message Queue Telemetry Transport,是在 1999 年,由 IBM 的 Andy Stanford-Clark 和 Arcom 的 Arlen Nipper 為了一個通過衛星網絡連接輸油管道的項目開發的。為了滿足低電量消耗和低網絡帶寬的需求,MQTT 協議在設計之初就包含了以下一些特點:
  • 實現簡單
  • 提供數據傳輸的 QoS
  • 輕量、占用帶寬低
  • 可傳輸任意類型的數據
  • 可保持的會話(session)
之后 IBM 一直將 MQTT 作為一個內部協議在其產品中使用,直到 2010 年,IBM 公開發布了 MQTT 3.1 版本。在 2014 年,MQTT 協議正式成為了 OASIS(結構化信息標準促進組織)的標準協議。隨著多年的發展,MQTT 協議的重點也不再只是嵌入式系統,而是更廣泛的物聯網(Internet of Things)世界了。
MQTT 協議是什么?簡單地來說 MQTT 協議有以下特性:
  • 基于 TCP 協議的應用層協議;
  • 采用 C/S 架構;
  • 使用訂閱/發布模式,將消息的發送方和接受方解耦;
  • 提供 3 種消息的 QoS(Quality of Service): 至多一次,最少一次,只有一次;
  • 收發消息都是異步的,發送方不需要等待接收方應答。
雖然 MQTT 協議名稱有 Message Queue 兩個詞,但是它并不是一個像 RabbitMQ 那樣的一個消息隊列,這是初學者最容易搞混的一個問題。MQTT 跟傳統的消息隊列相比,有以下一些區別:
  1. 在傳統消息隊列中,在發送消息之前,必須先創建相應的隊列;在 MQTT 中,不需要預先創建要發布的主題(可訂閱的 Topic);
  2. 在傳統消息隊列中,未被消費的消息總是會被保存在某個隊列中,直到有一個消費者將其消費;在 MQTT 中,如果發布一個沒有被任何客戶端訂閱的消息,這個消息將被直接扔掉;
  3. 在傳統消息隊列中,一個消息只能被一個客戶端獲取,在 MQTT 中,一個消息可以被多個訂閱者獲取,MQTT 協議也不支持指定消息被單一的客戶端獲取。
  4. MQTT 協議可以為大量的低功率、工作網絡環境不可靠的物聯網設備提供通訊保障。而它的應用范圍也不僅如此,在移動互聯網領域也大有作為:很多 Android App 的推送功能,都是基于 MQTT 實現的,也有一些 IM 的實現,是基于 MQTT 的。

2、MQTT協議的通信模型

MQTT的通信是通過發布/訂閱的方式來實現的,消息的發布方和訂閱方通過這種方式來進行解耦,它們沒有直接的連接,它們需要一個中間方。在 MQTT 里面我們稱之為 Broker,用來進行消息的存儲和轉發。一次典型的 MQTT 消息通信流程如下所示:
MQTT 消息通信流程
  1. 發布方將消息發送到Broker
  2. Broker 接受到消息以后,檢查下都有哪些訂閱方訂閱了此類消息,然后將消息發送到這些訂閱方
  3. 訂閱方從 Broker 獲取該消息

3、MQTT Client

任何終端、服務器、嵌入式設備等只要運行了MQTT的庫或者代碼,我們都可以稱之為MQTT Client。當然,任何的發布方(Publisher)和訂閱方(Subscriber)也都是Client,Publisher 或者 Subscriber 只取決于該 Client 當前的狀態——是在發布還是在訂閱消息,并且一個Client可以同時是Publisher 或者 Subscriber。

MQTT Client 庫在很多語言中都有實現,包括 Android、Arduino、Ruby、C、C++、C#、Go、iOS、Java、JavaScript,以及 .NET 等。如果你要查看相應語言的庫實現,可以在這里找到。

4、MQTT Broker

Broker 是整個MQTT 發布/訂閱 的核心,它接收 Publisher 發布的消息,并把消息傳遞給訂閱過此消息主題的 Subscriber。在實際應用中,一個 MQTT Broker 還應該提供以下一些功能:

  • 可以橫向擴展,比如集群,來滿足大量的 Client 接入
  • 可以擴展接入業務系統
  • 易于監控,滿足高可用性

在國內我們可以使用騰訊云、阿里云、青云之類的云服務商提供的MQTT 服務

5、MQTT 協議數據包

MQTT 協議的數據包格式非常簡單,一個 MQTT 協議數據包由下面三個部分組成:

  • 固定頭(Fixed header):存在于所有的 MQTT 數據包中,用于表示數據包類型及對應標識,表明數據包大小;
  • 可變頭(Variable header):存在于部分類型的 MQTT 數據包中,具體內容由相應類型的數據包決定;
  • 消息體(Payload):存在于部分 MQTT 數據包中,存儲消息的具體數據。

接下來看一下固定頭的格式:

Bit | 7 | 6 | 5 | 4 | | 3 | 2 | 1 | 0 |
字節 1 MQTT 數據包類型 MQTT 數據包 Flag,內容由數據包類型指定
字節 2…… 數據包剩余長度 ---

固定頭的第一個字節的高 4 位 bit 用于指定該數據包的類型,MQTT 的數據包有以下一些類型:

名稱 方向 描述
Reserved 0 不可用 保留位
CONNECT 1 Client 到 Broker Client 請求連接到 Broker
CONNACK 2 Broker 到 Client 連接確認
PUBLISH 3 雙向 發布消息
PUBACK 4 雙向 發布確認
PUBREC 5 雙向 發布收到
PUBREL 6 雙向 發布釋放
PUBCOMP 7 雙向 發布完成
SUBSCRIBE 8 Client 到 Broker Client 請求訂閱
SUBACK 9 Broker 到 Client 訂閱確認
UNSUBSCRIBE 10 Client 到 Broker Client 請求取消訂閱
UNSUBACK 11 Broker 到 Client 取消訂閱確認
PINGREQ 12 Client 到 Broker PING 請求
PINGRESP 13 Broker 到 Client PING 應答
DISCONNECT 14 Client 到 Broker Client 主動中斷連接
Reserved 15 不可用 保留位

固定頭的低 4 位 bit 用于指定數據包的 Flag,不同的數據包類型,其 Flag 的定義是不一樣的,每種數據包對應的 Flag 如下:

數據包 標識位 Bit 3 Bit 2 Bit 1 Bit 0
CONNECT 保留位 0 0 0 0
CONNACK 保留位 0 0 0 0
PUBLISH MQTT 3.1.1 使用 DUP QoS QoS RETAIN
PUBACK 保留位 0 0 0 0
PUBREC 保留位 0 0 0 0
PUBREL 保留位 0 0 0 0
PUBCOMP 保留位 0 0 0 0
SUBSCRIBE 保留位 0 0 0 0
SUBACK 保留位 0 0 0 0
UNSUBSCRIBE 保留位 0 0 0 0
UNSUBACK 保留位 0 0 0 0
PINGREQ 保留位 0 0 0 0
PINGRESP 保留位 0 0 0 0
DISCONNECT 保留位 0 0 0 0

從固定頭的第 2 字節開始是用于標識 MQTT 數據包長度的字段,最少一個字節,最大四個字節,每一個字節的低 7 位用于標識值,范圍為 0~127。最高位的 1 位是標識位,用來說明是否有后續字節來標識長度。例如:標識為 0,代表為沒有后續字節;標識為 1,代表后續還有一個字節用于標識包長度。MQTT 協議規定最多可以用四個字節來標識包長度。

所以這四個字節最多可以標識的包長度為:(0xFF, 0xFF, 0xFF, 0x7F) = 268435455 字節,約 256M,這個是 MQTT 協議中數據包的最大長度。
注意: 1、Remain Length 的值不包含固定頭的大小,包括第 1 字節和 Remain Length 字段。 2、本文章中 MQTT 協議版本為 3.1.1

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容