MQTT協議筆記之鏈接和心跳

前言

本篇會把鏈接(CONNECT)、心跳(PINGREQ/PINGRESP)、確認(CONNACK)、斷開鏈接(DISCONNECT)和在一塊兒。php

CONNECT

像前面所說,MQTT有關字符串部分採用的修改版的UTF-8編碼,CONNECT可變頭部中協議名稱、消息體都是採用修改版的UTF-8編碼。前面基本上可變頭部內容很少,下面是一個較爲完整的CONNECT消息結構:html

  Description 7 6 5 4 3 2 1 0
Fixed header/固定頭部
    Message Type(1) DUP flag QoS level RETAIN
byte 1
  0 0 0 1 x x x x
byte 2 Remaining Length
Variable header/可變頭部
Protocol Name
byte 1 Length MSB (0) 0 0 0 0 0 0 0 0
byte 2 Length LSB (6) 0 0 0 0 0 1 1 0
byte 3 'M' 0 1 0 0 1 1 0 1
byte 4 'Q' 0 1 0 1 0 0 0 1
byte 5 'I' 0 1 0 0 1 0 0 1
byte 6 's' 0 1 1 1 0 0 1 1
byte 7 'd' 0 1 1 0 0 1 0 0
byte 8 'p' 0 1 1 1 0 0 0 0
Protocol Version Number
byte 9 Version (3) 0 0 0 0 0 0 1 1
Connect Flags
  User Name Flag Password Flag Will Retain Will QoS Will Flag Clean Session Reserved
byte 10
1 1 0 0 1 1 1 x
Keep Alive timer
byte 11 Keep Alive MSB (0) 0 0 0 0 0 0 0 0
byte 12 Keep Alive LSB (10) 0 0 0 0 1 0 1 0
Payload/消息體

Client Identifier(客戶端ID)java

1-23個字符長度,客戶端到服務器的全局惟一標誌,若是客戶端ID超出23個字符長度,服務器須要返回碼爲2,標識符被拒絕響應的CONNACK消息。
處理QoS級別1和2的消息ID中,可使用到。
必填項。

Will Topic服務器

Will Flag值爲1,這裏即是Will Topic的內容。QoS級別經過Will QoS字段定義,RETAIN值經過Will RETAIN標識,都定義在可變頭裏面。

Will Message微信

Will Flag若設爲1,這裏即是Will Message定義消息的內容,對應的主題爲Will Topic。若是客戶端意外的斷開觸發服務器PUBLISH此消息。
長度有可能爲0。
在CONNECT消息中的Will Message是UTF-8編碼的,當被服務器發佈時則做爲二進制的消息體。

User Name網絡

若是設置User Name標識,能夠在此讀取用戶名稱。通常可用於身份驗證。協議建議用戶名爲很少於12個字符,不是必須。

Passwordsession

若是設置Password標識,即可讀取用戶密碼。建議密碼爲12個字符或者更少,但不是必須。

可變頭部

協議名稱和協議版本都是固定的。ide

鏈接標誌(Connect Flags)

一個字節表示,除了第1位是保留未使用,其它7位都具備不一樣含義。編碼

業務上很重要,對消息整體流程影響很大,須要牢記。spa

Clean Session

0,表示若是訂閱的客戶機斷線了,要保存爲其要推送的消息(QoS爲1和QoS爲2),若其從新鏈接時,需將這些消息推送(若客戶端長時間不鏈接,須要設置一個過時值)。 1,斷線服務器即清理相關信息,從新鏈接上來以後,會再次訂閱。

Will Flag

定義了客戶端(沒有主動發送DISCONNECT消息)出現網絡異常致使鏈接中斷的狀況下,服務器須要作的一些措施。

簡而言之,就是客戶端預先定義好,在本身異常斷開的狀況下,所留下的最後遺願(Last Will),也稱之爲遺囑(Testament)。 這個遺囑就是一個由客戶端預先定義好的主題和對應消息,附加在CONNECT的可變頭部中,在客戶端鏈接出現異常的狀況下,由服務器主動發佈此消息。

只有在Will Flag位爲1時,Will Qos和Will Retain纔會被讀取,此時消息體payload中要出現Will Topic和Will Message具體內容,不然,Will QoS和Will Retain值會被忽略掉。

Will Qos

兩位表示,和PUBLISH消息固定頭部的QoS level含義同樣。這裏先掠過,到PUBLISH消息再回過頭來看看,會更明白些。

