iOS MQTT協(xié)議筆記

前言

接到任務(wù)項目需要用MQTT來寫消息推送,經(jīng)過一段時間在網(wǎng)上查看資料后寫下這篇文章,文章內(nèi)容大都來自互聯(lián)網(wǎng),在文章最后也會貼出相關(guān)網(wǎng)址和Demo。寫這文章主要目的是自己總結(jié)下經(jīng)驗做下筆記,以便日后查閱,并希望能幫助一些需要幫助的人。

MQTT簡介

想必能找到我這篇文章的也一定看過不少其它關(guān)于MQTT的文章了,簡介也應(yīng)該能背下來了,我這里就不過多介紹了...(此處省略9999+字)

基于MQTT-Client的iOS客戶端實現(xiàn)

目前iOS的第三方主要有MQTT-ClientMQTTKit和MQTTSDK。因為MQTTKit較長時間沒有維護更新而MQTT-Client維護還是比較頻繁的,所以我選擇了MQTT-Client,至于MQTTSDK我還沒有去了解,有興趣的同學(xué)可以去了解下。使用swift的同學(xué)可以使用CocoaMQTT,這個sdk的作者同時也是服務(wù)端實現(xiàn)emqtt的作者。當然,MQTT-Client也是有swift的。

MQTT-Client支持pod,方便快速集成到工程中。

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.4'
pod 'MQTTClient'
target 'XXXXX' do
end

簡單說下應(yīng)用場景,app在每部安裝的終端上會產(chǎn)生不同的業(yè)務(wù)數(shù)據(jù),而業(yè)務(wù)數(shù)據(jù)需要在各終端app上都要看到,所以需要同步所有終端的業(yè)務(wù)數(shù)據(jù)。mqtt的消息傳輸都是通過topic進行的,topic需要創(chuàng)建,按照mqtt實現(xiàn)沒有單獨的創(chuàng)建topic的方法,topic是和訂閱綁定的。也就是說只要訂閱了一個topic,服務(wù)端首先判斷是否有topic存在,如果存在的話把當前客戶端加入到訂閱列表中,如果不在的話就先創(chuàng)建一個topic,同時把自己添加到訂閱列表中。

topic是和訂閱綁定的.png

建立連接

如果app需要在多個頁面?zhèn)鬏敂?shù)據(jù)建議使用單例模式,建立一個全局的連接,復(fù)用連接。因為每次連接到服務(wù)端也是很消耗資源的。建立連接的代碼如下:

self.manager = [[MQTTSessionManager alloc] init];
        self.manager.delegate = self;
        self.manager.subscriptions = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:MQTTQosLevelExactlyOnce] forKey:[NSString stringWithFormat:@"%@/#", self.base]];
        [self.manager connectTo:@“192.168.1.4” //服務(wù)器地址 
                             port:1883 //服務(wù)端端口號 
                              tls:false //是否使用tls協(xié)議,mosca是支持tls的,如果使用了要設(shè)置成true 
                        keepalive:60 //心跳時間,單位秒,每隔固定時間發(fā)送心跳包 
                            clean:false //session是否清除,這個需要注意,如果是false,代表保持登錄,如果客戶端離線了再次登錄就可以接收到離線消息。注意:QoS為1和QoS為2,并需訂閱和發(fā)送一致 
                             auth:true //是否使用登錄驗證,和下面的user和pass參數(shù)組合使用 
                             user:_userName //用戶名 
                             pass:_passwd //密碼 
                        willTopic:@"" //下面四個參數(shù)用來設(shè)置如果客戶端異常離線發(fā)送的消息,當前參數(shù)是哪個topic用來傳輸異常離線消息,這里的異常離線消息都指的是客戶端掉線后發(fā)送的掉線消息 
                             will:@"" //異常離線消息體。自定義的異常離線消息,約定好格式就可以了 
                          willQos:0 //接收離線消息的級別 0、1、2
                   willRetainFlag:false //只有在為true時,Will Qos和Will Retain才會被讀取,此時消息體payload中要出現(xiàn)Will Topic和Will Message具體內(nèi)容,否則,Will QoS和Will Retain值會被忽略掉
                     withClientId:nil]; //客戶端id,需要特別指出的是這個id需要全局唯一,因為服務(wù)端是根據(jù)這個來區(qū)分不同的客戶端的,默認情況下一個id登錄后,假如有另外的連接以這個id登錄,上一個連接會被踢下線

