MQTT由Andy Stanford-Clark(IBM)和Arlen Nipper(Eurotech,現爲Cirrus Link)於1999年開發,用於監測穿越沙漠的石油管道。目標是擁有一個帶寬有效且使用不多電池電量的協議,由於這些設備是經過衛星鏈路鏈接的,當時這種設備很是昂貴。 與HTTP及其請求/響應範例相比,該協議使用發佈/訂閱體系結構。html
與HTTP的區別在於客戶端沒必要提取所需的信息,可是在有新內容的狀況下,代理會將信息推送到客戶端。所以,每一個MQTT客戶端都與代理具備永久打開的TCP鏈接。若是此鏈接在任何狀況下中斷,MQTT代理能夠緩衝全部消息,並在它從新聯機時將它們發送到客戶端。 如前所述,MQTT中用於分派消息的核心概念是主題。主題是一個簡單的字符串,能夠有更多的層次結構級別,用斜槓分隔。用於發送起居室的溫度數據的示例主題能夠是房屋/起居室/溫度。一方面,客戶端能夠訂閱確切的主題,或者另外一方面使用通配符。對房屋/ + /溫度的訂閱將致使全部消息發送到先前提到的主題房屋/起居室/溫度以及在起居室的地方具備任意值的任何主題,例如房屋/廚房/溫度。加號是單級通配符,只容許一個層次結構的任意值。若是您須要訂閱多個級別,例如訂閱整個子樹,還有一個多級通配符(#)。它容許訂閱全部底層層次結構級別。好比房子/#訂閱以house開頭的全部主題。git
如下內容須要你對照着MQTT協議內容仔細推敲github
推薦資源:緩存
MQTT協議中文版: https://mcxiaoke.gitbooks.io/mqtt-cn/content/mqtt/01-Introduction.html服務器
MQTT Version 3.1.1: http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html網絡
結構 | 備註 |
---|---|
Fixed header | 固定報頭,全部控制報文都包含 |
Variable header | 可變報頭,部分控制報文包含 |
Payload | 有效載荷,部分控制報文包含 |
+-----+-----+-----+-----+-----+------+------+------+-------+
| | | | | | | | | |
| Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-----------+-----+-----+------------+------+------+-------+
| | | |
|byte1|MQTT ControlPacket type| Flags |
+----------------------------------------------------------+
| | |
|byte2| Remaining Length |
+----------------------------------------------------------+
名字 | 值 | 報文流動方向 | 描述 |
---|---|---|---|
Reserved | 0 | 禁止 | 保留 |
CONNECT | 1 | 客戶端到服務端 | 客戶端請求鏈接服務端 |
CONNACK | 2 | 服務端到客戶端 | 鏈接報文確認 |
PUBLISH | 3 | 兩個方向都容許 | 發佈消息 |
PUBACK | 4 | 兩個方向都容許 | QoS 1消息發佈收到確認 |
PUBREC | 5 | 兩個方向都容許 | 發佈收到(保證交付第一步) |
PUBREL | 6 | 兩個方向都容許 | 發佈釋放(保證交付第二步) |
PUBCOMP | 7 | 兩個方向都容許 | QoS 2消息發佈完成(保證交互第三步) |
SUBSCRIBE | 8 | 客戶端到服務端 | 客戶端訂閱請求 |
SUBACK | 9 | 服務端到客戶端 | 訂閱請求報文確認 |
UNSUBSCRIBE | 10 | 客戶端到服務端 | 客戶端取消訂閱請求 |
UNSUBACK | 11 | 服務端到客戶端 | 取消訂閱報文確認 |
PINGREQ | 12 | 客戶端到服務端 | 心跳請求 |
PINGRESP | 13 | 服務端到客戶端 | 心跳響應 |
DISCONNECT | 14 | 客戶端到服務端 | 客戶端斷開鏈接 |
Reserved | 15 | 禁止 | 保留 |
控制報文 | 固定報頭標誌 | 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 |
Bit3 | Bit2 | Bit1 | Bit0 |
---|---|---|---|
DUP | Qos | Qos | RETAIN |
DUP =控制報文的重複分發標誌架構
QoS = PUBLISH報文的服務質量等級性能
RETAIN = PUBLISH報文的保留標誌ui
備註:編碼
服務質量等級Qos:位置:第1個字節,第2-1位。這個字段表示應用消息分發的服務質量等級保證。
QoS值 | Bit 2 | Bit 1 | 描述 |
---|---|---|---|
0 | 0 | 0 | 最多分發一次 |
1 | 0 | 1 | 至少分發一次 |
2 | 1 | 0 | 只分發一次 |
- | 1 | 1 | 保留位 |
位置:從第2個字節開始。
剩餘長度(Remaining Length)表示當前報文剩餘部分的字節數,包括可變報頭和負載的數據。剩餘長度不包括用於編碼剩餘長度字段自己的字節數。
某些MQTT控制報文包含一個可變報頭部分。它在固定報頭和負載之間。可變報頭的內容根據報文類型的不一樣而不一樣。可變報頭的報文標識符(Packet Identifier)字段存在於在多個類型的報文裏。這個在後續的MQTT各個控制報文中進行手撕。
某些MQTT控制報文在報文的最後部分包含一個有效載荷,對於PUBLISH來講有效載荷就是應用消息。
包含有效載荷的控制報文Control Packets that contain a Payload
控制報文 | 有效載荷 |
---|---|
CONNECT | 須要 |
CONNACK | 不須要 |
PUBLISH | 可選 |
PUBACK | 不須要 |
PUBREC | 不須要 |
PUBREL | 不須要 |
PUBCOMP | 不須要 |
SUBSCRIBE | 須要 |
SUBACK | 須要 |
UNSUBSCRIBE | 須要 |
UNSUBACK | 不須要 |
PINGREQ | 不須要 |
PINGRESP | 不須要 |
DISCONNECT | 不須要 |
客戶端到服務端的網絡鏈接創建後,客戶端發送給服務端的第一個報文必須是CONNECT報文 。
在一個網絡鏈接上,客戶端只能發送一次CONNECT報文。服務端必須將客戶端發送的第二個CONNECT報文看成協議違規處理並斷開客戶端的鏈接。
有效載荷包含一個或多個編碼的字段。包括客戶端的惟一標識符,Will主題,Will消息,用戶名和密碼。除了客戶端標識以外,其它的字段都是可選的,基於標誌位來決定可變報頭中是否須要包含這些字段。
CONNECT報文的可變報頭按下列次序包含四個字段:協議名(Protocol Name),協議級別(Protocol Level),鏈接標誌(Connect Flags)和保持鏈接(Keep Alive)。
協議名是表示協議名 MQTT 的UTF-8編碼的字符串。MQTT規範的後續版本不會改變這個字符串的偏移和長度。
若是協議名不正確服務端能夠斷開客戶端的鏈接,也能夠按照某些其它規範繼續處理CONNECT報文。對於後一種狀況,按照本規範,服務端不能繼續處理CONNECT報文
客戶端用8位的無符號值表示協議的修訂版本。對於3.1.1版協議,協議級別字段的值是4(0x04)。
若是發現不支持的協議級別,服務端必須給發送一個返回碼爲0x01(不支持的協議級別)的CONNACK報文響應CONNECT報文,而後斷開客戶端的鏈接 。
鏈接標誌字節包含一些用於指定MQTT鏈接行爲的參數。它還指出有效載荷中的字段是否存在
+-------+---------+----------+--------+------+-------+-------+--------+--------+
| | | | | | | | | |
| Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+--------------------------------------------+---------------------------------+
| |User Name|Password | Will | | Will | Clean | |
| | Flag | Flag | Retain | Will Qos | Flag |Session |Reser^ed|
+--------------------------------------------+---------------------------------+
| | | | | | | | | |
| byte8 | X | X | X | X | X | X | X | 0 |
+-------+---------+----------+--------+------+-------+-------+--------+--------+
服務端必須驗證CONNECT控制報文的保留標誌位(第0位)是否爲0,若是不爲0必須斷開客戶端鏈接 。
位置:鏈接標誌字節的第1位
這個二進制位指定了會話狀態的處理方式。
客戶端和服務端能夠保存會話狀態,以支持跨網絡鏈接的可靠消息傳輸。這個標誌位用於控制會話狀態的生存時間。
位置:鏈接標誌的第2位。
遺囑標誌(Will Flag)被設置爲1,表示若是鏈接請求被接受了,遺囑(Will Message)消息必須被存儲在服務端而且與這個網絡鏈接關聯。以後網絡鏈接關閉時,服務端必須發佈這個遺囑消息,除非服務端收到DISCONNECT報文時刪除了這個遺囑消息
位置:鏈接標誌的第4和第3位。
這兩位用於指定發佈遺囑消息時使用的服務質量等級。
位置:鏈接標誌的第5位。
若是遺囑消息被髮布時須要保留,須要指定這一位的值。
位置:鏈接標誌的第7位。
若是用戶名(User Name)標誌被設置爲0,有效載荷中不能包含用戶名字段 。
若是用戶名(User Name)標誌被設置爲1,有效載荷中必須包含用戶名字段 。
位置:鏈接標誌的第6位。
若是密碼(Password)標誌被設置爲0,有效載荷中不能包含密碼字段 。
若是密碼(Password)標誌被設置爲1,有效載荷中必須包含密碼字段 。
若是用戶名標誌被設置爲0,密碼標誌也必須設置爲0 。
保持鏈接(Keep Alive)是一個以秒爲單位的時間間隔,表示爲一個16位的字,它是指在客戶端傳輸完成一個控制報文的時刻到發送下一個報文的時刻,二者之間容許空閒的最大時間間隔。
客戶端負責保證控制報文發送的時間間隔不超過保持鏈接的值。若是沒有任何其它的控制報文能夠發送,客戶端必須發送一個PINGREQ報文 。
CONNECT報文的有效載荷(payload)包含一個或多個以長度爲前綴的字段,可變報頭中的標誌決定是否包含這些字段。若是包含的話,必須按這個順序出現:客戶端標識符,遺囑主題,遺囑消息,用戶名,密碼 。
網絡鏈接創建後,若是服務端在合理的時間內沒有收到CONNECT報文,服務端應該關閉這個鏈接。
服務端必須按照3.1節的要求驗證CONNECT報文,若是報文不符合規範,服務端不發送CONNACK報文直接關閉網絡鏈接 [MQTT-3.1.4-1]。
服務端能夠檢查CONNECT報文的內容是否是知足任何進一步的限制,能夠執行身份驗證和受權檢查。若是任何一項檢查沒經過,按照3.2節的描述,它應該發送一個適當的、返回碼非零的CONNACK響應,而且必須關閉這個網絡鏈接。
服務端發送CONNACK報文響應從客戶端收到的CONNECT報文。服務端發送給客戶端的第一個報文必須是CONNACK。
若是客戶端在合理的時間內沒有收到服務端的CONNACK報文,客戶端應該關閉網絡鏈接。合理 的時間取決於應用的類型和通訊基礎設施。
剩餘長度字段
表示可變報頭的長度。對於CONNACK報文這個值等於2。
第1個字節是 鏈接確認標誌,位7-1是保留位且必須設置爲0。 第0 (SP)位 是當前會話(Session Present)標誌。
位置:鏈接確認標誌的第0位。
位置:可變報頭的第2個字節。
鏈接返回碼字段使用一個字節的無符號值。若是服務端收到一個合法的CONNECT報文,但出於某些緣由沒法處理它,服務端應該嘗試發送一個包含非零返回碼(表格中的某一個)的CONNACK報文。若是服務端發送了一個包含非零返回碼的CONNACK報文,那麼它必須關閉網絡鏈接 。
值 | 返回碼響應 | 描述 |
---|---|---|
0 | 0x00鏈接已接受 | 鏈接已被服務端接受 |
1 | 0x01鏈接已拒絕,不支持的協議版本 | 服務端不支持客戶端請求的MQTT協議級別 |
2 | 0x02鏈接已拒絕,不合格的客戶端標識符 | 客戶端標識符是正確的UTF-8編碼,但服務端不容許使用 |
3 | 0x03鏈接已拒絕,服務端不可用 | 網絡鏈接已創建,但MQTT服務不可用 |
4 | 0x04鏈接已拒絕,無效的用戶名或密碼 | 用戶名或密碼的數據格式無效 |
5 | 0x05鏈接已拒絕,未受權 | 客戶端未被受權鏈接到此服務器 |
6-255 | 保留 |
CONNACK報文沒有有效載荷。
PUBLISH控制報文是指從客戶端向服務端或者服務端向客戶端傳輸一個應用消息。
DUP =控制報文的重複分發標誌
QoS = PUBLISH報文的服務質量等級
RETAIN = PUBLISH報文的保留標誌
可變報頭按順序包含主題名和報文標識符。
主題名(Topic Name)用於識別有效載荷數據應該被髮布到哪個信息通道。
主題名必須是PUBLISH報文可變報頭的第一個字段。它必須是 1.5.3節定義的UTF-8編碼的字符串。
PUBLISH報文中的主題名不能包含通配符 。
服務端發送給訂閱客戶端的PUBLISH報文的主題名必須匹配該訂閱的主題過濾器(根據 4.7節定義的匹配過程)。
只有當QoS等級是1或2時,報文標識符(Packet Identifier)字段才能出如今PUBLISH報文中。2.3.1節提供了有關報文標識符的更多信息。
有效載荷包含將被髮布的應用消息。
數據的內容和格式是應用特定的。有效載荷的長度這樣計算:用固定報頭中的剩餘長度字段的值減去可變報頭的長度。包含零長度有效載荷的PUBLISH報文是合法的。
PUBLISH報文的接收者必須按照根據PUBLISH報文中的QoS等級發送響應。
服務質量等級 | 預期響應 |
---|---|
QoS 0 | 無響應 |
QoS 1 | PUBACK報文 |
QoS 2 | PUBREC報文 |
PUBACK報文是對QoS 1等級的PUBLISH報文的響應。
剩餘長度字段
表示可變報頭的長度。對PUBACK報文這個值等於2.
包含等待確認的PUBLISH報文的報文標識符。
PUBACK報文沒有有效載荷。
PUBREC報文是對QoS等級2的PUBLISH報文的響應。它是QoS 2等級協議交換的第二個報文。
剩餘長度字段
表示可變報頭的長度。對PUBREC報文它的值等於2。
可變報頭包含等待確認的PUBLISH報文的報文標識符。
PUBREC報文沒有有效載荷。
MqttMessage pubRecMessage = MqttMessageFactory.newMessage( new MqttFixedHeader(MqttMessageType.PUBREC, false, MqttQoS.AT_MOST_ONCE, false, 2), MqttMessageIdVariableHeader.from(variableHeader.messageId()), null);
PUBREL報文是對PUBREC報文的響應。它是QoS 2等級協議交換的第三個報文。
PUBREL控制報文固定報頭的第3,2,1,0位是保留位,必須被設置爲0,0,1,0。服務端必須將其它的任何值都當作是不合法的並關閉網絡鏈接 。
剩餘長度字段
表示可變報頭的長度。對PUBREL報文這個值等於2.
可變報頭包含與等待確認的PUBREC報文相同的報文標識符。
PUBREL報文沒有有效載荷。
MqttMessage pubRelMessage = MqttMessageFactory.newMessage( new MqttFixedHeader(MqttMessageType.PUBREC, false, MqttQoS.AT_LEAST_ONCE, false, 2), MqttMessageIdVariableHeader.from(variableHeader.messageId()), null);
PUBCOMP報文是對PUBREL報文的響應。它是QoS 2等級協議交換的第四個也是最後一個報文。
剩餘長度字段
表示可變報頭的長度。對PUBCOMP報文這個值等於2。
可變報頭包含與等待確認的PUBREL報文相同的報文標識符。
PUBCOMP報文沒有有效載荷。
MqttMessage pubCompMessage = MqttMessageFactory.newMessage( new MqttFixedHeader(MqttMessageType.PUBCOMP, false, MqttQoS.AT_MOST_ONCE, false, 2), MqttMessageIdVariableHeader.from(variableHeader.messageId()), null);
客戶端向服務端發送SUBSCRIBE報文用於建立一個或多個訂閱。每一個訂閱註冊客戶端關心的一個或多個主題。爲了將應用消息轉發給與那些訂閱匹配的主題,服務端發送PUBLISH報文給客戶端。SUBSCRIBE報文也(爲每一個訂閱)指定了最大的QoS等級,服務端根據這個發送應用消息給客戶端。
剩餘長度字段
等於可變報頭的長度(2字節)加上有效載荷的長度。
可變報頭包含報文標識符。
SUBSCRIBE報文的有效載荷包含了一個主題過濾器列表,它們表示客戶端想要訂閱的主題。
SUBSCRIBE報文有效載荷中的主題過濾器列表必須是1.5.3節定義的UTF-8字符串 [MQTT-3.8.3-1]。
服務端應該支持包含通配符(4.7.1節定義的)的主題過濾器。若是服務端選擇不支持包含通配符的主題過濾器,必須拒絕任何包含通配符過濾器的訂閱請求 [MQTT-3.8.3-2]。
每個過濾器後面跟着一個字節,這個字節被叫作 服務質量要求(Requested QoS)。它給出了服務端向客戶端發送應用消息所容許的最大QoS等級。
SUBSCRIBE報文的有效載荷必須包含至少一對主題過濾器 和 QoS等級字段組合。沒有有效載荷的SUBSCRIBE報文是違反協議的 [MQTT-3.8.3-3]。有關錯誤處理的信息請查看4.8節。
請求的最大服務質量等級字段編碼爲一個字節,它後面跟着UTF-8編碼的主題名,那些主題過濾器 /和QoS等級組合是連續地打包。
當前版本的協議沒有用到服務質量要求(Requested QoS)字節的高六位。若是有效載荷中的任何位是非零值,或者QoS不等於0,1或2,服務端必須認爲SUBSCRIBE報文是不合法的並關閉網絡鏈接 。
服務端發送SUBACK報文給客戶端,用於確認它已收到而且正在處理SUBSCRIBE報文。
SUBACK報文包含一個返回碼清單,它們指定了SUBSCRIBE請求的每一個訂閱被授予的最大QoS等級。
剩餘長度字段
等於可變報頭的長度加上有效載荷的長度。
可變報頭包含等待確認的SUBSCRIBE報文的報文標識符。
有效載荷包含一個返回碼清單。每一個返回碼對應等待確認的SUBSCRIBE報文中的一個主題過濾器。返回碼的順序必須和SUBSCRIBE報文中主題過濾器的順序相同 。
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
返回碼 | ||||||||
byte 1 | X | 0 | 0 | 0 | 0 | 0 | X | X |
容許的返回碼值:
0x00 - 最大QoS 0
0x01 - 成功 – 最大QoS 1
0x02 - 成功 – 最大 QoS 2
0x80 - Failure 失敗
0x00, 0x01, 0x02, 0x80以外的SUBACK返回碼是保留的,不能使用。
客戶端發送UNSUBSCRIBE報文給服務端,用於取消訂閱主題。
UNSUBSCRIBE報文固定報頭的第3,2,1,0位是保留位且必須分別設置爲0,0,1,0。
即:
DUP =控制報文的重複分發標誌 : false
QoS = PUBLISH報文的服務質量等級: Qos1(至少分發一次)
RETAIN = PUBLISH報文的保留標誌: false
服務端必須認爲任何其它的值都是不合法的並關閉網絡鏈接 。
剩餘長度字段
等於可變報頭的長度加上有效載荷的長度。
UNSUBSCRIBE報文的有效載荷包含客戶端想要取消訂閱的主題過濾器列表。UNSUBSCRIBE報文中的主題過濾器必須是連續打包的。
UNSUBSCRIBE報文的有效載荷必須至少包含一個消息過濾器。沒有有效載荷的UNSUBSCRIBE報文是違反協議的。
UNSUBSCRIBE報文提供的主題過濾器(不管是否包含通配符)必須與服務端持有的這個客戶端的當前主題過濾器集合逐個字符比較。若是有任何過濾器徹底匹配,那麼它(服務端)本身的訂閱將被刪除,不然不會有進一步的處理 。
若是服務端刪除了一個訂閱:
它必須中止分發任何新消息給這個客戶端 。
它必須完成分發任何已經開始往客戶端發送的QoS 1和QoS 2的消息。
它能夠繼續發送任何現存的準備分發給客戶端的緩存消息。
服務端必須發送UNSUBACK報文響應客戶端的UNSUBSCRIBE請求。UNSUBACK報文必須包含和UNSUBSCRIBE報文相同的報文標識符 。即便沒有刪除任何主題訂閱,服務端也必須發送一個UNSUBACK響應 。
若是服務端收到包含多個主題過濾器的UNSUBSCRIBE報文,它必須如同收到了一系列的多個UNSUBSCRIBE報文同樣處理那個報文,除了將它們的響應合併到一個單獨的UNSUBACK報文外。
服務端發送UNSUBACK報文給客戶端用於確認收到UNSUBSCRIBE報文。
剩餘長度字段
表示可變報頭的長度,對UNSUBACK報文這個值等於2。
可變報頭包含等待確認的UNSUBSCRIBE報文的報文標識符。
UNSUBACK報文沒有有效載荷。
MqttUnsubAckMessage unsubAckMessage = (MqttUnsubAckMessage)MqttMessageFactory.newMessage( new MqttFixedHeader(MqttMessageType.UNSUBACK, false, MqttQoS.AT_MOST_ONCE, false, 2), MqttMessageIdVariableHeader.from(msg.variableHeader().messageId()), null);
客戶端發送PINGREQ報文給服務端的。用於:
在沒有任何其它控制報文從客戶端發給服務的時,告知服務端客戶端還活着。
請求服務端發送 響應確認它還活着。
使用網絡以確認網絡鏈接沒有斷開。
保持鏈接(Keep Alive)處理中用到這個報文。
PINGREQ報文沒有可變報頭。
PINGREQ報文沒有有效載荷。
服務端必須發送 PINGRESP報文響應客戶端的PINGREQ報文
MqttMessage pingReqMessage = MqttMessageFactory.newMessage( new MqttFixedHeader(MqttMessageType.PINGREQ, false,MqttQoS.AT_MOST_ONCE, false, 0), null, null);
服務端發送PINGRESP報文響應客戶端的PINGREQ報文。表示服務端還活着。保持鏈接(Keep Alive)處理中用到這個報文。
PINGRESP報文沒有可變報頭。
PINGRESP報文沒有有效載荷。
MqttMessage pingRespMessage = MqttMessageFactory.newMessage( new MqttFixedHeader(MqttMessageType.PINGREQ, false,MqttQoS.AT_MOST_ONCE, false, 0), null, null);
DISCONNECT報文是客戶端發給服務端的最後一個控制報文。表示客戶端正常斷開鏈接。
DISCONNECT報文沒有可變報頭。
DISCONNECT報文沒有有效載荷。
客戶端發送DISCONNECT報文以後:
必須關閉網絡鏈接 。
不能經過那個網絡鏈接再發送任何控制報文 。
服務端在收到DISCONNECT報文時:
必須丟棄任何與當前鏈接關聯的未發佈的遺囑消息。
應該關閉網絡鏈接,若是客戶端 尚未這麼作。
MqttMessage disConnectMessage = MqttMessageFactory.newMessage( new MqttFixedHeader(MqttMessageType.DISCONNECT, false,MqttQoS.AT_MOST_ONCE, false,0), null, null);
關於Netty實現高性能IOT服務器(Groza)之手撕MQTT協議篇上詳解到這裏就結束了。
原創不易,若是感受不錯,但願給個推薦!您的支持是我寫做的最大動力!
下文會帶你們推動Netty實現MQTT協議的IOT服務器。
版權聲明:
做者:穆書偉
博客園出處:https://www.cnblogs.com/sanshengshui
github出處:https://github.com/sanshengshui
我的博客出處: