基於mqtt的消息推送(三)客戶端實現


MQTT簡介

mqtt基於訂閱者模型架構,客戶端如果互相通信,必須在同一訂閱主題下,即都訂閱了同一個topic,客戶端之間是沒辦法直接通訊的。訂閱模型顯而易見的好處是群發消息的話只需要發布到topic,所有訂閱了這個topic的客戶端就可以接收到消息了。

發送消息必須發送到某個topic,重點說明的是不管客戶端是否訂閱了該topic都可以向topic發送了消息,還有如果客戶端訂閱了該主題,那么自己發送的消息也會接收到。

另外需要重點說明的是QoS,這個代表消息的傳輸方式,QoS說明如下:

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

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

基於MQTT-Client-Framework的IOS客戶端實現

現有客戶端sdk分析,基本分為兩大類:一類移植自C類庫,如Mosquitto,一類是用objc或者swift原生實現。
各種sdk對比如下,我選用的是MQTT-Client,使用swift的同學可以使用CocoaMQTT,這個sdk的作者同時也是服務端實現emqtt的作者。

Name Type Programming Language Code
Paho Original C Open-Source. Eclipse project
IBM Original C Close Source. IBM SDK
Mosquitto Original C Open-Source. Eclipse project
MQTTKit Wrapper (Mosquitto) Objective-C Open-Source. Github
Marquette Wrapper (Mosquitto) Objective-C Open-Source. Github
Moscapsule Wrapper (Mosquitto) Swift Open-Source. Github
Musqueteer Wrapper (Mosquitto) Objective-C
MQTT-Client Native Objective-C Open-Source. Github
MQTTSDK Native Objective-C
CocoaMQTT Native Swift Open-Source. Github

可能的問題如下所述,蘋果禁止第三方網絡庫使用移動網絡。

If you are trying to use MQTT for an iOS application I will highly recommend you to use a native (Objective-C/Swift) iOS library. Using C or wrapper libraries usually means you are using POSIX networking calls at some point. Apple forbids the use of third party networking libraries from using the mobile internet antenna. Thus if you use Paho or something similar, you can only use MQTT when you are connected to a WiFi network.
源引自https://github.com/relayr/apple-mqtt-example

集成步驟

MQTT-Client支持pod,方便快速集成到工程中。簡單說下應用場景,app在每部安裝的終端上會產生不同的業務數據,而業務數據需要在各終端app上都要看到,所以需要同步所有終端的業務數據。mqtt的消息傳輸都是通過topic進行的,topic需要創建,按照mqtt實現沒有單獨的創建topic的方法,topic是和訂閱綁定的。也就是說只要訂閱了一個topic,服務端首先判斷是否有topic存在,如果存在的話把當前客戶端加入到訂閱列表中,如果不在的話就先創建一個topic,同時把自己添加到訂閱列表中。

建立連接

如果app需要在多個頁面傳輸數據建議使用單例模式,建立一個全局的連接,復用連接。因為每次連接到服務端也是很消耗資源的。建立連接的代碼如下:

1
MQTTSessionManager *sessionManager = [[MQTTSessionManager alloc] init];
 [sessionManager connectTo:@“192.168.1.4” //服務器地址
                      port:1883  //服務端端口號
                       tls:false //是否使用tls協議,mosca是支持tls的,如果使用了要設置成true
                 keepalive:60    //心跳時間,單位秒,每隔固定時間發送心跳包
                     clean:false //session是否清除,這個需要注意,如果味false,代表保持登錄,如果客戶端離線了再次登錄就可以接收到離線消息
                      auth:true //是否使用登錄驗證,和下面的user和pass參數組合使用
                      user:_userName //用戶名
                      pass:_passwd //密碼
                 willTopic:@""  //下面四個參數用來設置如果客戶端離線發送給其它客戶端消息,當前參數是哪個topic用來傳輸離線消息,這里的離線消息都指的是客戶端掉線后發送的掉線消息
                      will:@"" //自定義的離線消息,約定好格式就可以了
                   willQos:0  //接收離線消息的級別
            willRetainFlag:false 
              withClientId:]; //客戶端id,需要特別指出的是這個id需要全局唯一,因為服務端是根據這個來區分不同的客戶端的,默認情況下一個id登錄后,假如有另外的連接以這個id登錄,上一個連接會被踢下線

訂閱和發送消息

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

1
//訂閱topic
sessionManager.subscriptions = [[NSMutableDictionary alloc] init];
[sessionManager.subscriptions setObject:[NSNumber numberWithInt:1]
                                                                     forKey:_topic];
//發送消息,返回值msgid大於0代表發送成功
UInt16 msgid = [sessionManager sendData:[msg dataUsingEncoding:NSUTF8StringEncoding] //要發送的消息體
                                                                          topic:topic  //要往哪個topic發送消息                                                                        
                                                                            qos:1 //消息級別
                                                                         retain:false];

接收消息

接收消息有委托實現,實現如下委托MQTTSessionManagerDelegate接收消息

1
- (void)handleMessage:(NSData *)data onTopic:(NSString *)topic retained:(BOOL)retained;

 

根據topic可以區分不同的消息

監控連接狀態

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

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


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

 

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

Android客戶端和js集成

Android客戶端和js可以使用polo項目下的客戶端,地址http://www.eclipse.org/paho/
我測試使用的sdk:
Android https://www.eclipse.org/paho/clients/android/
js https://www.eclipse.org/paho/clients/js/

具體集成步驟同IOS,只需要注意IOS上面提到的參數設置就可以了

寫在最后

最近北方的天氣繼續惡化,霧霾嚴重到不能出門的地步了,心情也非常的低落,無力感越來越強烈,我也沒什么心思來寫了,准備離開這個地方了,等心靜下來再寫其它的東西吧。預計要寫的內容是樹莓派和Docker相關的東西。

 


轉載: http://targe.me/2016/01/02/mqtt-three/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM