MQTT协议
MQTT是什么?
MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是⼀种基于发布/订阅(Publish/Subscribe)模式的轻量级通讯协议,该协议构建于TCP/IP协议上,由IBM在1999年发布,⽬前最新版本为v3.1.1。MQTT最⼤的优点在于可以以极少的代码和有限的带宽,为远程设备提供实时可靠的消息服务。做为⼀种低开销、低带宽占⽤的即时通讯协议,MQTT在物联⽹、⼩型设备、移动应⽤等⽅⾯有⼴泛的应⽤。
当然,在物联⽹开发中,MQTT不是唯⼀的选择,与MQTT互相竞争的协议有XMPP和CoAP协议等,⽂章末尾会有⼀个⽐较和说明。MQTT是哪⼀层的协议?
众所周知,TCP/IP参考模型可以分为四层:应⽤层、传输层、⽹络层、链路层。TCP和UDP位于传输层,应⽤层常见的协议有HTTP、FTP、SSH等。MQTT协议运⾏于TCP之上,属于应⽤层协议,因此只要是⽀持TCP/IP协议栈的地⽅,都可以使⽤MQTT。
MQTT消息格式
每条MQTT命令消息的消息头都包含⼀个固定的报头,有些消息会携带⼀个可变报⽂头和⼀个负荷。
消息格式如下:固定报⽂头 | 可变报⽂头 | 负荷
固定报⽂头(Fixed Header)
MQTT固定报⽂头最少有两个字节,第⼀字节包含消息类型(Message Type)和QoS级别等标志位。第⼆字节开始是剩余长度字段,该长度是后⾯的可变报⽂头加消息负载的总长度,该字段最多允许四个字节。
剩余长度字段单个字节最⼤值为⼆进制0b0111 1111,16进制0x7F。也就是说,单个字节可以描述的最⼤长度是127字节。为什么不是256字节呢?因为MQTT协议规定,单个字节第⼋位(最⾼位)若为1,则表⽰后续还有字节存在,第⼋位起“延续位”的作⽤。
例如,数字64,编码为⼀个字节,⼗进制表⽰为64,⼗六进制表⽰为0×40。数字321(65+2*128)编码为两个字节,重要性最低的放在前⾯,第⼀个字节为65+128=193(0xC1),第⼆个字节是2(0x02),表⽰2×128。
由于MQTT协议最多只允许使⽤四个字节表⽰剩余长度(如表1),并且最后⼀字节最⼤值只能是0x7F不能是0xFF,所以能发送的最⼤消息长度是256MB,⽽不是512MB。
可变报⽂头(Variable Header)
可变报⽂头主要包含协议名、协议版本、连接标志(Connect Flags)、⼼跳间隔时间(Keep Alive timer)、连接返回码(Connect Return Code)、主题名(Topic Name)等,后⾯会针对主要部分进⾏讲解。
有效负荷(Payload)
Payload直译为负荷,可能让⼈摸不着头脑,实际上可以理解为消息主体(body)。
当MQTT发送的消息类型是CONNECT(连接)、PUBLISH(发布)、SUBSCRIBE(订阅)、SUBACK(订阅确认)、UNSUBSCRIBE(取消订阅)时,则会带有负荷。
MQTT的主要特性
MQTT的消息类型(Message Type)
固定报⽂头中的第⼀个字节包含连接标志(Connect Flags),连接标志⽤来区分MQTT的消息类型。MQTT协议拥有14种不同的消息类型(如表2),可简单分为连接及终⽌、发布和订阅、QoS 2消息的机制以及各种确认ACK。⾄于每⼀个消息类型会携带什么内容,这⾥不多阐述。
消息质量(QoS)
MQTT消息质量有三个等级,QoS 0,QoS 1和 QoS 2。
QoS 0:最多分发⼀次。消息的传递完全依赖底层的TCP/IP⽹络,协议⾥没有定义应答和重试,消息要么只会到达服务端⼀次,要么根本没有到达。
QoS 1:⾄少分发⼀次。服务器的消息接收由PUBACK消息进⾏确认,如果通信链路或发送设备异常,或者指定时间内没有收到确认消息,发送端会重发这条在消息头中设置了DUP位的消息。
QoS 2:只分发⼀次。这是最⾼级别的消息传递,消息丢失和重复都是不可接受的,使⽤这个服务质量等级会有额外的开销。
通过下⾯的例⼦可以更深刻的理解上⾯三个传输质量等级。
⽐如⽬前流⾏的共享单车智能锁,智能锁可以定时使⽤QoS level 0质量消息请求服务器,发送单车的当前位置,如果服务器没收到也没关系,反正过⼀段时间⼜会再发送⼀次。之后⽤户可以通过App查询周围单车位置,到单车后需要进⾏解锁,这时候可以使⽤QoS level 1质量消息,⼿机App不断的发送解锁消息给单车锁,确保有⼀次消息能达到以解锁单车。最后⽤户⽤完单车后,需要提交付款表单,可以使⽤QoS level 2质量消息,这样确保只传递⼀次数据,否则⽤户就会多付钱了。
遗愿标志(Will Flag)
在可变报⽂头的连接标志位字段(Connect Flags)⾥有三个Will标志位:Will Flag、Will QoS和Will R
etain Flag,这些Will字段⽤于监控客户端与服务器之间的连接状况。如果设置了Will Flag,就必须设置Will QoS和Will Retain标志位,消息主体中也必须有Will Topic和Will Message字段。
那遗愿消息是怎么回事呢?服务器与客户端通信时,当遇到异常或客户端⼼跳超时的情况,MQTT服务器会替客户端发布⼀个Will消息。当然如果服务器收到来⾃客户端的DISCONNECT消息,则不会触发Will消息的发送。
因此,Will字段可以应⽤于设备掉线后需要通知⽤户的场景。
连接保活⼼跳机制(Keep Alive Timer)
MQTT客户端可以设置⼀个⼼跳间隔时间(Keep Alive Timer),表⽰在每个⼼跳间隔时间内发送⼀条消息。如果在这个时间周期内,没有业务数据相关的消息,客户端会发⼀个PINGREQ消息,相应的,服务器会返回⼀个PINGRESP消息进⾏确认。如果服务器在⼀个半
(1.5)⼼跳间隔时间周期内没有收到来⾃客户端的消息,就会断开与客户端的连接。⼼跳间隔时间最⼤值⼤约可以设置为18个⼩时,0值意味着客户端不断开。
MQTT其他特点
异步发布/订阅实现
发布/订阅模式解耦了发布消息的客户(发布者)与订阅消息的客户(订阅者)之间的关系,这意味着发布者和订阅者之间并不需要直接建⽴联系。
这个模式有以下好处:
发布者与订阅者只需要知道同⼀个消息代理即可;
发布者和订阅者不需要直接交互;
发布者和订阅者不需要同时在线。
由于采⽤了发布/订阅实现,MQTT可以双向通信。也就是说MQTT⽀持服务端反向控制设备,设备可以订阅某个主题,然后发布者对该主题发布消息,设备收到消息后即可进⾏⼀系列操作。
⼆进制格式实现
MQTT基于⼆进制实现⽽不是字符串,⽐如HTTP和XMPP都是基于字符串实现。由于HTTP和XMPP拥有冗长的协议头部,⽽MQTT固定报⽂头仅有两字节,所以相⽐其他协议,发送⼀条消息最省流量。
MQTT的安全
由于MQTT运⾏于TCP层之上并以明⽂⽅式传输,这就相当于HTTP的明⽂传输,使⽤Wireshark可以完全看到MQTT发送的所有消息,消息指令⼀览⽆遗,如图1所⽰。
这样可能会产⽣以下风险:
设备可能会被盗⽤;
客户端和服务端的静态数据可能是可访问的(可能会被修改);
协议⾏为可能有副作⽤(如计时器攻击);
拒绝服务攻击;
通信可能会被拦截、修改、重定向或者泄露;
虚假控制报⽂注⼊。
作为传输协议,MQTT仅关注消息传输,提供合适的安全功能是开发者的责任。安全功能可以从三个层次来考虑——应⽤层、传输层、⽹络层。
应⽤层:在应⽤层上,MQTT提供了客户标识(Client Identifier)以及⽤户名和密码,可以在应⽤层验证设备。
传输层:类似于HTTPS,MQTT基于TCP连接,也可以加上⼀层TLS,传输层使⽤TLS加密是确保安
全的⼀个好⼿段,可以防⽌中间⼈攻击。客户端证书不但可以作为设备的⾝份凭证,还可以⽤来验证设备。
⽹络层:如果有条件的话,可以通过拉专线或者使⽤VPN来连接设备与MQTT代理,以提⾼⽹络传输的安全性。
认证
MQTT⽀持两种层次的认证:
应⽤层:MQTT⽀持客户标识、⽤户名和密码认证;
传输层:传输层可以使⽤TLS,除了加密通讯,还可以使⽤X509证书来认证设备。
客户标识
MQTT客户端可以发送最多65535个字符作为客户标识(Client Identifier),⼀般来说可以使⽤嵌⼊式芯⽚的MAC地址或者芯⽚序列号。虽然使⽤客户标识来认证可能不可靠,但是在某些封闭环境或许已经⾜够了。
⽤户名和密码
MQTT协议⽀持通过CONNECT消息的username和password字段发送⽤户名和密码。
⽤户名及密码的认证使⽤起来⾮常⽅便,不过由于它们是以明⽂形式传输,所以使⽤抓包⼯具就可以轻易的获取。
⼀般来说,使⽤客户标识、⽤户名和密码已经⾜够了,⽐如⽀持MQTT协议连接的OneNET云平台,就是使⽤了这三个字段作为认证。如果感觉还不够安全,还可以在传输层进⾏认证。
在传输层认证
在传输层认证是这样的:MQTT代理在TLS握⼿成功之后可以继续发送客户端的X509证书来认证设备,如果设备不合法便可以中断连接。使⽤X509认证的好处是,在传输层就可以验证设备的合法性,在发送CONNECT消息之前便可以阻隔⾮法设备的连接,以节省后续不必要的资源浪费。⽽且,MQTT协议运⾏在使⽤TLS时,除了提供⾝份认证,还可以确保消息的完整性和保密性。
选择⽤户数据格式
MQTT协议只实现了传送消息的格式,并没有限制⽤户协议需要按照⼀定的风格,因此在MQTT协议之上,我们需要定义⼀套⾃⼰的通信协议。⽐如说,发布者向设备发布⼀条打开消息,设备可以回复⼀个消息并携带返回码,这样的消息格式是使⽤⼆进制、字符串还是JSON格式呢?下⾯就简单做个选型
参考。
⼗六进制/⼆进制
MQTT原本就是基于⼆进制实现的,所以⽤户协议使⽤⼆进制实现是⼀个不错的选择。虽然失去了直观的可读性,但可以将流量控制在⾮常⼩。其实对于单⽚机开发者来说⼗六进制并不陌⽣,因为单⽚机寄存器都是以位来操作的,芯⽚间通信也会使⽤⼗六进制/⼆进制。⽽对于没有单⽚机开发经验的⼯程师来说,⼗六进制/⼆进制可能就太原始了。下⾯我们继续看看还有没有其他⽅案。
字符串
对单⽚机开发者来说,字符串也是⼀个选择。⽐如通过串⼝传输的AT指令就是基于字符串通信的。使⽤字符串⽅便了⼈阅读,但是对⾼级语⾔开发者来说,字符串依旧不是最佳选择,恐怕键值对(Key-Value)才是最优形式。开源mqtt服务器
JSON
JSON中⽂全称是JavaScript对象标记语⾔,在这门语⾔中,⼀切都是对象。因此,任何⽀持的类型都可以通过JSON来表⽰,例如字符串、数字、对象、数组等。其语法规则是:
对象表⽰为键值对;
数据由逗号分隔;
花括号保存对象;
⽅括号保存数组。
JSON层次结构简洁清晰,易于阅读和编写,同时也易于机器解析和⽣成,有效提升⽹络传输效率。
对于单⽚机开发者,主流的微控制器软件开发⼯具Keil有提供JSON库,可以⽤于STC、STM32等微控制器开发,所以在微控制器上解析JSON不需要⾃⼰写⼀个JSON解析器或者移植了。
如果实在懒得使⽤JSON库⽣成或解析,也可以直接使⽤C语⾔中的sprintf⽣成JSON字符串,⽐如:
sprintf(buf, "{\"String\":\"%s\", \"Value\":%d}", "Hello World!", 12345);
1
这样就可以⽣成⼀个{“String”:”Hello World!”, “Value”:12345}JSON字符串了。
XML
MQTT协议只负责通信部分,⽤户协议可以⾃⼰选择,当然也可以选择复杂⼜冗长的XML格式。可是既然要选择MQTT+XML,为什么不考虑换为XMPP呢?
⼩结
综上所述,MQTT+JSON是⽬前最优⽅案。协议简洁清晰、易于阅读、解析和⽣成等,也考虑了服务器端开发者和设备端开发者的开发成本。
有关MQTT的云平台和⼯具
⽀持MQTT的云平台
⽬前,百度、阿⾥、腾讯的云平台都逐渐有了物联⽹开发套件:腾讯QQ物联平台内测中,阿⾥云物联⽹套件公测中,两者都需要进⾏申请试⽤,⽽百度云物联⽹套件已经⽀持MQTT并且可以免费试⽤⼀段时间。除了BAT三⼤家,下⾯再介绍⼀些其他⽀持MQTT的物联⽹云平台。
OneNET云平台:OneNET是由中国移动打造的PaaS物联⽹开放平台。平台能够帮助开发者轻松实现设备接⼊与设备连接,快速完成产品开发部署,为智能硬件、智能家居产品提供完善的物联⽹解决⽅案。OneNET云平台已经于2014年10⽉正式上线。
云巴:云巴(Cloud Bus)是⼀个跨平台的双向实时通信系统,为物联⽹、App和Web提供实时通信服务。云巴基于MQTT,⽀持Socket.IO协议,⽀持RESTful API。
MQTT服务器
如果不想使⽤云平台,只是纯粹地玩⼀下MQTT,或者只想在内⽹对设备进⾏监控,那么可以⾃⼰本地部署⼀个MQTT服务器。下⾯介绍⼏款MQTT服务器:
Apache-Apollo:⼀个代理服务器,在ActiveMQ基础上发展⽽来,可以⽀持STOMP、AMQP、MQTT、Openwire、SSL和WebSockets等多种协议,并且Apollo提供后台管理页⾯,⽅便开发者管理和调试。
EMQ:EMQ 2.0,号称百万级开源MQTT消息服务器,基于Erlang/OTP语⾔平台开发,⽀持⼤规模连接和分布式集,发布订阅模式的开源MQTT消息服务器。
HiveMQ:⼀个企业级的MQTT代理,主要⽤于企业和新兴的机器到机器M2M通讯和内部传输,最⼤程度的满⾜可伸缩性、易管理和安全特性,提供免费的个⼈版。HiveMQ提供了开源的插件开发包。
Mosquitto:⼀款实现了消息推送协议MQTT v3.1的开源消息代理软件,提供轻量级的、⽀持可发布/可订阅的消息推送模式。MQTT调试⼯具
知道了各⼤平台的MQTT,同时⾃⼰也可以在内⽹部署MQTT服务器,那接下来没有调试⼯具怎么⾏呢,难道要⽤⾃⼰喜欢的语⾔编写⼀个?当然不需要。MQTT调试⼯具可以考虑使⽤HiveMQ的MQTT客户端——HiveMQ Websockets Client,这是⼀款基于WebSocket的浏览器MQTT客户端,⽀持主题订阅和发布。
MQTT与其他协议
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论