訂閱和發(fā)送消息

連接一旦建立以后就可以訂閱topic和發(fā)送消息了,訂閱和發(fā)送消息代碼如下:

//訂閱主題。NSDictionary類型,Object 為 QoS,key 為 Topic
self.manager.subscriptions = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:MQTTQosLevelExactlyOnce] forKey:_topic];

//發(fā)送消息,返回值msgid大于0代表發(fā)送成功。(注意:這里說“發(fā)送成功”并不是指“成功發(fā)送給服務(wù)器”(無網(wǎng)絡(luò)情況下也可能會返回大于0))
//msgid:PUBLISH消息的消息標識符。 如果qos為0,則為零。如果qos為1或2,則在消息丟棄時為零
UInt16 msgid = [self.manager sendData:[msg dataUsingEncoding:NSUTF8StringEncoding] //要發(fā)送的消息體 
                                  topic:_topic //要往哪個topic發(fā)送消息 
                                    qos:MQTTQosLevelExactlyOnce //消息級別 
                                 retain:false];

實際消息發(fā)送成功時回調(diào)
僅在使用qos 1或2時有效。(記得設(shè)置delegate)

//MQTTSessionManagerDelegate
/ **在實際已發(fā)送消息時被調(diào)用
  @param msgID傳遞的消息的消息標識符
  @note 僅在使用qos 1或2發(fā)布后調(diào)用此方法
 */
-(void)messageDelivered:(UInt16)msgID{
    
}

獲取訂閱成功的主題:

//注冊一個觀察者,監(jiān)聽effectiveSubscriptions的改變
[self.manager addObserver:self
                  forKeyPath:@"effectiveSubscriptions"
                     options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
                     context:nil];

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if ([keyPath isEqualToString:@"effectiveSubscriptions"]) {
        MQTTSessionManager *manager = (MQTTSessionManager *)object;
        NSLog(@"訂閱成功的主題: %@", manager.effectiveSubscriptions);
        return;
    }
}

接收消息

接收消息有委托實現(xiàn),實現(xiàn)如下委托MQTTSessionManagerDelegate接收消息

/*
 * MQTTSessionManagerDelegate
 */
- (void)handleMessage:(NSData *)data onTopic:(NSString *)topic retained:(BOOL)retained {
    /*
     * MQTTClient: process received message
     */
    NSString *dataString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}

監(jiān)控連接狀態(tài)

注冊一個觀察者,判斷state獲取不同的連接狀態(tài)

//注冊觀察者,記得在離開頁面時移除觀察者
- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [self.manager addObserver:self
                     forKeyPath:@"state"
                        options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
                        context:nil];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    switch (self.manager.state) {
        case MQTTSessionManagerStateClosed: //連接已經(jīng)關(guān)閉
            break;
        case MQTTSessionManagerStateClosing: //連接正在關(guān)閉
            break;
        case MQTTSessionManagerStateConnected: //已經(jīng)連接
            break;
        case MQTTSessionManagerStateConnecting: //正在連接中
            break;
        case MQTTSessionManagerStateError: //異常
            break;
        case MQTTSessionManagerStateStarting: //開始連接
        default:
            break;
    }
}

mqtt協(xié)議本身支持斷線重連,另外單獨說明此sdk在app退出到后臺后自動斷開連接,當回到前臺時會自動重新連接 ! ! !

