MQTT-SN協議亂翻之功能描述

前言

緊接上文,這是第三篇,主要是對MQTT-SN 1.2協議進行整體性功能描述。html

嗯,這一部分能夠結合着MQTT協議對比着來看。java

網關的廣播和發現

網關只能在成功鏈接到MQTT Server以後,纔可以週期性的在無線我的區域網WPNs內對全部客戶端廣播ADVERTISE消息,便於客戶端被動知道網關的存在。緩存

在同一網絡下,多個擁有不一樣Id的網關可有同時運行中,但會由客戶端根據信號強弱決定鏈接具體網關,不管什麼時候只能鏈接一個網關。服務器

客戶端可維護一份可用網關列表(包含網關地址),在接收到包含有新的網關id的ADVERTISE和GWINFO消息後,其列表須要添加新的網關元素進去。網絡

ADVERTISE廣播消息包含的下一次廣播間隔時長Duration屬性,單位秒,設爲變量T_ADV,應該儘量大與15分鐘(900秒),頻率下降是爲了不低速我的區域網絡的擁塞。併發

針對接收ADVERTISE消息頻率,處理能力較強客戶端能夠用於監督網關是否可用。eg:客戶端連續N_ADV次接收不到某個網關ADVERTISE廣播消息,可認爲此網關經死掉不可用而且從已維護的網關列表中移除。一樣的,做爲備用的網關認爲主網關已掛掉,此時可處於激活狀態,正常發揮做用。spa

網關發送廣播消息ADVERTISE的時間間隔很長,這對致使新加入的客戶端不利,但客戶端能夠直接發送SEARCHGW廣播消息進行查詢網關。大量的新入設備會形成廣播風暴形成網絡擁擠,每個新加入的客戶端在發送SEARCHGW廣播消息以前都須要獲取一個隨機的延遲發送值(0-Tsearchgw),在延遲等待發送期間若接收到其它客戶端發送的SEARCHGW廣播消息,會取消掉本身的SEARCHGW廣播消息發送,等待網關GWINFO消息通知。.net

SEARCHGW消息屬性radius廣播半徑,記爲變量Rb,1跳(1 hop)在通常密集部署下的MQTT-SN客戶端基本可用。code

網關接收到SEARCHGW會即刻回覆包含自身id的GWINFO消息。客戶端收到SEARCHGW後,如有須要延遲發送的SEARCHGW會取消掉,若自身維護一份多個可用網關列表,在等待T_GWINFO時間內沒有收到GWINFO消息,會從列表中取出一條網關信息組裝成GWINFO消息並廣播出去。這就要求客戶端已運行多時,而且維護多個可用網關列表。htm

GWINFO和SEARCHGW所包含半徑radius屬性值一致,這就要求底層網絡在傳輸時進行決定是否須要傳輸到其它類型網絡中。

若沒有接收到響應,SEARCHGW消息可能被從新傳輸。兩個連續的SEARCHGW消息重傳間隔應該呈指數形式增長,避免太密集傳輸。

客戶端的鏈接創建

不管是基於哪種傳輸協議,TCP or UDP,客戶端都須要創建鏈接,而且保持心跳,邏輯上和服務器端保持一條不斷線的雙向通道。下面一張圖,演示了客戶端創建鏈接的過程,而且設定客戶端在CONNECT消息中標誌位字段中遺囑WILL屬性爲true,而後就有了遺囑主題/消息的請求過程。

不少狀況下,鏈接CONNECT是不須要遺囑支持的,網關會直接返回CONNACK消息,但網關會由於擁塞或不支持一些CONNET特性,CONNACK所包含返回代碼字段ReturnCode中包含拒絕代碼,要求客戶端檢查是否鏈接成功,區別對待。好比:

CONNACK消息返回狀態碼爲0x01(Rejected: congestion,因擁塞被拒絕),客戶端須要在T_WAIT時間間隔後進行重試。 

回話清理

