MQTTClient的使用介紹

開篇


最近在使用MQTTClient實現一個類似于消息推送的服務,說實話,真沒怎么使用過MQTTClient,也不知道這是個啥? 好尷尬 ?? ?? ?? 上網了解了一下,發現MQTT功能挺強(牛)大(逼)。這里我使用的是消息推送服務,通過和服務器端協商,終于能夠與服務器連接,并且能夠收發消息了。 所以,簡單總結了一下,有了這篇文章。

MQTT介紹


  • MQTT
    MQTT基于訂閱者模型架構,客戶端如果互相通信,必須在同一訂閱主題下,即都訂閱了同一個topic,客戶端之間是沒辦法直接通訊的。訂閱模型顯而易見的好處是群發消息的話只需要發布到topic,所有訂閱了這個topic的客戶端就可以接收到消息了。
    發送消息必須發送到某個topic,重點說明的是不管客戶端是否訂閱了該topic都可以向topic發送了消息,還有如果客戶端訂閱了該主題,那么自己發送的消息也會接收到。
  • MQTT特點
    • 使用發布/訂閱消息模式,提供一對多的消息發布,解除應用程序耦合。這一點很類似于XMPP,但是MQTT的信息冗余遠小于XMPP.

    • 對負載內容屏蔽的消息傳輸。
      使用TCP/IP提供網絡連接。主流的MQTT是基于TCP連接進行數據推送的,但是同樣有基于UDP的版本,叫做MQTT-SN。這兩種版本由于基于不同的連接方式,優缺點自然也就各有不同了。

    • 三種消息傳輸方式QoS:

      • 0代表“至多一次”,消息發布完全依賴底層 TCP/IP 網絡。會發生消息丟失或重復。這一級別可用于如下情況,環境傳感器數據,丟失一次讀記錄無所謂,因為不久后還會有第二次發送。
      • 1代表“至少一次”,確保消息到達,但消息重復可能會發生。
      • 2代表“只有一次”,確保消息到達一次。這一級別可用于如下情況,在計費系統中,消息重復或丟失會導致不正確的結果。 (備注:由于服務端采用Mosca實現,Mosca目前只支持到QoS 1)

      如果發送的是臨時的消息,例如給某topic所有在線的設備發送一條消息,丟失的話也無所謂,0就可以了(客戶端登錄的時候要指明支持的QoS級別,同時發送消息的時候也要指明這條消息支持的QoS級別),如果需要客戶端保證能接收消息,需要指定QoS為1,如果同時需要加入客戶端不在線也要能接收到消息,那么客戶端登錄的時候要指定session的有效性,接收離線消息需要指定服務端要保留客戶端的session狀態。

具體MQTT的詳細介紹可以戳這里 https://baike.baidu.com/item/MQTT/3618851?fr=aladdin

MQTTClient的使用


iOS 環境下開發 MQTT 客戶端程序,一般依賴穩定的第三方 FrameWork,由于涉及網絡數據傳輸,建議選擇 Object-c 原生的框架,比如 MQTT-Client-Framework
現在一般常用的有兩個MQTT
1) MQTTKit
2) MQTTClient
不過MQTTKit貌似很長時間不維護了, 使用較多的是MQTTClient。

  • 集成MQTTClient
    MQTT-Client-Framework
    • 用cocopod直接, pod 'MQTTClient'
    • GitHub下載,把相對應的文件夾拖進工程即可

MQTT-Client-FrameWork 包提供的客戶端類有 MQTTSessionMQTTSessionManager,建議使用后者維持靜態資源,而且已經封裝好自動重連等邏輯。初始化時需要傳入相關的網絡參數。

我使用的是第二種, 引入 #import "MQTTClient.h" #import "MQTTSessionManager.h" 頭文件, 遵循 MQTTSessionManagerDelegate協議

使用步驟:

  • 建立連接
    和服務器端確定好MQTT服務器地址,端口號, 用戶名, 密碼, 訂閱主題topic
/**
      host: 服務器地址
      port: 服務器端口
      tls:  是否使用tls協議,mosca是支持tls的,如果使用了要設置成true
      keepalive: 心跳時間,單位秒,每隔固定時間發送心跳包, 心跳間隔不得大于120s
      clean: session是否清除,這個需要注意,如果是false,代表保持登錄,如果客戶端離線了再次登錄就可以接收到離線消息
      auth: 是否使用登錄驗證
      user: 用戶名
      pass: 密碼
      willTopic: 訂閱主題
      willMsg: 自定義的離線消息
      willQos: 接收離線消息的級別
      clientId: 客戶端id,需要特別指出的是這個id需要全局唯一,因為服務端是根據這個來區分不同的客戶端的,默認情況下一個id登錄后,假如有另外的連接以這個id登錄,上一個連接會被踢下線, 我使用的設備UUID
*/
NSString *clientId = [UIDevice currentDevice].identifierForVendor.UUIDString;
MQTTSessionManager *sessionManager = [[MQTTSessionManager alloc] init];
[sessionManager connectTo:@"121.199.19.126"  
                       port:1883
                        tls:false
                  keepalive:60  //心跳間隔不得大于120s
                      clean:true
                       auth:true
                       user:@"guest"
                       pass:@"guest"
                       will:false
                  willTopic:nil
                    willMsg:nil
                    willQos:0
             willRetainFlag:false
               withClientId:clientId];
    sessionManager.delegate = self;
    self.sessionManager = sessionManager;
  • 監控連接狀態
    連接當前狀態,添加對應的回調接口,可以進行相關的業務邏輯處理。
