各个类型MQTT控制包详细说明
关于MQTT的介绍和控制包格式介绍,请参见上一篇文章。上一篇文章提到,MQTT协议标准中一共有14种类型的控制包,这篇文章就对着14种类型的控制包做一个介绍。
- CONNECT
如果一个客户端和服务端建立了网络通信,客户端第一个发送给服务端的包就应该是CONNECT。CONNECT在一次网络连接中只发送一次。如果服务器收到了第二个CONNECT,就应该视为协议违反,断开和客户端的连接。
固定头
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | MQTT控制包类型 (1) | 保留 | ||||||
| 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
byte 2… | 剩余长度 |
变量头
变量头包含了四个部分,按照顺序分为:协议名称,协议登记,连接标志 和保持存活.
协议名称
描述 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
协议名称 | |||||||||
byte 1 | 长度值的MSB (0) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
byte 2 | 长度值的LSB (4,MQTT四位) | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
byte 3 | ‘M’ | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 1 |
byte 4 | ‘Q’ | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 1 |
byte 5 | ‘T’ | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 0 |
byte 6 | ‘T’ | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 0 |
前面两位表示后面还有4位字符,后面4位是MQTT,如果收到的不是MQTT,就不能以MQTT的标准协议来解析下面的数据。
协议等级(版本)
| 描述 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
协议等级 | |||||||||
byte 7 | 等级(4) | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
现在使用的是MQTT3.1.1版本,规定等级是4。如果服务端不可接收这个版本,那就需要在CONNACK中回复代码0x01,然后断开和客户端的连接。
连接标志
连接标志包含了一些MQTT连接行为的设置和payload中值存在性。
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| User Name Flag | Password Flag | Will Retain | Will QoS | Will Flag | Clean Session | Reserved | |
byte 8 | X | X | X | X | X | X | X | 0 |
最后一位Reserved也必须为0,如果Server确认最后一位不是零,必须断开连接。
Clean Session
如果被设置成0,服务端必须继续与服务器通信,根据当前会话状态(由服务器标志符定义)。如果当前没会话,就新建一个会话。服务端和客户端在服务器和客户端断开后,必须存储会话。在Clean Session设置为0的会话断开以后,服务端必须存储客户端在断开时的订阅的QoS1和QoS2信息作为会话状态。他可能也存储符合相同标准的QoS 0消息。
如果被设置成1,客户端和服务端必须抛弃之前的会话并且开始一个新会话。这个会话仅在这个网络连接上持续。会话的状态数据在后面的会话中都不能再使用。
客户端会话的状态有两种:
QoS1和QoS2已经发送到服务端,不过还没有被完整的响应。
已经从服务端收到QoS2 信息,不过还没有被完整响应
服务端会话的状态有如下几种:
至少存在一个会话(即便其他会话状态都是空的)
客户端的订阅
QoS1 和QoS2信息已经被发送到客户端,不过还没有被完整响应
QoS1 和 QoS2信息等待传输至客户端
QoS2信息已经被客户端接收,不过还没有被完整响应
可选的,QoS0信息等待传输至客户端
注:
保留信息不组成服务器的会话状态,在会话结束时,不能被删除。
在Clean Session被设置为1时,客户端和服务器不需要以自动删除会话状态。
在Clean Session被设置为1时,客户端应该重复尝试连接,直到连接成功。
一般在一个应用程序下,客户端应该始终用一种Clean Session进行连接,而不是有时0有时1(可能也视应用程序而定)。在Clean Session被设置为1时,客户端将不会收到旧的应用程序消息,而且必须每次都重新订阅。在Clean Session被设置为0时,客户端将接收所有QoS1或QoS2断开连接时发布的消息。因此,如果你要断开连接时不丢失消息,要使用QoS1或QoS2,并且Clean Session要设置为0。
在Clean Session被设置为0时,则服务器在客户端断开连接后,仍要维护会话状态。客户端如果还要再后面再连接这个服务器,那他应该连接时将Clean Session被设置为0。如果客户端需要永久结束会话,那他需要连接一次服务端,将Clean Session被设置为1,然后断开连接。
Will Flag(遗嘱标志)
在Will Flag设置为1时:如果连接建立,则遗嘱信息必须被存储在服务端,与网络连接关联。如果网络连接关闭,则遗嘱信息必须被发布。除非遗嘱信息在收到一个DISCONNECT包后被删除。
一般遗嘱信息被发布的情况可能包括:
服务器检测到一个IO错误或者网络连接失败
客户端在Keep Alive时间中没有通信
客户端在没有发送DISCONNECT包时就关闭了网络连接
服务器检测到协议错误后关闭了网络连接
在Will Flag设置为1时:连接标志中的Will QoS 和 Will Retain 位置将会被服务器使用,而遗嘱话题和遗嘱信息必须在有效负载中被展示。
如果遗嘱信息被发布或者服务器从客户端接收到一个DISCONNECT包,则它就要在存储的会话状态中被移除。
在Will Flag设置为0时: 连接标志中的Will QoS 和 Will Retain 位置会被设置成0,,而遗嘱话题和遗嘱信息不会在有效负载中被展示,而遗嘱信息也不会在网络连接结束时被发布。
服务器应该及时发布遗嘱信息,但是可能服务器会因为一些故障关机,这样服务器可能会在下一次重启时再发布遗嘱,就会有延误的情况。
Will QoS
占据了两个bit
在发布遗嘱信息时,用于说明QoS的等级。
如果Will Flag被设置为0,那Will QoS也要被设置为00
如果Will Flag被置1,那QoS可以是00,01或10,但是不能是11。
Will Retain
这一位标明了遗嘱信息在发布时是否要被设置为保留。
如果Will Flag被设置为0,那Will Retain也要被设置为0
如果Will Flag被设置为0,有两种情况:
如果Will Retain被设置为0,服务器发布一个非保留的遗嘱信息
如果Will Retain被设置为1,服务器发布一个保留的遗嘱信息
User Name Flag
如果User Name Flag被置1,那user name需要包括在有效负载中。
0就是不包括。
Password Flag
如果Password Flag被置1,那密码需要包括在有效负载中。
0就是不包括。
但是如果User Name Flag设置为0,那Password Flag也要被设为0。
Keep Alive
包括两个字节
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 9 | Keep Alive MSB | |||||||
byte 10 | Keep Alive LSB |
Keep Alive代表一个时间间隔,最小单位为秒。是一个客户端发送两个控制包之间的最大允许时间(从上一个结束到下一个开始)。客户端必须保证发送两个控制包的时间间隔要小于该值,如果没啥发了,就发送PINGREQ的控制包。
客户端可以在任何时候发送PINGREQ控制包,不管Keep Alive值是多少,而且使用PINGRESP来确定网络或者服务器是否工作。
如果Keep Alive不是0,那服务器在1.5倍Keep Alive时间周期中接收不到客户端的值,他就必须断开和客户端的网络连接(视为网络故障)
如果客户端在发送PINGREQ后一段时间(时间长度可能自定义)内收不到PINGRESP,那他应该对服务器关闭网络连接。
如果Keep Alive被设置为0,那就视为关闭keep alive这个机制。这是,(客户端请求)服务器不能以不活动为理由关闭客户端的连接。当然其实服务器可以主动断开和一个客户端的连接,以任何理由任何时间。
Keep Alive最长可以设置为18小时12分钟15秒。
有效载荷
有效载荷必须按照变量头中的标志位来,而且顺序必须为客户标志符,遗嘱话题,遗嘱信息,User Name, Password。
客户端标志符
就是客户端ID,每一个客户端都要有一个单独的id。
客户端标志符是必须的,并且放在有效载荷的第一个位置。
客户端标志符必须符合UTF-8编码字符串的格式。
服务器必须允许的不超过23,只能包括数字、小写字母和大写字母的客户端标志符长度。
服务器也可以允许长度超过23,包括其他字符的客户端标志符。
服务器甚至可以接受长度为0的客户端标志符,但是如果这样,服务器也应该给这个客户端赋与一个特别的客户端标志符,并且按照这个特殊标志符来处理CONNECT包。
如果客户端提供了一个0位的id,客户端的Clean Session也必须为1。
如果客户端提供了一个0位的id,但是将Clean Session设为0,那服务端必须回复一个CONNACK包,代码为0x02(拒绝id),并且关闭网络连接。其他情况的拒绝id也是同样处理。
注:客户端可能会用随机的方法生成一个标志符。但是当Clean Session为0时,这种方法最好别用。
Will Topic
如果Will Flag被设置为1,那Will Topic就要跟在客户端标志符后面。
必须符合UTF-8编码字符串的格式
Will Message
如果Will Flag被设置为1,那Will Message就要跟在Will Topic后面,这个就是Will Topic相关的信息。
也符合UTF-8编码字符串的格式。但是UTF-8编码前两位是字符串字节的长度,是不发布的。
User Name
如果User Name Flag被置1,就必须包括。
必须符合UTF-8编码字符串的格式,用于授权和认证。
Password
密码可以是不符合UTF-8编码的格式,可以只看内容的二进制码,但是密码内容是不包括前面的两位密码长度。
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
字节 1 | 数据长度 MSB | |||||||
字节 2 | 数据长度 LSB | |||||||
字节 3 .... | 数据,如果长度 > 0. |
响应
服务器可能支持多种协议,如果服务器是MQTT 3.1.1,按照如下方式验证连接尝试:
如果服务器在网络连接后的合理时间内未收到CONNECT数据包,则应该关闭连接。
服务器必须验证CONNECT包遵守本节中的标准,如果不遵守,就直接关闭网络连接,不需要发送CONNACK。
如果服务器确认CONNECT不符合自己更近一步的要求,或者没有密码或权限验证,它应该发送一个正确的CONNACK,包括一个非零的码,然后关闭网络连接。
如果验证正确,服务器将执行以下步骤:
如果客户端标志符代表的客户端已经和服务器连接了,服务器必须断开原有连接。
服务器必须执行之前设置的Clean Session
服务器必须响应一个CONNACK包,代码为0
开始信息传输和检测存活
客户端允许在发送CONNECT包以后马上发送其他控制包,不用等到收到CONNACK包。不过如果收到服务端的拒绝,客户端在发送CONNECT包后必须不再发送任何数据。
注:客户端一般是等待CONNACK包的。不等包可能会简化一些客户端实现。不过如果收到服务端的拒绝响应,客户端必须认为,在CONNECT包发送后的其他包,都不会被服务端执行。
- CONNACK
客户端发送CONNECT包后,服务器发送CONNACK包进行确认。服务端发送给客户端的第一个包必须是CONNACK包。
如果客户端在一定时间内没有收到CONNACK包,客户端应该关闭和服务器的网络连接。这个时间限制可以根据应用自己来定。
固定头
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
字节 1 | MQTT 控制数据包 类型 (2) | 保留 | ||||||
| 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
字节 2 | 剩余长度 (2) | |||||||
| 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
对于CONNACK包,剩余长度肯定是2。
变量头
描述 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
连接确认标志 | 保留 | SP | |||||||
字节 1 |
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | X |
连接返回代码 | |||||||||
字节 2 |
| X | X | X | X | X | X | X | X |
连接响应标志
字节1是连接响应标志符,只用到了第0位(SP),其他位必须设置为0;
SP(Session Present)是会话存在的标志。
会话存在
如果服务端收到的CONNECT的Clean Session设置为1,那服务端必须设置SP为0,并且返回码为0。
如果服务端收到的CONNECT的Clean Session设置为0,那SP的值取决于服务端是否已经为提供的客户端ID存储了会话状态。如果服务端已经存储了会话状态,那SP必须设置为1。如果服务端没有存储会话状态,那必须设置SP为0。同时返回码也要设置为0。
SP标志就是用来在会话是否保存这一问题,统一服务端和客户端的。
如果会话建立完成,保存了会话的客户端也会希望服务端位置保存的会话。如果客户端收到的SP值和预期的不一样,客户端可以决定是否继续会话,或者断开连接。如果客户端连接服务端的时候,设置Clean Session为1,然后再断开连接,那客户端可以丢弃服务端和客户端两端的会话状态。
如果服务端发送了一个CONNACK包,包含的返回码不为0,那服务端必须设置SP为0。
连接返回码
连接返回码是变量头的第二个字节。
如果客户端发送的CONNECT正确,但是服务端因为一些原因不能继续会话,就会发送一个返回码不是0的CONNACK。服务端发送非0返回码的CONNACK以后,必须马上断开网络连接。
值 | 返回代码响应 | 描述 |
0 | 0x00,接受连接 | 接受连接 |
1 | 0x01,连接被拒绝,不可接受的协议版本 | 服务器不支持客户端要求的 MQTT 协议级别 |
2 | 0x02,连接被拒绝,标识符被拒绝 | 客户端标识符是正确的 UTF-8,但服务器不接受 |
3 | 0x03,连接被拒绝,服务器不可用 | 已建立网络连接,但 MQTT 服务不可用 |
4 | 0x04,连接被拒绝,用户名或密码错误 | 用户名或密码中的数据格式不正确 |
5 | 0x05,连接被拒绝,未授权 | 客户端无权连接 |
6-255 |
| 保留供将来使用 |
如果以上的这些代码都不适用,服务器必须直接关闭网络连接,不发送CONNACK。
有效载荷
CONNACK包没有有效载荷。
- PUBLISH
PUBLISH是一个双向传输应用消息的控制包。
固定头
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
字节 1 | MQTT控制数据包类型 (3) | DUP标志 | QoS级别 | RETAIN | ||||
| 0 | 0 | 1 | 1 | X | X | X | X |
字节 2 | 剩余长度 |
DUP
如果DUP标志被设为0,表示这是服务端或客户端第一次尝试发送MQTT的PUBLISH包。如果DUP标志设置为1,表示这可能是之前MQTT的PUBLISH包的重复发送。
如果服务端或者客户端尝试重复发送一个PUBLISH包,DUP标志必须设置为1。如果QoS为0,那所有的DUP标志也要设置为0。
在PUBLSIH包中DUP的标志位不会被服务器传给相关的订阅者。DUP标志在PUBLISH包中是独立的。
注1:如果接收到DUP为1的控制包,不能表示它看到过一个相同的包。
注2:DUP指的是控制包本身,不是控制包中的应用信息。当使用QoS为1是,可能客户端接收到了一个DUP标志位0的PUBLSIH包,但是包含的应用信息是之前收到过的,包ID也不同。
QoS
QoS还是一个很重要的概念。它指代了发送一条应用消息的保证登记。
QoS值 | 位 2 | 位 1 | 描述 |
0 | 0 | 0 | 最多一次发送 |
1 | 0 | 1 | 至少一次发送 |
2 | 1 | 0 | 恰好一次发送 |
- | 1 | 1 | 保留 – 不得使用 |
如果服务端或者客户端收到一条QoS两位都设为1的包,必须立即关闭网络连接。
RETAIN
如果一个客户端发送到服务端中的一个PUBLISH包中RETAIN标志设为1,那服务端必须存储应用信息和QoS,从而控制包可以将消息发送给以后的订阅符合主题名称的订阅者。当一个新的订阅建立,其关注的主题的最后的保留信息将会发送给订阅者。如果服务端接收到一个QoS为0的包,但是RETAIN为1,那他必须丢弃之前这个主题下面的所有信息。他应该存储信息QoS为0的消息,作为主题下新保留的信息,不过可能选择在任何时间下都丢弃掉它(不过这样的话这个主题下就没有保留信息了)。
如果一个消息是作为一个新的订阅的结果,服务端发送一个PUBLISH包到客户端,必须设置RETAIN标志位1。当服务端发送的一个PUBLISH是符合一个已经建立的订阅,则必须设置PUBLISH的RETAIN标志位0。
如果一个PUBLISH包的RETAIN标志为1,有效载荷为空,也可以被服务器正常处理(正常情况下,如果有效载荷为空,RETAIN标志也要为0,消息不会储存),然后发送给客户端一个匹配主题名称的订阅。同时相同主题下,任何存在的保留信息必须被移除,任何未来的订阅者也不会收到该主题的保留信息。
如果保留标志是0,客户端发送给服务器的PUBLISH包中的信息,服务器都不能存储,也不能覆盖或删除任何已经保留的信息。
保留信息在发布者不定期发布状态消息时很有用。一个新的订阅者会收到最近的状态。
变量头
变量头中按顺序包含了主题名称和包标志符两个部分。
主题名称
主题名称标明了有效载荷数据被发布的信息通道。
PUSbLISH包中的不能包含通配符。
服务器发送给订阅服务器的一个PUBLISH包中的主题名称必须匹配订阅的主题过滤器。不过服务端允许重写主题名称,所以服务器发送的包里的主题名称可能和原来PUBLISH中的不一样。
包标志符
包标志符只有在QoS=1或2的PUBLISH包里面有。
有效载荷
有效载荷里面就是应用信息,格式和内容都可以根据应用制定。也可以为长度为0。
响应
接收方接收到一个PUBLISH包后,必须根据QoS响应一个对应的包。
QoS等级 | 期望响应 |
QoS 0 | 无 |
QoS 1 | PUBACK 包 |
QoS 2 | PUBREC 包 |
操作
客户端使用PUBLISH包来发送应用消息至服务器,让其发布到匹配订阅的客户端。
服务器使用PUBLISH包发布应用消息到匹配订阅的客户端。
当客户端使用包含通配符的主题过滤器生成订阅时,这样可能一个客户端的订阅会重叠(一个发布的信息可能会匹配多个过滤器)。这种情况下,服务端会发送消息到期望所有匹配当中QoS最大的客户。服务端也可能发送多分复制的信息,每一份信息既匹配订阅也遵循订阅的QoS。
PUBLISH包接收方的操作取决于QoS等级。
如果一个服务端的实现不允许一个客户端发送一个PUBLISH,它也没有方法通知到客户端。它只能按照QoS规则进行积极响应,要不就关闭网络连接。
- PUBACK
PUBACK是QoS为1时候的PUBLISH包的响应。
固定头
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
字节 1 | MQTT 控制数据包 类型 (4) | 保留 | ||||||
| 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
字节 2 | 剩余长度 (固定就是2) | |||||||
| 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
变量头
变量头里面包含了对应PUBLISH包中的包标志符。
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
字节 1 | 数据包标识符 MSB | |||||||
字节 2 | 数据包标识符 LSB |
有效载荷
无
操作
详见本系列文章的(三)。
- PUBREC(收到QoS 2发布,第1部分)
PUBREC包是对一个QoS为2的PUBLISH包的响应。是QoS协议交换是的第二个数据包。
固定头
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
字节 1 | MQTT 控制数据包 类型 (5) | 保留 | ||||||
| 0 | 1 | 0 | 1 | 0 | 0 | 0 | 0 |
字节 2 | 剩余长度 (固定为2) | |||||||
| 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
变量头
变量头里面包含了对应PUBLISH包中的包标志符。
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
字节 1 | 数据包标识符 MSB | |||||||
字节 2 | 数据包标识符 LSB |
有效载荷
无
操作
详见本系列文章的(三)。
- PUBREL(收到QoS 2发布,第2部分)
PUBREL 数据包是对 PUBREC 数据包的响应。是的 QoS 2 协议交换的第三个数据包。
固定头
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
字节 1 | MQTT 控制数据包 类型 (6) | 保留 | ||||||
| 0 | 1 | 1 | 0 | 0 | 0 | 1 | 0 |
字节 2 | 剩余长度 (2) | |||||||
| 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
变量头
变量头包含与PUBREC 数据包中相同的包标志符。
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
字节 1 | 数据包标识符 MSB | |||||||
字节 2 | 数据包标识符 LSB |
有效载荷
无
操作
详见本系列文章的(三)。
- PUBCOMP(收到QoS 2发布,第三部分)
PUBCOMP 数据包是对 PUBREL 数据包的响应。是的 QoS 2 协议交换的第四个也是最后一个数据包。
固定头
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
字节 1 | MQTT 控制数据包 类型 (7) | 保留 | ||||||
| 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 |
字节 2 | 剩余长度 (2) | |||||||
| 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
变量头
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
字节 1 | 数据包标识符 MSB | |||||||
字节 2 | 数据包标识符 LSB |
有效载荷
无
操作
详见本系列文章的(三)。
- SUBSCRIBE
SUBSCRIBE包是从客户端发到服务端,来创造一个或多个订阅。每个订阅登记了一个客户端感兴趣的话题。服务端发送PUBLSIH包至客户端为了转发匹配相关订阅的信息。SUBSCRIBE包也标明了服务端发送到客户端的最大的QoS。
固定头
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
字节 1 | MQTT 控制数据包 类型 (8) | 保留 | ||||||
| 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
字节 2 | 剩余长度 |
变量头
变量标头包含数据包标识符,其他没有了,所以不在此处显示了。
有效载荷
SUBSCRIBE的有效载荷包括了一串客户端想要订阅的主题过滤器。主题过滤器必须符合UTF-8编码字符串的格式要求。一个服务器必须支持包含通配符的主题过滤器。如果服务器选择不支持包含通配符的主题过滤器,那它必须拒绝任何包含通配符的订阅。所有的过滤器后面都必须跟一个字节,叫做请求QoS。这给出了服务器能够发送给客户端的最高QoS等级。
SUBSCRIBE包必须包含至少一个主题过滤器/QoS对,一个SUBSCRIBE包如果不包括有效载荷的话是不符合协议的。
描述 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
主题过滤器 | ||||||||
字节 1 | 长度 MSB | |||||||
字节 2 | 长度 LSB | |||||||
字节 3..N | 主题过滤器 | |||||||
请求的 QoS | ||||||||
| 保留 | QoS | ||||||
字节 N+1 | 0 | 0 | 0 | 0 | 0 | 0 | X | X |
请求的QoS字节高位的6个位目前没有使用。如果这6个为不为0,或者QoS都为1,那服务器必须视为该控制包格式非法,并且关闭网络连接。
有效载荷的例子
| 描述 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
主题过滤器 | |||||||||
字节 1 | 长度 MSB (0) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
字节 2 | 长度 LSB (3) | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
字节 3 | “a”(0x61) | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 1 |
字节 4 | “/”(0x2F) | 0 | 0 | 1 | 0 | 1 | 1 | 1 | 1 |
字节 5 | “b”(0x62) | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 0 |
请求的 QoS | |||||||||
字节 6 | 请求的 QoS(1) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
主题过滤器 | |||||||||
字节 7 | 长度 MSB (0) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
字节 8 | 长度 LSB (3) | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
字节 9 | “c”(0x63) | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 1 |
字节 10 | “/”(0x2F) | 0 | 0 | 1 | 0 | 1 | 1 | 1 | 1 |
字节 11 | “d”(0x64) | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 0 |
请求的 QoS | |||||||||
字节 12 | 请求的 QoS(2) | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
响应
当服务器收到一个SUBSCRIBE包,服务器必须回复一个SUBACK包。SUBACK包必须和SUBSCRIBE包有相同的包标识符(ID)。
服务器允许在发送SUBACK包以前就开始发送匹配订阅的PUBLISH包。
如果服务器收到一个SUBSRIBE包,包含一个主题过滤器和现有的一个订阅的主题过滤器相同,它也要完整的将新订阅覆盖旧订阅,因为他们的QoS可能不同。任何存在的匹配主题过滤器的保留信息都必须被重发,不过发布流不能被打断。
如果主题过滤器不匹配任何现在的订阅过滤器,那一个新的订阅会生成,所有匹配的保留信息都会被发送。
如果一个服务器接收了一个SUBSCRIBE包,包含多个主题过滤器,那它处理的方式和接收多个SUBCRIBE包是一样的,除了将响应都合并到一个SUBACK包中。
服务器发送的SUBACK包必须对每一个主题过滤器/QoS对都包括一个返回码。这个返回码必须既表明了授权订阅的最大的QoS也能指出订阅的失败。服务器可能授权的最大QoS比订阅者要求低。订阅反馈信息中有效载荷信息的QoS必须是原始发布信息QoS的和服务器授权的最大QoS中的较小值。当原始发布信息的QoS为1,但是保证的最大QoS为0,服务器允许发送重复信息到订阅者。
非规范示例:
如果订阅客户端已获得特定的最大 QoS为1 主题筛选器的授权,然后收到筛选器匹配的 QoS为0的应用程序消息。这意味着客户端消息最多收到一个重复的信息。另一方面,发布到同一主题的 QoS为2的消息 被服务器降级到 QoS为1 传送到客户端,客户端可能会收到消息的副本。
如果订阅客户端已授予最大QoS为0,然后是最初作为 QoS为2 发布的应用程序消息可能会在客户端出其不意的丢失,但服务器不应发送该消息的副本。发布到同一主题的 QoS为1消息可能在传输到该客户端时丢失或重复。
非规范评论:
订阅QoS为2的主题筛选器 等效于说“我想接收匹配此过滤器消息在它们发布时使用的QoS等级”。这意味着发布者负责确定消息可以送达的最大 QoS,但订阅者能够要求服务器降级 QoS到一个更适合它的使用。
- SUBACK
SUBACK包被服务器用来发送给客户端,确认接收和处理SUBSCRIBE包。
SUBACK包包含了一列返回码,指明了每个订阅中最大的QoS等级。
固定头
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
字节 1 | MQTT 控制数据包 类型 (9) | 保留 | ||||||
| 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
字节 2 | 剩余长度 |
变量头
变量头包含了来自SUBSCRIBE包中的数据包标志符
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
字节 1 | 数据包标识符 MSB | |||||||
字节 2 | 数据包标识符 LSB |
有效载荷
有效载荷中包含了一系列返回码。每个返回码对应SUBSCRIBE包中的一个主题过滤器。SUBACK包中返回码的顺序必须和SUBSCRIBE包中主题过滤器的顺序一致。
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| 返回代码 | |||||||
字节 1 | X | 0 | 0 | 0 | 0 | 0 | X | X |
允许的返回代码:
0x00 - 成功 - 最大 QoS 0
0x01 - 成功 - 最大 QoS 1
0x02 - 成功 - 最大 QoS 2
0x80 - 失败
其他的代码目前不能使用。
- UNSUBSCRIBE
客户端发送至服务器,为了从主题当中退订。
固定头
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
字节 1 | MQTT 控制数据包 类型 (10) | 保留(固定) | ||||||
| 1 | 0 | 1 | 0 | 0 | 0 | 1 | 0 |
字节 2 | 剩余长度(等于变量标头2个字节的长度加上有效负载的长度) |
字节1的后四位是必须这样设置的,其他格式视为不正确,需要关闭网络连接。
变量头
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
字节 1 | 数据包标识符 MSB | |||||||
字节 2 | 数据包标识符 LSB |
有效负载
UNSUBSCRIBE包中的有效载荷包含了客户端希望退订的主题过滤器。一个UNSUBSCRIBE包的主题过滤器必须符合UTF-8编码字符串。
UNSUBSCRIBE包中的有效负载必须至少包含一个主题过滤器,如果没有包含内容,则会视为违反协议。
| 描述 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
主题过滤器 | |||||||||
字节 1 | 长度 MSB (0) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
字节 2 | 长度 LSB (3) | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
字节 3 | “a”(0x61) | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 1 |
字节 4 | “/”(0x2F) | 0 | 0 | 1 | 0 | 1 | 1 | 1 | 1 |
字节 5 | “b”(0x62) | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 0 |
主题过滤器 | |||||||||
字节 6 | 长度 MSB (0) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
字节 7 | 长度 LSB (3) | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
字节 8 | “c”(0x63) | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 1 |
字节 9 | “/”(0x2F) | 0 | 0 | 1 | 0 | 1 | 1 | 1 | 1 |
字节 10 | “d”(0x64) | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 0 |
和SUBSCRIBE包的有效载荷不一样,没有请求的QoS字节。
响应
服务器在收到UNSUBSCRIBE包后,不管主题过滤器中有没有通配符,都必须和主题过滤器组一个字符一个字符的比较。如果任何过滤器准确匹配,那么它的订阅就会被删除,否则不会做其他处理。
如果一个服务器删除一个订阅:
它必须停止发送给原订阅客户端新的信息。
它必须完成已经开始发送了的QoS为1和QoS为2的消息。
它可以发送给客户端已经存在的缓存信息。
服务器必须发送一个UNSUBACK包来响应UNSUBSCRIBE请求。UNSUBACK包的数据包标识符必须和UNSUBSCRIBE包一致。就算没有任何订阅主题被删除(不代表没有payload,有可能是payload中的内容没有匹配上),服务端也要送一个UNSUBACK。
如果一个服务器收到一个包括了多个主题过滤器的UNSUBSCRIBE包,那它处理的方式和接收多个UNSUBCRIBE包是一样的,除了将响应都合并到一个UNSUBACK包中。
- UNSUBACK
UNSUBACK包由服务端发送给客户端,用来确认接收到一个UNSUBSCRIBE包。
固定头
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
字节 1 | MQTT 控制数据包 类型 (11) | 保留 | ||||||
| 1 | 0 | 1 | 1 | 0 | 0 | 0 | 0 |
字节 2 | 剩余长度 (固定为2) | |||||||
| 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
变量头
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
字节 1 | 数据包标识符 MSB | |||||||
字节 2 | 数据包标识符 LSB |
有效负载
无
- PINGREQ
固定头
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
字节 1 | MQTT 控制数据包 类型 (12) | 保留 | ||||||
| 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
字节 2 | 剩余长度 (0) | |||||||
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
变量头
无
有效负载
无
响应
服务器必须发送PINGRESP数据包来响应客户端发送的PINREQ数据包。
- PINGRESP
PINGRESP 数据包由服务器发送到客户端 对 PINGREQ 数据包的响应,它指示服务器处于活动状态。
固定头
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
字节 1 | MQTT 控制数据包 类型 (13) | 保留 | ||||||
| 1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 |
字节 2 | 剩余长度 (0) | |||||||
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
变量头
无
有效负载
无
- DISCONNECT
DISCONNECT数据包是客户端发送给服务器的最后一个控制包。它标明了客户端已经完全断开连接。
固定头
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
字节 1 | MQTT 控制数据包 类型 (14) | 保留 | ||||||
| 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 |
字节 2 | 剩余长度 (0) | |||||||
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
变量头
无
有效负载
无
响应
客户端在发送了一个DISCONNECT包以后:
必须关闭网络连接
必须不能再发送任何控制包
服务器在收到DISCONNECT包以后:
必须丢弃任何和当前连接有关的遗嘱信息,不能发送。
就算客户端没有关闭网络连接,也应该关闭网络连接。