前言
在互联网时代http协议负责建立网络连接,到了物联网时代,由于智能硬件的差异,相比互联网终端,硬件配置要低得多,而且智能设备的环境也相当复杂,物联网中的数据传输会面临很多问题,如何保证数据不会重复发送,连接断开后如何进行重连。而HTTP协议太过重量级,不适合物联网。因此提出一种轻量级的MQTT协议,适合于低带宽、不可靠连接、嵌入式设备、CPU、内存资源紧张,适合于各种受限环境。
TCP/IP参考模型可以分为四层:应用层、传输层、网络层、链路层。TCP和UDP属于传输层,应用层常见的协议有HTTP、FTP、ssh等。MQTT协议运行与TCP之上,属于应用层协议,因此只要是支持TCP/IP协议栈的地方,都可以使用MQTT。
协议特征
客户端/服务器模式 vs 发布/订阅模式
传统的网络通信模式几乎都是基于客户端/服务器模式的:客户端直接和一个服务端建立连接,通信来获取数据,数据源和数据目的地双方直接连接。比如在Web应用程序中,浏览器直接和Web服务器通过HTTP协议通信,浏览器需要知道服务器的IP和端口号。
而发布/订阅模式解耦了客户端,客户端分为2种角色:发布者(Publisher)和订阅者(Subscriber)。每一个发布者(Publisher)可以发送不同类型的消息,我们把消息的类型叫做主题(topic),MQTT通信中的消息都属于某一个主题 ,而只有订阅了这个主题的订阅者(Subscriber)才能收到属于这个主题的消息。发布者和订阅者不需要在意和知道对方的存在(不需要知道对方的IP和端口),也不需要直接与对方建立连接。因为通信中存在着一个叫代理(MQTT broker)的第三种角色,也可以叫MQTT服务器(MQTT server)。
发布者、订阅者只需要知道MQTT 服务器的IP和端口即可,并和它直接建立连接通信。MQTT代理作为消息的中转,它过滤所有接受到的消息,并按照一定的机制(MQTT标准规定是基于主题的消息过滤派发方式,而具体的MQTT服务器软件也提供了其他的派发方式)分发它们,使得所有注册到MQTT代理的订阅者只接收到他们订阅了的消息,而不会收到他不关心的消息。
当发布者发布一条消息的时候,他必须同时指定消息的主题和消息的负载。MQTT代理在收到发布者发过来的消息时,无需访问消息负载,他只是访问消息的主题信息,然后根据这主题派发给订阅者。需要注意的是,一个客户端可以同时既当发布者又当订阅者。比如一个开发板连接了一盏LED灯,它可以发布灯的暗/亮状态信息,也可以从其他节点订阅对灯的控制消息。
消息格式
每条MQTT命令消息的消息头都包含一个固定的报头,有些消息会携带一个固定报文头和一个负荷。
固定报文头 | 可变报文头 | 负荷
固定报文头(Fixed Header)
存在于所有MQTT数据包中,表示数据包类型及数据包的分组类标识。
MQTT固定报文头最少有2个字节,第一个字节包含消息类型(Message Type)和QoS等级标志位。第二字节开始是剩余长度字段,该长度是后面的可变报文头加消息负载的总长度,该字段最多允许4个字节。
可变报文头(Variable Header)
存在于部分MQTT数据包中,数据包类型决定了可变头是否存在及其具体内容。
包含协议名、协议版本、连接标志(Connect Flag)、心跳间隔时间(Keep Alive Timer)、连接返回码(Connect Return Code)、主题名(Topic Name)等。
有效负荷(Payload)
存在于部分MQTT数据包中,标识客户端收到的具体内容。
实际上可以理解为消息主体(body)。当MQTT发送的消息类型是connect(连接)、publish(发布)、subscribe(订阅)、suback(订阅确认)、ubsubscribe(取消订阅)时,则会带有负荷。
消息模型
MQTT是一种基于代理的发布/订阅的消息协议。提供一对多的消息分发,解除应用程序耦合。一个发布者可以对应多个订阅者。这种模式提供了更大的网络拓展性和更动态的网络拓扑。
协议质量
MQTT提供三种质量的服务
QoS0 : 至多一次,可能会出现丢包现象。消息要么只会到达服务器一次,要么根本没有到达。
使用在对实时性不高的情况。这一级别可应用与如下场景,如环境传感器数据,
丢失一次读记录无所谓,很快下一次记录就会生成。
QoS1 : 至少一次,保证包会到达目的地,但可能出现重复包
QoS2 : 正好一次,保证包会到达目的地,且不会出现重复包现象。
这一级别可用于计费系统等场景,在计费系统中,消息丢失或重复可能会导致生成错误的费用。
共享单车智能锁,智能锁可以定时使用QoS0质量消息请求服务器,发送单车当前位置,如果服务器没收到也没关系,过一段时间又会发送一次。之后用户可以通过APP查询周围单车位置,找到单车后需要进行解锁,这时候可以使用QoS1质量消息,手机APP不断发送解锁消息给单车锁,确保有一次消息能到达以解锁单车。最后用户用完单车后,需要提交付款表单,可以使用QoS2质量消息,确保只传递一次数据,避免重复付款。
主题名称(Topic name)
用来标识已发布消息的信息的渠道。订阅者用他来接收到所关心的信息。
主题名称是一个分层的结构,用斜线“/”作为分隔符。有两种通配符可以在主题发布/订阅时使用——“#”和“+”。“#” 通配多层结构,“+”通配一层结构
一个topic:“a/b/c”,则“a/+/c”和“a/#”都可以和他相同
发布不支持模糊匹配,必须是确定的主题。
遗嘱(Will Flag)
当一个客户端断开连接的时候,它希望客户端能发送它指定的消息。该消息和普通消息结构相同。通过设置该位并填入和信息相关的内容即可。
消息类型 (Message Type)
固定报文头中的第一个字节包含连接标志(Connect Flag),连接标志用来区分MQTT的消息类型。
类型名称 | 类型值 | 报文说明 |
---|---|---|
Reserved | 0 | 保留 |
Connect | 1 | 客户端对服务端的连接请求 |
ConnACK | 2 | 服务端对连接请求的响应 |
Publish | 3 | 发布消息 |
puback | 4 | 对发布消息的回应 QoS1消息确认 |
pubRec | 5 | 收到发布消息 QoS2消息回执(保证交付第一步) |
pubRel | 6 | 释放发布消息 QoS2消息释放(保证交付第二步) |
pubComp | 7 | 完成发布消息 QoS2消息完成(保证交付第三步) |
subcribe | 8 | 客户端订阅请求 |
subBack | 9 | 订阅请求回应 |
unsubscribe | 10 | 停止订阅请求 |
unsubBack | 11 | 停止订阅请求响应 |
pingReq | 12 | ping请求(保持连接) |
pingResp | 13 | ping响应 |
disconnect | 14 | 客户端正在断开 |
reserved | 15 | 保留 |
开发一个MQTT库需要提供如下命令
Connect:当一个TCP/IP套接字在服务器端和客户端建立连接时需要使用的命令
publish:由客户端向服务端发送,告诉服务端自己感兴趣的Topic。
每一个publishMessage都是与一个Topic的名字联系在一起。
pubRec:是publish命令的响应,使用了QoS2,是QoS2的第二条消息
pubRel:是QoS2协议的第三条消息
pubComp:是QoS2协议的第四条消息
subscribe:允许一个客户端注册自己感兴趣的Topic名字,发布到这些Topic的消息会
以Publish Message的形式由服务器发送给客户端
unsubscribe:从客户端到服务端,退订一个Topic
ping:由客户端向服务端发送“are you alive”的消息
disconnect:断开这个TCP/IP协议
MQTT协议原理
MQTT协议实现方式
实现MQTT协议需要客户端与服务端通讯完成,在通讯过程中,MQTT协议中有3中身份:发布者(Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者也可以同时是订阅者。
MQTT传输的消息分为:主题(Topic)和负载(payload)
主题:可以理解为消息的类型,订阅者订阅(subscribe)后,就会收到该主题的消息内容(payload)
负载:可以理解为消息的内容,是指订阅者具体要使用的内容。
网络传输与应用消息
MQTT会构建底层网络传输。将建立客户端到服务器的连接,提供两者之间的一个无序的、无损的、基于字节流的双向传输。当应用数据通过MQTT网络发送时,MQTT会把与之相关的服务质量(QoS)和主题名(Topic)相关联。
MQTT客户端
一个使用MQTT协议的应用程序或者设备,它总是建立到服务器的网络连接。
客户端可以:
发布其他客户端可能会订阅的信息;
订阅其他客户端发布的消息;
退订或删除应用程序的消息;
断开与服务器的连接。
MQTT服务器
称为“消息代理(Broker)”,可以是一个应用程序或一台设备,位于消息发布者与订阅者之间。
服务器可以:
接受来自客户的网络连接;
接收客户发布的应用信息;
处理来自客户端的订阅和退订请求;
向订阅的客户转发应用程序消息。
MQTT协议中的订阅、主题、会话
订阅(Subscription)
订阅包含主题筛选器(Topic Filter)和最大服务质量(QoS)。订阅会与一个会话(Session)关联。一个会话可以包含多个订阅。每个会话中的每个订阅都有一个不同的主题筛选器。
会话(Session)
每个客户端与服务器建立连接后就是一个会话,客户端与服务器之间有状态交互。会话存在于一个网络之间,也可能在客户端与服务器之间跨越多个连续的网络连接。
主题名(Topic Name)
连接到一个应用程序消息的标签,该标签与服务器的订阅相匹配。服务器会将消息发送给订阅所匹配标签的每个客户端。
主题筛选器(Topic Filter)
一个对主题名通配符筛选器,在订阅表达式中使用,表示订阅所匹配到的多个主题。
负载(Payload)
消息订阅者所具体接收的内容。
MQTT协议中的方法
MQTT协议中定义了一些方法(动作),来用于表示对确定资源所进行的操作。这个资源可以代表预先存在的数据或动态生成的数据,这取决与服务器的实现。通常来说,资源指服务器上的文件或输出。
Connect:等待与服务器建立连接
Disconnect:等待MQTT客户端完成所做的工作,并与服务器断开TCP/IP会话
Subscribe:等待完成订阅
UnSubcribe:等待服务器取消客户端的一个或多个topics订阅
Publish:MQTT客户端发送消息请求,发送完成后返回应用程序线程