MQTT协议

MQTT是一个轻量级的PUB/SUB消息传输协议,设计目标是简单开放,网络传输相对于XMPP协议要小,比较适合于一些嵌入式设备(Internet Of Things物联网)和无线设备。我们主要想设计一套基于该协议的Android的消息推送系统。

消息格式

消息体最多有三部分组成

1.Fixed header

固定头部由最多5个字节构成

bit 7 6 5 4 3 2 1 0
byte 1 Message Type DUP flag QoS level RETAIN
byte 2 ~ 5 Remaining Length

其中Remaining Length = Variable header + Payload

Message Type:

Mnemonic Enumeration Description
Reserved 0 Reserved
CONNECT 1 Client request to connect to Server
CONNACK 2 Connect Acknowledgment
PUBLISH 3 Publish message
PUBACK 4 Publish Acknowledgment
PUBREC 5 Publish Received (assured delivery part 1)
PUBREL 6 Publish Release (assured delivery part 2)
PUBCOMP 7 Publish Complete (assured delivery part 3)
SUBSCRIBE 8 Client Subscribe request
SUBACK 9 Subscribe Acknowledgment
UNSUBSCRIBE 10 Client Unsubscribe request
UNSUBACK 11 Unsubscribe Acknowledgment
PINGREQ 12 PING Request
PINGRESP 13 PING Response
DISCONNECT 14 Client is Disconnecting
Reserved 15 Reserved

DUP
Qos>0时,客户端或服务器端尝试重发PUBLISH, PUBREL, SUBSCRIBEUNSUBSCRIBE的消息中,DUP值为1,同时的Variable Header中需要包含MessageID。

QoS Level
保证PUBLISH的接收级别,0表示最多接收一次,1表示最少接收一次,2表示只能接收一次。对QoS的实现影响整个系统的复杂度这是显而易见的。

RETAIN
仅影响PUBLISH操作,0表示仅对当前订阅者推送该消息,1表示对未来的订阅者也推送。
流程如下:一个Client给Server发PUBLISH消息中包含RETAIN为1时,Server拿到该消息,对当前的Subscriber发送PUBLISH,注意这个PUBLISH消息的RETAIN为0,这样能保证Subscriber能区分该消息是live的还是retain的。

2.Variable header

可变头部包含了Protocol name/Protocol Version/Connect flags/Keep Alive timer/Connect return code/Topic name等用来补充command的信息。

Connect Flags

bit 7 6 5 4 3 2 1 0
User Name Flag Password Flag Will Retain Will QoS Will Flag Clean Session Reserved
x x x x x x x

一个Client发起CONNECT操作的时候,可以告知Server自己的遗愿(用Will Flag/WillQoS/WillRetain来表示),一旦出现I/O错误或者在Keep Alive timer规定的时间内没有发生通讯,Server会代表该Client发送一个消息,该消息的Topic和消息体在Playload中指定。

Keep Alive timer
CONNECT消息中指定,单位为second,标明Client和Server之间的心跳间隔,间隔内如果没有消息传输发生,Client应该发送PINGREQ消息,Server以PINGRESP回应。

Connect return code
CONNACK 消息中表示返回信息,如Connection Accepted,Connection Refused: not authorized等。

Topic name
PUBLISH消息中指定,所有订阅该Topic的Client将会收到该消息。

Message identifiers
QoS Level>0时,PUBLISH, PUBACK, PUBREC, PUBREL, PUBCOMP, SUBSCRIBE, SUBACK, UNSUBSCRIBE, UNSUBACK消息的可变头部中才包含。两个字节,最大值65535。

3.Payload

CONNECT/SUBSCRIBE/SUBACK/PUBLISH等命令才有消息体。

CONNECT消息体可能是最复杂的,有Client Identifier,Will Topic和Will Message,User Name和Password(当然,这取决于在Variable Header中有无设置相应标志位)。

SUBSCRIBE消息体中包含了topic name的列表和QoS Level。

SUBACK消息体中包含了服务器授权的Qos Level,对应SUBSCRIBE中提交的顺序。

PUBLISH消息体由用户自己定义。

简要流程

以下简单描述QoS=1时的一些基本流程。

①
           CONNECT
Client-----------------> Server

可变头部包含Protocol name/Protocol Version/Connect flags/Keep Alive timer
消息体中包含Client Identifier/Will Topic/Will Message/User Name/User Password

一旦客户端出现连接异常,服务器会发送这里定制的Will Message到Will Topic上。

②
           CONNACK
Server-----------------> Client

可变头部包含了Connect return code,共四个字节

③
           PINGREQ
Client-----------------> Server

只有两字节固定头部。

④
           PINGRESP
Server-----------------> Client

只有两字节固定头部。服务器如在超过心跳周期的时间内没有接收到PINGREQ消息,应该关闭对应的TCP连接。
客户端如果在心跳周期内没有接收到PINGRESP消息,应该关闭连接并考虑其他措施重连。

⑤
           DISCONNECT
Client-----------------> Server

表示用户主动关闭连接(如用户关闭手机上的推送设置),两字节表示。
这时候如果该客户端CONNECT消息的可变头部Connect flag中的“Clean session flag”为0,不会把其从订阅列表中移除。

⑥
                       PUBLISH                   PUBLISH
Publishing Client -----------------> Server -----------------> Subscriber Client

(Qos=1)固定头部DUP为0,表示第一次发送;RETAIN注明该消息是否发送给新的订阅者。

可变头部,包含Topic Name,Message Identifier。
消息体包含消息内容。

服务器拿到消息,发送给订阅者,然后反馈给发送者(见⑦)

⑦
                        PUBACK                PUBACK
Subscriber Client----------------->Server -----------------> Publisher Client

可变头部包含⑥中Message Identifier。

⑧
        SUBSCRIBE(UNSUBSCRIBE)                 
Client ------------------------> Server 

固定头部QoS=1,DUP=0表示第一次发送
可变头部包含Message Identifier。
消息体包含Topic Name和Requested QoS(UNSUBSCRIBE没有此数据)。

⑨
          SUBACK(UNSUBACK)            
Server ---------------------> Client 

可变头部包含⑥中Message Identifier。

支持MQTT协议的级别

如果需要开发个简单的基于MQTT协议的推送系统,可以考虑:

  • 需要支持QoS 1,保证订阅用户收到至少一次消息,当然如果订阅者离线,消息缓存的时间也需要有时限;
  • 不支持RETAIN,即所有消息只发送给当前订阅者,新增加的订阅者不会收到该消息;