若標識了Will Flag值爲1,那麼Will QoS就會生效,不然會被忽略掉。

Will RETAIN

若是設置Will Flag,Will Retain標誌就是有效的,不然它將被忽略。

當客戶端意外斷開服務器發佈其Will Message以後,服務器是否應該繼續保存。這個屬性和PUBLISH固定頭部的RETAIN標誌含義同樣,這裏先掠過。

User name 和 password Flag:

用於受權,二者要麼爲0要麼爲1,不然都是無效。都爲0,表示客戶端可自由鏈接/訂閱,都爲1,表示鏈接/訂閱須要受權。

Payload/消息體

消息體定義的消息順序(如上表所示),約定俗成,不得更改,不然將可能引發混亂。

若Will Flag值爲0,那麼在payload中,Client Identifer後面就不會存在Will Topic和Will Message內容。

若User Name和Password都爲0,意味着Payload/消息體中,找不到User Name和password的值,就算有,也是無效。標誌決定着是否讀取與否。

心跳時間(Keep Alive timer)

以秒爲單位,定義服務器端從客戶端接收消息的最大時間間隔。通常應用服務會在業務層次檢測客戶端網絡是否鏈接,不是TCP/IP協議層面的心跳機制(好比開啓SOCKET的SO_KEEPALIVE選項)。 通常來說,在一個心跳間隔內,客戶端發送一個PINGREQ消息到服務器,服務器返回PINGRESP消息,完成一次心跳交互,繼而等待下一輪。若客戶端沒有收到心跳反饋,會關閉掉TCP/IP端口鏈接,離線。 16位兩個字節,可看作一個無符號的short類型值。最大值,2^16-1 = 65535秒 = 18小時。最小值能夠爲0,表示客戶端不斷開。通常設爲幾分鐘,好比微信心跳週期爲300秒。

Will Message編碼

Will Message在CONNECT Payload/息體中,使用UTF-8編碼。假設內容爲「abcd」,大概以下:

  Description 7 6 5 4 3 2 1 0
byte 1 Length MSB (0) 0 0 0 0 0 0 0 0
byte 2 Length LSB (4) 0 0 0 0 0 1 0 0
byte 3 'a' (0x61) 0 1 1 0 0 0 0 1
byte 4 'b' (0x62) 0 1 1 0 0 0 1 0
byte 5 'c' (0x63) 0 1 1 0 0 0 1 1
byte 6 'd' (0x64) 0 1 1 0 0 1 0 0

有一點須要記住,PUBLISH的Payload/消息體中以二進制編碼保存。

某刻客戶端異常關閉觸發服務器會PUBLISH此消息。那麼服務器會直接把byte3-byte6之間字符取出,保存爲二進制,附加到PUBLISH消息體中,大概存儲以下:

  Description 7 6 5 4 3 2 1 0
byte 1 'a' (0x61) 0 1 1 0 0 0 0 1
byte 2 'b' (0x62) 0 1 1 0 0 0 1 0
byte 3 'c' (0x63) 0 1 1 0 0 0 1 1
byte 4 'd' (0x64) 0 1 1 0 0 1 0 0

另外,MQTT 3.1協議對Will message的說明很容易引發誤解,3.1.1草案已經獲得修正。

相關說明:

http://mqtt.org/wiki/doku.php/willmessageutf8_support

https://tools.oasis-open.org/issues/browse/MQTT-2

鏈接異常中斷通知機制

CONNECT消息一旦設置在可變頭部設置了Will flag標記,那就啓用了Last-Will-And-Testament特性,此特性很贊。

一旦客戶端出現異常中斷,便會觸發服務器發佈Will Message消息到Will Topic主題上去,通知Will Topic訂閱者,對方因異常退出。

接收CONNECT後的響應動做

接收到CONNECT消息以後,服務器應該返回一個CONNACK消息做爲響應:

  1. 若客戶端繞過CONNECT消息直接發送其它類型消息,服務器應關閉此非法鏈接 若客戶端發送CONNECT以後未收到CONNACT,須要關閉當前鏈接,而後從新鏈接
  2. 相同Client ID客戶端已鏈接到服務器,先前客戶端必須斷開鏈接後,服務器才能完成新的客戶端CONNECT鏈接 客戶端發送無效非法CONNECT消息,服務器須要關閉

CONNACK

