iOS MQTT 接入示例(MQTT 物联套件)

前言

需求是移动端接入MQTT,点击按钮利用MQTT给门禁上的设备发送消息;
注:门禁设备(Android系统集成了MQTT和给硬件信息发送指令的包)
缺陷未解决:
1.门最后开没开成功,硬件是没有给反馈的,门禁设备也不知道;
2.移动设备消息发送了,指定的门禁设备是否收到消息,移动端还不知道;

该文介绍的是使用阿里的MQTT接入ios的说明
因给的demo里没有参数说明,看👇简单说明作为了解;

MQTT协议中文版

MQTT是一个客户端服务端架构的发布/订阅模式的消息传输协议。它的设计思想是轻巧、开放、简单、规范,易于实现。
这些特点使得它对很多场景来说都是很好的选择,特别是对于受限的环境如机器与机器的通信(M2M)以及物联网环境(IoT)
协议传送门:https://mcxiaoke.gitbooks.io/mqtt-cn/content/

MQTT应用场景

屏幕快照.png

MQTT 物联套件

介绍 MQTT 协议基本概念,阿里巴巴 MQ 提供的 MQTT 服务的主要原理以及 MQTT 协议主要的应用场景
地址如下:
https://help.aliyun.com/document_detail/42419.html?spm=5176.doc47755.6.560.dcabYu

MQTT iOS 接入示例

介绍如何使用 iOS 客户端收发 MQTT 消息地址如下:
https://help.aliyun.com/document_detail/47755.html?spm=5176.doc44711.6.633.pN8AIa

iOS接入 非CocoaPods 安装配置

滑动到👆示例地址的底部: 下载demo 拿到路径pods下的MQTTClient下的MQTTClient导入工程;
不要将LICENSE文件也导入进程序

不是CocoaPods安装,需要将如下头文件改成双引号""
#import <MQTTClient/MQTTSession.h>
#import <MQTTClient/MQTTSessionLegacy.h>
#import <MQTTClient/MQTTSessionSynchron.h>
#import <MQTTClient/MQTTMessage.h>
#import <MQTTClient/MQTTTransport.h>
#import <MQTTClient/MQTTCFSocketTransport.h>
#import <MQTTClient/MQTTCoreDataPersistence.h>
#import <MQTTClient/MQTTSSLSecurityPolicyTransport.h>
#import "MQTTSession.h"
#import "MQTTSessionLegacy.h"
#import "MQTTSessionSynchron.h"
#import "MQTTMessage.h"
#import "MQTTTransport.h"
#import "MQTTCFSocketTransport.h"
#import "MQTTCoreDataPersistence.h"
#import "MQTTSSLSecurityPolicyTransport.h"
在需要实现的地方导入头文件;
/*
 * MQTTClient: imports
 * MQTTSessionManager.h is optional
 */
#import "MQTTClient.h"
#import "MQTTSessionManager.h"

/*
 * MQTTClient: using your main view controller as the MQTTSessionManagerDelegate
 */

#import <CommonCrypto/CommonHMAC.h>
添加代理:
MQTTSessionManagerDelegate
/*
 * MQTTClient: keep a strong reference to your MQTTSessionManager here
 */
@property (strong, nonatomic) MQTTSessionManager *manager;

@property (strong, nonatomic) NSDictionary *mqttSettings;
@property (strong, nonatomic) NSString *rootTopic;
@property (strong, nonatomic) NSString *accessKey;
@property (strong, nonatomic) NSString *secretKey;
@property (strong, nonatomic) NSString *groupId;
@property (strong, nonatomic) NSString *clientId;
@property (assign, nonatomic) NSInteger qos;

@property (strong, nonatomic) NSMutableArray *chat;
初始化客户端连接到host
 MQTT-Client-FrameWork 包提供的客户端类有 MQTTSession 和 MQTTSessionManager,
 建议使用后者维持静态资源,而且已经封装好自动重连等逻辑。
 初始化时需要传入相关的网络参数

参数在阿里提供的demo里有一个plist文件,如下:

屏幕快照 2017.png

