今天在重构代码,总结一下:MQTT的使用和坑
MQTT 是IBM开发的一个即时通讯协议,有可能成为物联网的重要组成部分。该协议支持所有平台,几乎可以把所有联网物品和外部连接起来,被用来当做传感器和致动器(比如通过Twitter让房屋联网)的通信协议 ----------------------百度词条解释
应用场景
MQTT是一个设计得非常出色的传输层协议,在移动消息、物联网、车联网、智能硬件甚至能源勘探等领域有着广泛的应用。1个字节报头、2个字节心跳、消息QoS支持等设计,非常适合在低带宽、不可靠网络、嵌入式设备上应用。
不同的应用有不同的系统要求,用户使用emqttd消息服务器前,可以按自己的应用场景进行测试,而不是简单的连接压力测试:
Android消息推送: 推送消息广播测试。
移动即时消息应用: 消息收发确认测试。
智能硬件应用: 消息的往返时延测试。
物联网数据采集: 并发连接与吞吐测试
其他的不多说,有很多资料,一般常用的有两个MQTT
1.MQTTKit
2.MQTTClient
两个都用过,不过目前用的是MQTTClient,在Ios10出来后更新了一版,经常维护;
MQTTKit,这个貌似很久没更新了,也不多说
首先明确一下你拿这个东西干嘛的,拿我做的app来说:
在地图页面,请求历史数据,然后通过mqtt订阅主题,获取终端的返回的数据,然后实时更新数据,效果就类似于打车软件的样子,实现实时监控数据等功能。
第一步:安装MQTT
MQTT-Client-Framework GitHub地址
用cocopod的直接,下载就好 pod 'MQTTClient'
第二步:
![](https://i-blog.csdnimg.cn/blog_migrate/18b3376ec2da0ab254067aab28fa7d32.webp?x-image-process=image/format,png)
安装好之后就这样,每个类一看知道就是干嘛,直接上代码详细说
第三步:
导入 #import<MQTTClient/MQTTClient>
1.设置地址和端口号.
![](https://i-blog.csdnimg.cn/blog_migrate/0c9101233677edda94e3bdd2b29fb248.webp?x-image-process=image/format,png)
2.设置mqtt的账号和密码,同样找好基友要
![](https://i-blog.csdnimg.cn/blog_migrate/145cdcc57d46e495c9190861383caf19.webp?x-image-process=image/format,png)
3. 最后订阅主题,这个地方看了很多人写的博客,假设你的主题很多比如5个,10个,使用线程处理,这样也是可以的,但是不是最优化的方式,后面会详细说明,对了mqtt是可以同时订阅多个主题的,很多资料都未说明.
这个地方用了枚举,主要是为了判断订阅主题,来处理回调的数据
//注意:订阅主题不能放到子线程进行,否则block不会回调
//下面这些都是自己封装过得
//主题格式 @“$IOT/haha/datapoint/motor_control”
![](https://i-blog.csdnimg.cn/blog_migrate/813d63c9498562117162247cfe6eb15c.webp?x-image-process=image/format,png)
//如果不封装就是这样的
![](https://i-blog.csdnimg.cn/blog_migrate/f3477f5a3e455f13bdb0143825c70772.webp?x-image-process=image/format,png)
这样就实现了主题订阅,订阅成功后会有log,一堆信息,这样的
![](https://i-blog.csdnimg.cn/blog_migrate/a247aefba201723098eb0cbcac7dc2af.webp?x-image-process=image/format,png)
第四步:实现Session代理方法,处理数据
![](https://i-blog.csdnimg.cn/blog_migrate/47620d632577322c1021e3b637e2149b.webp?x-image-process=image/format,png)
在代理方法中,就可以得到对应的数据,在这里说明一下
1.如果你订阅的主题只有一个,那么你不判断也是可以的,但是如果有多个主题,你需要判断,返回的哪个对应的主题,然后才能处理数据。
2.你订阅主题假如是这样的 @“$IOT/haha/datapoint/motor_control” ,那么在处理数据时,你判断“motor_control”字符串就可以找到对应的数据,判断最后的参数就行,看截图就明白了.
3.然后拿到你的数据,你想干嘛就干嘛.
(⊙o⊙)…,到这里基本的就完了,那是不可能的,我发现很多资料写到这里就完了,还有很多的重点没说明,不然整理东西就没意义了.
可能大家用处不一样的,有人会碰到,有人不会,在这里说明一下,可能会遇到的问题:
1.先说常用的属性,如果你一直把mqtt开着,你不处理,那么mqtt一直会有数据返回,我这边就是1秒一次,然后一堆数据....
在刚开始的时候,初始化了 MQTTSession 的对象,看截图
[self.mySession disconnect] 断开连接 对应的就是 [self.mySession connect] 重新连接
如果不需要mqtt了,记得close,不然mqtt还会返回数据.
当然这个截图,只是全部举例说明,如果不用mqtt直接,close就搞定,不需要写这么多.
![](https://i-blog.csdnimg.cn/blog_migrate/75e6417c3111aa3ec4b9d7f4ec86326e.webp?x-image-process=image/format,png)
2.在正常情况下,你的mqtt成功订阅了主题,那么它会一直在监听服务器是否有数据返回,但是有的时候嘛,你会遇到下面这中情况,mqtt停止了,当然排除你自己 close的情况外.
![](https://i-blog.csdnimg.cn/blog_migrate/df1970288523e7ffc7aace865848976d.webp?x-image-process=image/format,png)
这种情况就很坑爹啦,你用mqtt的目的是啥,就是要实时监控嘛。。。。
在我查询的资料中有几种解释:
前提是你家的服务器一直在发送数据,而不是停掉了
2.1 你的clientID,没有设置,简单说明,你有一个账号是老王,那么你登录了,你的同事也登录了,这个时候,mqtt会自动掉线。
self.mySession.clientId 有这个属性,我咨询过后台,说移动端要设置,设置成功就可以避免,但是如果你用的是,MQTTClient 这个,完全不用,因为别人已经处理好了,可以自己点击进去看看,那么如果你用的是其他的第三方,clientId需要取随机数,不能相同.
2.2 碰到上面这个问题咋办呢,MQTTClient 没有断线重连的机制,也可能是我没找到,哪位小伙伴看到了也麻烦告诉我一声.
这个时候,你需要监听消息的状态,就可以处理了,来看看,self.mySession.status的值
![](https://i-blog.csdnimg.cn/blog_migrate/943fcd1344ac3bade5cd5aa04bc093d7.webp?x-image-process=image/format,png)
在上面也写到了,如果你使用disconnect\connect,那么消息的状态最终都会输出第五个MQTTSessionStatusClosed.
2.3 介绍两种监听方法:
2.3.1 KVO 在创建MQTTSession对象的时候,添加就可以了
![](https://i-blog.csdnimg.cn/blog_migrate/f2346b94e3fab6eb9cefc731a8865763.webp?x-image-process=image/format,png)
![](https://i-blog.csdnimg.cn/blog_migrate/f7c65eb523b938b18a1cce62f5476f85.webp?x-image-process=image/format,png)
这样只要消息关闭了,那么mqtt会立即重连;但是会有一个问题,如果你的mqtt只是部分页面用,离开这个页面之后,mqtt还会继续监听,不停的创建对象,上面解释过了,消息状态最终都是MQTTSessionStatusClosed.
2.3.2 目前我在用就是定时器,用定时器处理消息状态
![](https://i-blog.csdnimg.cn/blog_migrate/a29aee4e5180d900a4699bbe5c50af6a.webp?x-image-process=image/format,png)
好处就是,离开页面的时候,mqtt关闭,定时器也关闭,不会导致kvo那种情况出现,坏处暂时没发现.
这样就可以让mqtt一只处于监控的状态下了.
3.关于MQTT一次性订阅多个主题,每个mqtt框架都可以。
假如你要一次性订阅10个主题,那么你不得写十次么,完全不用这样,现在介绍一个更简单的办法,
你只需要更改一个地方就可以了,主题URL
上面我写的主题格式是这样的
//主题格式 @“$IOT/haha/datapoint/motor_control”
现在你只需要把主题的URL 改成这样 @"$IOT/haha/#"
MQTT主题(Topic)支持’+’, ‘#’的通配符,’+’通配一个层级,’#’通配多个层级(必须在末尾)
解释一下,#表示,haha下面的所有主题,假如你有10个主题,你这样就可以一次性订阅完成,然后你在返回的数据中,判断对应的主题处理数据就可以了.
代码中就可以把其它的注释掉.
![](https://i-blog.csdnimg.cn/blog_migrate/a84b2370c70aa7d92fe0c4571034b117.webp?x-image-process=image/format,png)
话又说回来,一个页面你只用到一个主题的内容,你难道也这样写嘛,所以一切看实际情况。
4.最后说一个,MQTT超时时间设置的问题.
[self.mySession connectAndWaitTimeout:1];
MQTT在网络情况不太好/接收不到服务器数据时,容易导致线程卡住,你懂得,什么都不能点了,建议把这里的时间设置小一点.
5.其他
最最后,补充一个网站,后台搭建mqtt服务器,移动端使用原理介绍都可以用,MQTT介绍的很全面了,深入了解的可以看看,有swift和oc的版本,这个我没用过,原理一样.