一個完整的CONNACK消息大體以下:

  Description 7 6 5 4 3 2 1 0
Fixed header/固定頭部
byte 1   Message type (2) DUP flag QoS flags RETAIN
    0 0 1 0 x x x x
byte 2   Remaining Length (2)
    0 0 0 0 0 0 1 0
Variable header/可變頭部
Topic Name Compression Response
byte 1 Reserved values. Not used. x x x x x x x x
Connect Return Code
byte 2 Return Code                

可變頭部第一個字節爲保留,無甚用處。第二個字節爲鏈接握手返回碼:

返回值 16進制 含義
0 0x00 Connection Accepted
1 0x01 Connection Refused: unacceptable protocol version
2 0x02 Connection Refused: identifier rejected
3 0x03 Connection Refused: server unavailable
4 0x04 Connection Refused: bad user name or password
5 0x05 Connection Refused: not authorized
6-255   Reserved for future use

只有0-5目前被使用到,其餘值有待往後使用。通常返回值爲0x00,表示鏈接創建。非法的請求,須要返回相應的數值。

從上面看出,一個CONNACT,四個字節表示。一個正常的CONNACT消息實際內容可能以下: 0x20 0x02 0x00 0x00

如果在私有協議中,兩個字節就足夠了。

不少時候,客戶端和服務器端在沒有消息傳遞時,會一直保持着鏈接。雖然不能依靠TCP心跳機制(好比SO_KEEPALIVE選項),業務層面定義心跳機制,會讓鏈接狀態檢測、控制更爲直觀。

PINGREQ

由客戶端發送到服務器端,證實本身還在一直鏈接着呢。兩個字節,固定值。

  Description 7 6 5 4 3 2 1 0
Fixed header/固定頭部
byte 1   Message type (12) DUP flag QoS flags RETAIN
    1 1 0 0 x x x x
byte 2   Remaining Length (0)
    0 0 0 0 0 0 0 0

客戶端會在一個心跳週期內發送一條PINGREQ消息到服務器端。

心跳頻率在CONNECT可變頭部「Keep Alive timer」中定義時間,單位爲秒,無符號16位short表示。

PINGRESP

服務器收到PINGREQ請求以後,會當即響應一個兩個字節固定格式的PINGRESP消息。

  Description 7 6 5 4 3 2 1 0
Fixed header/固定頭部
byte 1   Message type (13) DUP flag QoS flags RETAIN
    1 1 0 1 x x x x
byte 2   Remaining Length (0)
    0 0 0 0 0 0 0 0

服務器通常若在1.5倍的心跳週期內接收不到客戶端發送的PINGREQ,可考慮關閉客戶端的鏈接描述符。此時的關閉鏈接的行爲和接收到客戶端發送DISCONNECT消息的處理行爲一致,但對客戶端的訂閱不會產生影響(不會清除客戶端訂閱數據),這個須要牢記。

若客戶端發送PINGREQ以後的一個心跳週期內接收不到PINGRESP消息,可考慮關閉TCP/IP套接字鏈接。

DISCONNECT

客戶端主動發送到服務器端,代表即將關閉TCP/IP鏈接。此時要求服務器要完整、乾淨的進行斷開處理,不能僅僅相似於關閉鏈接描述符相似草草處理之。 須要兩個字節,值固定:

  Description 7 6 5 4 3 2 1 0
Fixed header/固定頭部
byte 1   Message type (14) DUP flag QoS flags RETAIN
    1 1 1 0 x x x x
byte 2   Remaining Length (0)
    0 0 0 0 0 0 0 0

服務器要根據先前此客戶端在發送CONNECT消息可變頭部Connect flag中的「Clean session flag」所設置值,再次複習一下:

  1. 值爲0,服務器必須在客戶端斷開以後繼續存儲/保持客戶端的訂閱狀態。這些狀態包括:

    • 存儲訂閱的消息QoS1和QoS2消息
    • 正在發送消息期間鏈接丟失致使發送失敗的消息
    • 以便當客戶端從新鏈接時以上消息能夠被從新傳遞。
  2. 值爲1,服務器須要馬上清理鏈接狀態數據。

有一點須要牢記,服務器在接收到客戶端發送的DISCONNECT消息以後,須要主動關閉TCP/IP鏈接。

 

原文 http://www.blogjava.net/yongboy/archive/2014/02/09/409630.html

相關文章
相關標籤/搜索