参数如何配置请查看👇:
https://help.aliyun.com/document_detail/29536.html?spm=5176.doc29535.6.551.OrrVHX

从配置文件导入相关属性,发起连接

我的需求是只有一个地方使用,进到这个页面的时候,我才发起连接,
点击按钮给门禁上的java程序发送开门消息,连接方法调用如下方法:

-(void)initMQTT{

    NSURL *bundleURL = [[NSBundle mainBundle] bundleURL];
    NSURL *mqttPlistUrl = [bundleURL URLByAppendingPathComponent:@"mqtt.plist"];
    self.mqttSettings = [NSDictionary dictionaryWithContentsOfURL:mqttPlistUrl];
    self.rootTopic = self.mqttSettings[@"rootTopic"];
    self.accessKey = self.mqttSettings[@"accessKey"];
    self.secretKey = self.mqttSettings[@"secretKey"];
    self.groupId = self.mqttSettings[@"groupId"];
    self.qos =[self.mqttSettings[@"qos"] integerValue];
    
    //clientId的生成必须遵循GroupID@@@前缀,且需要保证全局唯一
    /*为了保证全局唯一,ios可以获取CFUUID,每次获取都是不一样的,
       想保证一个设备一样,需要存本地一份;
        但是我这里需要每次使用的时候,每次连击所以为了保证clientid全局唯一,
        我每次都获取一次CFUUID,去掉中间的分隔线" - "代码如下,
    */
    CFUUIDRef cfuuid = CFUUIDCreate(kCFAllocatorDefault);
    NSString* cfuuidString = (NSString*)CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, cfuuid));
    NSString* tempstr = [cfuuidString stringByReplacingOccurrencesOfString:@"-" withString:@""];
    cfuuidString = tempstr;

    self.clientId=[NSString stringWithFormat:@"%@@@@%@%@",self.groupId,@"",cfuuidString];
    
    self.chat = [[NSMutableArray alloc] init];
    /*
     * MQTTClient: create an instance of MQTTSessionManager once and connect
     * will is set to let the broker indicate to other subscribers if the connection is lost
     */
    if (!self.manager) {
        self.manager = [[MQTTSessionManager alloc] init];
        self.manager.delegate = self;
        
        self.manager.subscriptions = [NSDictionary dictionaryWithObject:
                                      [NSNumber numberWithLong:self.qos]forKey:
                                      [NSString stringWithFormat:@"%@/#", self.rootTopic]];
        
        //password的计算方式是,使用secretkey对groupId做hmac签名算法,具体实现参考macSignWithText方法
        NSString *passWord = [[self class] macSignWithText:self.groupId secretKey:self.secretKey];
          /*
          此处从配置文件导入的Host即为MQTT的接入点,该接入点获取方式请参考资源申请章节文档,
           在控制台上申请MQTT实例,每个实例会分配一个接入点域名
          */
        [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];
        
    } else {
        [self.manager connectToLast];
    }
 }

/*
 userName 和 passWord 的设置
 
 由于服务端需要对客户端进行鉴权,因此需要传入合法的 userName 和 passWord。
 userName 设置为当前用户的 AccessKey,
 password 则设置为 MQTT 客户端 GroupID 的签名字符串,
 签名计算方式是使用 SecretKey 对 GroupID 做 HmacSHA1 散列加密。
 具体方法请参考 👇 中的 macSignWithText 函数。
 */
+ (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;
}