// 添加監聽狀態觀察者
[self.sessionManager addObserver:self
                   forKeyPath:@"state"
                      options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
                      context:nil];

監聽連接狀態,進行相應處理。

 // 監聽當前連接狀態
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    switch (self.sessionManager.state) {
        case MQTTSessionManagerStateClosed:
            NSLog(@"連接已經關閉");
            break;
        case MQTTSessionManagerStateClosing:
            NSLog(@"連接正在關閉");

            break;
        case MQTTSessionManagerStateConnected:
            NSLog(@"已經連接");

            break;
        case MQTTSessionManagerStateConnecting:
            NSLog(@"正在連接中");

            break;
        case MQTTSessionManagerStateError: {
            NSString *errorCode = self.sessionManager.lastErrorCode.localizedDescription;
            NSLog(@"連接異常 ----- %@",errorCode);
        }

            break;
        case MQTTSessionManagerStateStarting:
            NSLog(@"開始連接");
           break;
        default:
            break;
    }
}

  • 接收消息
    實現MQTTSessionManagerDelegate代理方法,處理數據。
// 獲取服務器返回數據
- (void)handleMessage:(NSData *)data onTopic:(NSString *)topic retained:(BOOL)retained {
    NSLog(@"------------->>%@",topic);
    
    NSString *dataString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    NSLog(@"%@",dataString);
    
     // 進行消息處理
}

  • 訂閱和發送消息
// 訂閱主題    NSDictionary類型,Object 為 QoS,key 為 Topic
self.sessionManager.subscriptions = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:MQTTQosLevelExactlyOnce] forKey:@"hello"];
    
// 發送消息   返回值msgid大于0代表發送成功
NSString *msg = @"hahaha";
UInt16 msgid = [self.sessionManager sendData:[msg dataUsingEncoding:NSUTF8StringEncoding] //要發送的消息體
                                      topic:@"hello" //要往哪個topic發送消息
                                        qos:0 //消息級別
                                     retain:false];

如果是使用阿里云的服務器替代自己的服務器,需要在阿里云控制臺申請 TopicGroup ID 等資源。
在建立連接時,傳入的參數值也會有所改變, userpass 由于服務端需要對客戶端進行鑒權,因此需要傳入合法的 userpassuser 設置為當前用戶的 AccessKeypass 則設置為 MQTT 客戶端 GroupID 的簽名字符串,簽名計算方式是使用 SecretKeyGroupIDHmacSHA1 散列加密。

       self.manager = [[MQTTSessionManager alloc] init];
        self.manager.delegate = self;
        self.manager.subscriptions = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:self.qos]
                                                                 forKey:[NSString stringWithFormat:@"%@/#", self.rootTopic]];
        //password的計算方式是,使用secretkey對groupId做hmac簽名算法,具體實現參考macSignWithText方法
        NSString *passWord = [[self class] macSignWithText:self.groupId secretKey:self.secretKey];
        [self.manager connectTo:self.mqttSettings[@"host"]
                                  port:[self.mqttSettings[@"port"] intValue]
                                   tls:[self.mqttSettings[@"tls"] boolValue]
                             keepalive:60  //心跳間隔不得大于120s
                                 clean:true
                                  auth:true
                                  user:self.accessKey
                                  pass:passWord
                                  will:false
                             willTopic:nil
                               willMsg:nil
                               willQos:0
                        willRetainFlag:false
                          withClientId:self.clientId];

使用 SecretKeyGroupIDHmacSHA1 散列加密

+ (NSString *)macSignWithText:(NSString *)text secretKey:(NSString *)secretKey {
    NSData *saltData = [secretKey dataUsingEncoding:NSUTF8StringEncoding];
    NSData *paramData = [text dataUsingEncoding:NSUTF8StringEncoding];
    NSMutableData* hash = [NSMutableData dataWithLength:CC_SHA1_DIGEST_LENGTH ];
    CCHmac(kCCHmacAlgSHA1, saltData.bytes, saltData.length, paramData.bytes, paramData.length, hash.mutableBytes);
    NSString *base64Hash = [hash base64EncodedStringWithOptions:0];
    return base64Hash;
}

具體介紹請戳下面幫助鏈接
MQTT接入環境配置
阿里云接入MTQQ示例
申請MQ資源

參考


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

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,837評論 18 139
  • MQTT-SN的一個重要設計原則是盡可能與MQTT相近。因此,所有的協議語義應保持盡可能與MQTT中定義的一致。接...
    aded3e27ac95閱讀 865評論 0 2
  • 序 本篇會把連接(CONNECT)、心跳(PINGREQ/PINGRESP)、確認(CONNACK)、斷開連接(D...
    技術學習閱讀 9,904評論 0 8
  • MQTT Protocol MQTT協議特性 一句話總結:MQTT是一個簡單,輕量的消息發布/訂閱協議。 MQTT...
    福克斯記閱讀 7,276評論 0 9
  • 酒這玩意兒日怪,給錢不算,還須問到身體賒賬。償還更莫談錢,——他龜兒要的是時間。這不,前晚和阿野還有幾個美女喝酒到...
    湯沅霖閱讀 1,582評論 0 3