已經鏈接的客戶端斷線後,若以前在CONNECT中沒有設置過會話清理(Clean Session)標識,那麼以前的訂閱等信息在網關處將會持久存在。相比MQTT,MQTT-SN中的「Clean Session」標識被擴展到遺囑特性中。在CONNECT消息中,CleanSession和Will組合將會產生如下效果:

  • CleanSession=true, Will=true: 網關將會刪除以前對應的全部訂閱和遺囑,新的遺囑主題/消息稍後即將從新處理
  • CleanSession=true, Will=false: 網關將會刪除以前對應的全部訂閱和遺囑,返回CONNACK消息
  • CleanSession=false, Will=true: 網關將繼續持有以前對應的全部訂閱,新的遺囑主題/消息稍後即將從新處理
  • CleanSession=false, Will=false: 網關將會繼續持有以前對應的全部訂閱和遺囑等數據,並返回CONNACK消息

更新遺囑流程

  • CONNEECTION中標誌位Will中設置是否須要更新遺囑主題/消息
  • 空WILLTOPIC(兩個字節)消息將會促使網關刪除對應遺囑數據
  • WILLTOPICUPD/WILLMSGUPD能夠更新/修改遺囑主題、遺囑消息
  • 空白WILLTOPICUPD(兩個字節)消息意味着請求網清空對應已有的遺囑數據

主題註冊流程

受限於無線傳感器網絡的有限帶寬和微小消息負載,PUBLISH消息中不可以包含完整的主題名稱topic name。這就須要客戶端和網關之間經過註冊流程,獲取主題名稱對應的(16位的天然數)topic id,而後塞入PUBLISH消息的topicId屬性中。

客戶端發送REGISTER消息,網關返回REGACK消息,其所包含的ReturenCode屬性決定註冊成功與否:

  • ReturnCode = 「accepted」,topicId能夠很愉快的使用在稍後的PUBLISH消息中
  • ReturnCode = 「rejected: congestion」,客戶端須要稍等一段時間(T_WAIT表示,大於5分鐘)再次從新註冊
  • ReturnCode = 「rejected: invalid topic ID/not supported」,客戶端須要稍做調整,再次從新註冊

任意時間,只能執行一個REGISTER消息,有沒有完成註冊流程,須要等待。

網關->客戶端方向,網關發送REGISTER消息給通知客戶端指定topicId對應某個主題,以便後面發送PUBLISH消息使用。若客戶端在訂閱SUBSCRIBE消息時使用了通配符(#/+),那麼與之相匹配的topic name也將被一一通知到。所以不建議使用通配符,較爲低效。

客戶端發佈流程

客戶端一旦獲取到topic name對應topic id,就能夠直接發送PUBLISH消息了。這和MQTT協議相比,PUBLISH消息中Topic Name被替換成Topic Id,除此以外,還要注意ReturnCode:

  • ReturnCode = 「rejected: congestion」,客戶端須要稍等一段時間(>5分鐘)後再次重試
  • ReturnCode = 「rejected: invalid topic ID」,客戶端須要從新註冊topic name獲取topic id,而後再次從新發布

QoS 1和 QoS 2在任一時間,都必須等待已有PUBLISH消息完成,才能進行下面的PUBLISH消息發佈流程。

預約義topic id和兩個字符的topic name

預約義的topic id已提早指派好對應的topic name,須要客戶端和網關在代碼層級支持,省略了中間註冊流程,在鏈接創建以後能夠立刻進行PUBLISH消息,但這須要在PUBLISH標誌Flags字段中設置TopicIdType值爲0b01(0b10表示兩個字節長度的短topic name)。雖然能夠快速發送PUBLISH消息,但客戶端想訂閱預約義的topic id,接收對應的PUBLISH消息,同樣須要發送SUBSCRIBLE消息請求進行訂閱。若亂指定預約義topic id,會收到ReturnCode=「Rejection: invalid topic Id」的異常。

預約義的短topic name只有兩個字符長度的字符串(也是兩個字節),topic id爲兩個字節表示的一個天然數(0-65535),二者使用場景一致,都須要在標誌位Flags設置TopicIdType具體值,0b01表示預約義topic id,0b10表示兩個字節長度的短topic name,須要分清。

PUBLISH對應QoS -1值

這對僅僅支持PUBLISH QoS -1的很是簡單的客戶端實現而言,除此以外不支持任何特性。它不關心鏈接是否創建,也沒有註冊、訂閱這一說,按照已經固化到代碼中的網關地址直接發送PUBLISH消息,不關心網關地址是否正確、網關是否存活、消息是否發送成功。

下面的PUBLISH屬性值依賴於QoS -1的狀況:

  • QoS標誌,被置爲0b11
  • TopicIdType標誌,多是(預約義topic id)0b01也多是(短topic name)0b10
  • TopicId字段,預約義topic id或短topic name
  • Data字段,須要發送的數據,沒啥變化

客戶端的訂閱和退訂

客戶端對某個主題感興趣,能夠發起SUBSCRIBLE流程,攜帶上感興趣的主題名(topic id),服務器通常會返回包含有指定主題Id(topic id)的SUBACK消息。訂閱失敗,能夠從PUBACK的ReturnCode中獲知:

  • ReturnCode = 「rejected: congestion」,客戶端須要稍等一段時間T_WAIT(>5分鐘)後再次重試

有一種狀況是SUBSCRIBLE訂閱主題包含通配符,網關的處理就很簡單,在SUBACK中返回的topic id爲0x0000。稍後,網關向客戶端發送REGISTER消息走註冊流程,通知通配符匹配到的主題對應的topic id值。

來自客戶端的SUBSCRIBLE消息同樣支持預約義topic id,以及短topic name,這和PUBLISH消息差很少。

退訂就很簡單,客戶端發送UNSUBSCRIBLE消息,網關返回UNSUBACK消息。

但同一時刻,客戶端只容許處理訂閱SUBSCRIBLE或取消訂閱UNSUBSCRIBLE按照串行化順序,下一個操做依賴於上一個操做徹底成功。

網關發佈流程

服務器發佈流程和客戶端相似,在發佈以前須要檢測其主題是否已經向客戶端提早註冊過,若無須要把主題和指定的topic id放入REGISTER消息中發送給客戶端進行註冊流程,而後等待客戶端處理結果REGACK。註冊經過,而後才能正常發送PUBLISH消息。

網關須要確保REGISTER的主題以及PUBLISH消息的內容負載都不能太長超過當前網絡負載上限(好比在ZigBee環境下不能超過60個字節),取消註冊/發佈流程就行了。

網關發佈PUBLISH消息時,客戶端檢測到未知的topic id,把拒絕理由封裝到PUBACK後,網關遇到ReturnCode=「Rejected: invalid Topic ID」非法topic id,須要考慮刪除或從新註冊。

客戶端或許會拒絕其註冊,或許會不容許PUBLISH消息,網關如上靜默處理就行了,失敗就失敗了,不須要告知別人。

客戶端發佈流程於此相似,須要在發佈以前進行主題註冊以獲取指定的topic id,提交PUBLISH消息後,一樣須要檢查PUBACK所包含的ReturnCode字段是接受仍是拒絕,因網絡擁塞而產生的拒絕,客戶端須要在T_WAIT時間後再次重試。

客戶端的發佈必須是串行方式,下一個須要發送到PUBLISH消息須要等待上一個發送成功被網關接受以後才能進行處理。

心跳保活流程

通常是客戶端->網關,網關->客戶端也沒有問題。但要求PINGREQ -> PINGRESP 必定要單個時針循環,PINGREQ發送者不能也是PINGRESP的發送者,那樣不但亂了流程,也浪費了網絡資源。嗯,不容許雙向互發。

客戶端可基於心跳機制監測已鏈接網關健康與否,連續屢次接收不到來自網關的PINGRESP消息後,客戶端鏈接下一個可替換的網關。由於客戶端的鏈接和心跳和其它客戶端狀態屬性不一樣步,但這可能會帶來一個問題,同一時間如有大量的客戶端洪水般同時鏈接一個網關,網關可能毫無徵兆的會被沖垮掉。這就要求網關要有批量的鏈接處理能力,併發特性加強才行。

客戶端斷線流程

客戶端主動發送DISCONNECT消息告知網關須要斷線以後,如有交換信息的須要能夠從新發起一個新的會話鏈接。DISCONNECT消息以後,網關不會清理掉已有訂閱和遺囑數據,除非在以前的CONNECT消息中已硬性設置了CleanSession會話清理標識爲true。網關接收到DISCONNECT消息以後會返回一個DISCONNECT消息做爲響應。

有一種狀況是客戶端會忽然接收到來自網關的DISCONNECT消息,這也許是網關自身發生了異常錯誤,或網關沒法定位客戶端的消息歸屬(客戶端的消息和客戶端沒法關聯到一塊兒),此時客戶端須要發送CONNECT消息重建與網關的會話鏈接。

客戶端重傳流程

客戶端->網關的消息都是單路傳播的,這依賴於客戶端所持有的已鏈接網關的單播地址。

客戶端發送一個消息以後,須要啓動一個重試定時器Tretry和一個重試計數器Nretry用以監督網關消息響應。定時器會被客戶端在指定時間內接收到來自網關的消息後取消掉,若沒有準時接收到則會觸發定時器執行消息重發流程,連續Nretry次重發後,客戶端會直接取消掉當前流程,判斷當前網關已經斷線,須要鏈接到另一個可用的網關。假如另外的網關也是鏈接失敗,會嘗試重連以前的網關。

若在休眠狀態下,一旦超太重試計數器值,客戶端直接進入休眠狀態。

客戶端休眠支持策略

這裏所說的客戶端指的是依賴電池驅動的電子設備,你要明白一個事實,節省電池資源是多麼的重要,省電就是關鍵,沒電了就沒得玩了嘛。當不處於激活狀態時爲了省電就得須要進入睡眠/休眠狀態,當有數據須要接收或發送時就能夠醒過來。網關嘛須要追蹤設備的休眠狀態而且支持緩存須要發送給休眠設備的消息,在設備喚醒時一一發送。

下面是客戶端的狀態轉換圖,很清晰描述了各類狀態之間的交互: 

客戶端具備五種狀態:激活(active),休眠(asleep),喚醒(awake),斷線(disconnected),丟失(lost),每次只能是其中一種。

網關須要監督客戶端的狀態,開始於CONNECT消息中存活時長字段(keep alive),在大於存活時長時間內網關接收不到來自客戶端消息,網關認爲客戶端已經處於丟失狀態(lost),會激活對應的遺囑特性若存在的話。

客戶端發送DISCONNECT消息但沒有duration休眠時長字段,網關這將處於沒有時間監督的斷線狀態。一旦包含duration休眠時長字段,表示客戶端須要休眠一段時間,網關這客戶端被轉換爲休眠狀態,休眠時長爲duration所定義在值。超過此休眠時長的一段時間內,網關若接收不到客戶端發送過來的任何消息,那麼客戶端會被轉化爲丟失狀態,若已設置遺囑特性,此時遺囑特性會生效。客戶端休眠期間須要被髮送的消息都會被網關緩存。

睡眠狀態下流程圖會更形象的說明流程: 

毫無疑問,網關可以使用一個休眠定時器維護客戶端的休眠狀態等,休眠定時器會被停掉當網關接收到客戶端發送過的PINGREQ消息,網關從PINGREQ消息所包含的Client Id檢索是否存在已緩存的PUBLISH消息,如有會一一按照順序發送到客戶端。全部對應已緩存消息發送完畢後,會隨之發送一個PINGRESP消息。若沒有緩存消息,網關直接返回一個PINGRESP消息。網關會從新啓動休眠定時器,網關維護的客戶端狀態被轉換爲休眠狀態,客戶端在接收到PINGRESP消息以後,將直接轉向休眠狀態,節省用電。

客戶端在喚醒狀態下處理消息,遵照「客戶端重傳流程」行爲,一旦達到重試計數器限制,將進入睡眠狀態。

客戶端從休眠狀態轉向喚醒狀態用於檢查網關是否爲其緩存消息時,須要發送一個PINGREQ消息到網關;從休眠/喚醒狀態轉換爲激活狀態,須要發送一個CONNECT消息告知網關;轉換爲斷線狀態時須要發送兩個字節的DISCONNECT(沒有休眠時長字段duration)消息;須要從新定義的休眠時長,發送一個DISCONNECT消息(包含新的duration時長值)通知網關便可。

小結

功能性描述介紹完了,基本上MQTT-SN協議介紹已接近尾聲,最後面的篇章就是短短的實現描述了。

 

原文 http://www.blogjava.net/yongboy/archive/2015/01/09/422156.html

相關文章
相關標籤/搜索