connectTo方法里的参数说明:
  • tls:false //是否使用tls协议,mosca是支持tls的,如果使用了要设置成true
  • clean:false //session是否清除,这个需要注意,如果是false,代表保持登录,
    如果客户端离线了再次登录就可以接收到离线消息。注意:QoS为1和QoS为2,并需订阅和发送一致
  • auth:true //是否使用登录验证,和下面的user和pass参数组合使用
  • user:_userName //用户名
  • pass:_passwd //密码
  • willTopic:@"" //下面四个参数用来设置如果客户端异常离线发送的消息,
    当前参数是哪个topic用来传输异常离线消息,这里的异常离线消息都指的是客户端掉线后发送的掉线消息
  • will:@"" //异常离线消息体。自定义的异常离线消息,约定好格式就可以了
  • willQos:0 //接收离线消息的级别 0、1、2
  • willRetainFlag:false //只有在为true时,Will Qos和Will Retain才会被读取,此时消息体payload中
    要出现Will Topic和Will Message具体内容,否则,Will QoS和Will Retain值会被忽略掉
  • withClientId:nil]; //客户端id,需要特别指出的是这个id需要全局唯一,因为服务端是根据这个来区分不同的客户端的,
    默认情况下一个id登录后,假如有另外的连接以这个id登录,上一个连接会被踢下线;

发送消息(当点击按钮的时候,发送消息方法如下:)

  [self.manager sendData:[self.scanDic.mj_JSONString dataUsingEncoding:NSUTF8StringEncoding]
                     topic:[NSString stringWithFormat:@"%@",
                            self.rootTopic]//此处设置多级子topic
                       qos:self.qos
                    retain:FALSE];
发送方法注意topic的设置

以下为安卓代码中的注释示例:
ios的demo中没有此说明
消息发送到某个主题Topic,所有订阅这个Topic的设备都能收到这个消息。
遵循MQTT的发布订阅规范,Topic也可以是多级Topic。
此处设置了发送到二级topic如下:
sampleClient.publish(topic+"/notice/", message);
但是发送P2P消息,二级Topic必须是“p2p”,三级topic是目标的ClientID
此处设置的三级topic需要是接收方的ClientID如下:
string p2pTopic =topic+"/p2p/"+consumerClientId;

qos:消息的传输方式

QoS说明如下:

  • 0 代表“至多一次”,消息发布完全依赖底层 TCP/IP 网络。会发生消息丢失或重复。
    这一级别可用于如下情况,环境传感器数据,丢失一次读记录无所谓,因为不久后还会有第二次发送。
  • 1 代表“至少一次”,确保消息到达,但消息重复可能会发生。
  • 2 代表“只有一次”,确保消息到达一次。这一级别可用于如下情况,在计费系统中,消息重复或丢失会导致不正确的结果。
  • 备注:由于服务端采用Mosca实现,Mosca目前只支持到QoS 1
  • 如果发送的是临时的消息,例如:给某topic所有在线的设备发送一条消息,丢失的话也无所谓,0就可以了
    (客户端登录的时候要指明支持的QoS级别,同时发送消息的时候也要指明这条消息支持的QoS级别)
  • 如果需要客户端保证能接收消息,需要指定QoS为1,如果同时需要加入客户端不在线也要能接收到消息,
    那么客户端登录的时候要指定session的有效性,接收离线消息需要指定服务端要保留客户端的session状态。

接收发送消息的回调

/*
 * MQTTSessionManagerDelegate
 */
- (void)handleMessage:(NSData *)data onTopic:(NSString *)topic retained:(BOOL)retained {
    /*
     * MQTTClient: process received message
     */
    NSString *dataString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    [self.chat insertObject:[NSString stringWithFormat:@"RecvMsg from Topic: %@\nBody: %@", topic, dataString] atIndex:0];    
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,596评论 18 139
  • 前言 接到任务项目需要用MQTT来写消息推送,经过一段时间在网上查看资料后写下这篇文章,文章内容大都来自互联网,在...
    Hank_Zhong阅读 16,502评论 69 51
  • 序 本篇会把连接(CONNECT)、心跳(PINGREQ/PINGRESP)、确认(CONNACK)、断开连接(D...
    技术学习阅读 9,712评论 0 8
  • MQTT-SN的一个重要设计原则是尽可能与MQTT相近。因此,所有的协议语义应保持尽可能与MQTT中定义的一致。接...
    aded3e27ac95阅读 844评论 0 2
  • 早上一大早,赶着给人送工资卡,我天,100来公里,感觉,稍微有些远,但杯子一贯比较务实,诚恳,热忱,不怕困难,一旦...
    杯里水阅读 166评论 0 0