作者: wbl
MQTT
MQTT(Message Queuing Telemetry Transport,消息隊列遙測傳輸)是IBM開發的一個即時通訊協議,有可能成為物聯網的重要組成部分。該協議支持所有平台,幾乎可以把所有聯網物品和外部連接起來,被用來當做傳感器和制動器(比如通過Twitter讓房屋聯網)的通信協議
MQTT特點
MQTT協議是為大量計算能力有限,且工作在低帶寬、不可靠的網絡的遠程傳感器和控制設備通訊而設計的協議,它具有以下主要的幾項特性:
1. 使用發布/訂閱消息模式,提供一對多的消息發布,解除應用程序耦合;
2. 對負載內容屏蔽的消息傳輸;
3. 使用 TCP/IP 提供網絡連接;
4. 有三種消息發布服務質量:
- “至多一次”,消息發布完全依賴底層 TCP/IP 網絡。會發生消息丟失或重復。這一級別可用於如下情況,環境傳感器數據,丟失一次讀記錄無所謂,因為不久后還會有第二次發送。
- “至少一次”,確保消息到達,但消息重復可能會發生。
- “只有一次”,確保消息到達一次。這一級別可用於如下情況,在計費系統中,消息重復或丟失會導致不正確的結果。
5. 小型傳輸,開銷很小(固定長度的頭部是 2 字節),協議交換最小化,以降低網絡流量;
6. 使用 Last Will 和 Testament 特性通知有關各方客戶端異常中斷的機制;
好了廢話不多說,直接上干貨
雙向認證方法:讓后台生成 ca.crt 和 client.p12文件(client.p12文件由client.crt和client.key合成)
單項認證方法:讓后台生成 ca.crt
1.使用命令行把crt轉化為der格式
openssl x509 -in ca.crt -out ca.der -outform der
2.建立連接
if (!self.manager) { MQTTSSLSecurityPolicyTransport *transport = [[MQTTSSLSecurityPolicyTransport alloc]init]; transport.host = @"xxxx.com"; transport.port = 8883; transport.tls = YES; NSString* ca = [[NSBundle bundleForClass:[MQTTSession class]] pathForResource:@"ca" ofType:@"der"]; ////TODO:雙向認證需加入client證書
// NSString* client = [[NSBundle bundleForClass:[MQTTSession class]] pathForResource:@"certificate" ofType:@"p12"]; // transport.certificates = [MQTTSSLSecurityPolicyTransport clientCertsFromP12:client passphrase:@"password"];
MQTTSSLSecurityPolicy *securityPolicy = [MQTTSSLSecurityPolicy policyWithPinningMode:MQTTSSLPinningModeCertificate]; securityPolicy.allowInvalidCertificates = YES; securityPolicy.validatesDomainName = NO; securityPolicy.validatesCertificateChain = NO; securityPolicy.pinnedCertificates = @[[NSData dataWithContentsOfFile:ca]]; transport.securityPolicy = securityPolicy; self.manager = [[MQTTSessionManager alloc] init]; self.manager.delegate = self; ////不使用證書
// [self.manager connectTo:@"xxxxx.com" // port:1883 // tls:NO // keepalive:60 //心跳間隔不得大於120s // clean:true // auth:true // user:@"username" // pass:@"password" // will:false // willTopic:nil // willMsg:nil // willQos:0 // willRetainFlag:FALSE // withClientId:@"xxxxxxxxxxxxx"];
////使用證書(這里采用單項認證,雙向認證只需把certificates:參數設置為transport.certificates即可)
[self.manager connectTo:@"xxxxx.com" port:8883 tls:YES keepalive:60 clean:true auth:YES user:@"username" pass:@"password" will:false willTopic:nil willMsg:nil willQos:0 willRetainFlag:FALSE withClientId:@"xxxxxxxxx" securityPolicy:securityPolicy certificates:nil]; } else { [self.manager connectToLast]; } /* MQTTCLient: observe the MQTTSessionManager's state to display the connection status */ [self.manager addObserver:self forKeyPath:@"state" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:nil];
參數解釋:
- host 主機
- ip 端口
- tls 是否使用ssl/tls認證
- keepalive 心跳是一個時間間隔,客戶端有規律地向代理服務器發送PING Request消息。服務器用PING Response消息進行響應,這種機制可以使雙方據此判定對方是否還活着以及能否到達。
- clean clean標志位向代理服務器表明客戶端是否要建立持續的會話。持續的會話(CleanSession設置為false)意思是代理將存儲所有服務質量 (QoS) 為1或2的客戶端的訂閱信息以及所有錯過的消息。如果clean設置為true,則代理將不會存儲客戶端的任何信息,並將清除之前持續會話的所有信息。
- auth MQTT允許發送用戶名和密碼以便驗證客戶端的身份和進行授權。
- user 如果auth為yes 需要填用戶名
- pass 如果auth為yes 需要填密碼
- will 是否設遺囑 是的話需要填寫willTopic和willMsg 遺囑消息是MQTT遺囑特征的組成部分。當某個客戶端惡意斷開連接時,遺囑消息將通知其他客戶端。一個連接着的客戶端將在CONNECT消息中以MQTT消息和主題的形式提供其遺囑。如果客戶端惡意斷開了連接,代理將發送此消息來代表客戶端。
- willTopic 遺囑的topic
- willMsg 遺囑消息 nsdate類型。
- willQos 服務質量(QoS) 等級。
- willRetainFlag 會話表示標志表示,代理和客戶端之間自從前面的交互以來是否是持久會話。值為0,服務器必須在客戶端斷開之后繼續存儲/保持客戶端的訂閱狀態。這些狀態包括: 存儲訂閱的消息QoS1和QoS2消息 正在發送消息期間連接丟失導致發送失敗的消息 以便當客戶端重新連接時以上消息可以被重新傳遞。 值為1,服務器需要立刻清理連接狀態數據。 (tips:斷線重連時 如果為yes,會自動訂閱回消息,如果為no,則要手動訂閱topic,不然會收不到消息)。
- clientId 客戶端標識符(不能固定,每個用戶需要不同的clientId,相同的會導致掉線),是每個連接到MQTT代理服務器的MQTT客戶端的編號。單詞identifier本身已經表明,ClientID應當是唯一的。代理服務器使用它來識別客戶端以及當前的客戶端狀態。如果你不想讓代理獲得當前狀態,那么在MQTT3.1.1(當前的標准)中也可以發送一個空的ClientID,這樣的話,連接就不會附帶任何狀態。前提條件是clean為true,否則,連接會被拒絕。
3.服務器返回數據
狀態信息:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { switch (self.manager.state) { case MQTTSessionManagerStateClosed: NSLog(@"MQTTSessionManagerStateClosed"); break; case MQTTSessionManagerStateClosing: NSLog(@"MQTTSessionManagerStateClosing"); break; case MQTTSessionManagerStateConnected: NSLog(@"MQTTSessionManagerStateConnected"); //連接成功訂閱
for (int i=0; i<3; i++) { [self.manager subscribeToTopic:[NSString stringWithFormat:@"%d%d%d%d%d%d%d%d",i,i,i,i,i,i,i,i] atLevel:1]; } break; case MQTTSessionManagerStateConnecting: NSLog(@"MQTTSessionManagerStateConnecting"); break; case MQTTSessionManagerStateError: NSLog(@"MQTTSessionManagerStateError"); break; case MQTTSessionManagerStateStarting: NSLog(@"MQTTSessionManagerStateStarting"); default: NSLog(@"default"); break; } }
數據信息:
- (void)handleMessage:(NSData *)data onTopic:(NSString *)topic retained:(BOOL)retained { NSLog(@"------------->>%@",topic); NSString *dataString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"%@",dataString); }
4.連接(斷開后的再次連接):
- (IBAction)connect:(id)sender { [self.manager connectToLast]; }
5.訂閱
- (IBAction)sub:(id)sender { [self.manager subscribeToTopic:@"55555555555555555" atLevel:1]; }
6.取消訂閱
- (IBAction)unsub:(id)sender { [self.manager unsubscribeTopic:@"55555555555555555"]; }
7.斷開連接
- (IBAction)disconnect:(id)sender { [self.manager disconnect];
說明:MQTTSessionManager是沒有subscribeToTopic:atLevel:和unsubscribeTopic:方法的,只有subscriptions。為了更靈活使用,所以需要自己添加
1. 在.h中添加:
//訂閱
- (void)subscribeToTopic:(NSString *)topic atLevel:(MQTTQosLevel)qosLevel; //取消訂閱
- (void)unsubscribeTopic:(NSString *)topic;
2. 在.m中添加
//訂閱
- (void)subscribeToTopic:(NSString *)topic atLevel:(MQTTQosLevel)qosLevel { if (self.state == MQTTSessionManagerStateConnected) { [self.session subscribeToTopic:topic atLevel:1 subscribeHandler:^(NSError *error, NSArray<NSNumber *> *gQoss) { if (!error) { }else{ } }]; } } //取消訂閱
- (void)unsubscribeTopic:(NSString *)topic { if (self.state == MQTTSessionManagerStateConnected) { [self.session unsubscribeTopic:topic unsubscribeHandler:^(NSError *error) { if (!error) { }else{ } }]; } }
項目地址
https://github.com/wangbaolei/Mqtt-Client.git