一些屬性的說明

  • clean:值為false,服務(wù)器必須在客戶端斷開之后繼續(xù)存儲/保持客戶端的訂閱狀態(tài)。這些狀態(tài)包括:
    存儲訂閱的消息QoS1和QoS2消息
    正在發(fā)送消息期間連接丟失導(dǎo)致發(fā)送失敗的消息
    以便當客戶端重新連接時以上消息可以被重新傳遞。
    值為true,服務(wù)器需要立刻清理連接狀態(tài)數(shù)據(jù)。

  • topic:客戶端如果互相通信,必須在同一訂閱主題下,即都訂閱了同一個topic,客戶端之間是沒辦法直接通訊的。訂閱模型顯而易見的好處是群發(fā)消息的話只需要發(fā)布到topic,所有訂閱了這個topic的客戶端就可以接收到消息了。好比QQ群的群號,你訂閱一個topic 就相當于加入了一個群

  • qos:這個代表消息的傳輸方式,QoS說明如下:
    0代表“至多一次”,消息發(fā)布完全依賴底層 TCP/IP 網(wǎng)絡(luò)。會發(fā)生消息丟失或重復(fù)。這一級別可用于如下情況,環(huán)境傳感器數(shù)據(jù),丟失一次讀記錄無所謂,因為不久后還會有第二次發(fā)送。
    1代表“至少一次”,確保消息到達,但消息重復(fù)可能會發(fā)生。
    2代表“只有一次”,確保消息到達一次。這一級別可用于如下情況,在計費系統(tǒng)中,消息重復(fù)或丟失會導(dǎo)致不正確的結(jié)果。 備注:由于服務(wù)端采用Mosca實現(xiàn),Mosca目前只支持到QoS 1
    如果發(fā)送的是臨時的消息,例如給某topic所有在線的設(shè)備發(fā)送一條消息,丟失的話也無所謂,0就可以了(客戶端登錄的時候要指明支持的QoS級別,同時發(fā)送消息的時候也要指明這條消息支持的QoS級別),如果需要客戶端保證能接收消息,需要指定QoS為1,如果同時需要加入客戶端不在線也要能接收到消息,那么客戶端登錄的時候要指定session的有效性,接收離線消息需要指定服務(wù)端要保留客戶端的session狀態(tài)。

  • retain:true:表示發(fā)送的消息需要一直持久保存(不受服務(wù)器重啟影響),不但要發(fā)送給當前的訂閱者,并且以后新來的訂閱了此Topic name的訂閱者會馬上得到推送。
    備注:新來乍到的訂閱者,只會取出最新的一個RETAIN flag = 1的消息推送。
    false:僅僅為當前訂閱者推送此消息。
    假如服務(wù)器收到一個空消息體(zero-length payload)、RETAIN = 1、已存在Topic name的PUBLISH消息,服務(wù)器可以刪除掉對應(yīng)的已被持久化的PUBLISH消息。

  • 連接異常中斷通知機制

在建立連接的時候就把 will(遺囑)寫好保存在服務(wù)器,一旦客戶端出現(xiàn)異常中斷,便會觸發(fā)服務(wù)器發(fā)布Will Message消息到Will Topic主題上去,通知Will Topic訂閱者,對方因異常退出。

  • will Topic:用來傳輸異常離線消息的 topic,這里的異常離線消息都指的是客戶端掉線后發(fā)送的掉線消息
  • will:異常離線消息。自定義的異常離線消息,約定好格式就可以了
  • willQos:和發(fā)布消息固的QoS含義一樣
  • willRetainFlag:只有在為true時,Will Qos和Will Retain才會被讀取,此時消息體中要出現(xiàn)Will Topic和Will Message具體內(nèi)容,否則,Will QoS和Will Retain值會被忽略掉

網(wǎng)址和Demo

-END-
一個公開的mosquitto MQTT測試服務(wù)器 test.mosquitto.org

如果此文章對你有幫助,希望給個??。有什么問題歡迎在評論區(qū)探討。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容