在之前的文章中已简单介绍了 MQTT 协议报文的格式,本篇文章将对集中的连接协议进行详细的介绍,以及自己对该协议的一些思考和理解。
1 CONNECT
客户端和服务端建立连接之后,发送的第一个报文必须是 CONNECT。客户端只能发送一次 CONNECT 报文,如果服务端收到了第二个 CONNECT 报文,必须将其视为错误,并且断开连接。
协议格式
固定报头
可变报头
可变报头分为四个部分,分别是协议名称(Protocol Name),协议级别(Protocol Level),连接标志(Connect Flags),保持连接(Keep Alive)。
协议名称
协议名是 MQTT 的 UTF-8 编码的字符串。MQTT 规范的后续版本不会改变这个字符串的偏移和长度。
如果协议不正确服务端断开连接。(MQTT 3.1.1)其他规范中可以有其他规范。
数据包检测工具,可以使用协议名来识别 MQTT 流量。
协议级别
使用 8 位来表示协议的修订版本级别。MQTT3.1.1 的协议级别为 4。这个也是 MQTT5 的由来,MQTT5 的协议级别为 5,故称为 MQTT5。
服务端收到一个自己不支持的协议级别的时候,必须返回一个 returnCode 为 0x01 的 CONNACK 的报文给客户端,随后服务端断开相应的连接。
连接标志
服务端必须校验连接标志中的预留字段是否为 0,如果不为 0,必须断开该连接。
清理会话标志
A. 清理会话标志的表现
B. 状态信息
C. 注意事项
遗嘱标志 遗嘱标志设置了(1 或者 true)之后,连接建立成功之后,遗嘱消息会被存储在服务端并且和这个网络连接绑定,当该网络连接异常关闭时,服务端必须发布这个遗嘱消息,除非客户端发送了 DISCONNECT 消息后,服务端删除了该消息。
A. 遗嘱消息发布的条件,包括但不限于
B. 注意事项
遗嘱消息 Qos 遗嘱消息的 QOS。
遗嘱预留消息 遗嘱消息的保留标志。
用户名标志 表示是否又用户名在有效载荷中。
密码标志 表示是否又密码在有效载荷中,如果用户名标志为 0,该字段也必须为 0。
保持连接
保持连接单位为秒(s),两个 Byte 16 位表示,所以最长的 keepAlive 的时间为 18 小时 12 分 15 秒。
表示的是客户端从发送完一个报文到发送下一个报文,两者之间允许的最大时间间隔。客户端负责保证控制报文发送的时间间隔不超过保持连接的值,如果没有其他的报文可以发送,客户端必须发送 PINGREQ 报文。
客户端可以随时发送 PINGREQ 报文,并且根绝 PINGRESP 的报文判断和服务端的活动状态。
如果服务端在 1.5 倍的保持连接(非 0)时间内没有收到任何的报文,服务端必须断开相应的连接。
客户端发送 PINGREQ 之后,在规定的时间没有收到服务端的 PINGRESP 报文的回应,客户端应该断开该连接。
保持连接设置为 0 表示关闭保持连接的功能。服务端不需要因为客户端的不活跃而断开连接。
服务端主要认为客户端是不活跃或者无响应的,都可以断开客户端连接。
有效载荷
有效载荷中的字段包含一个或者多个是根据可变报头中的标志来决定有无的。如果存在的话,必须按照如下的顺序,ClientId, Will Topic, Will Message, User Name, Password。
ClientId
A. 每个客户端连接服务端必须有一个唯一的 ClientId 标志。该标志是客户端和服务端用来区分会话的唯一标志。
B. 必须提供并且必须是 CONNECT 报文有效负载的第一个字段。
C. 必须[1-23]的 UTF-8 编码,包含的内容必须是[0-9][a-z][A-Z]。服务端也可以支持更长的以及其他的 UTF-8 字符。
D. 如果 ClientId 是 0 字节的,服务端必须为其赋予一个唯一的标志,然后进行后续的处理。ClientId 为 0 字节时有如下的限制,
a. 清理会话标志必须设置为 1,否则服务端返回 returnCode=0x02 的 CONNACK,并且关闭该网络连接。
E. 服务端如果拒绝该 ClientId,服务端必须返回 returnCode=0x02 的 CONNACK,并且关闭该网络连接。
Will Topic 遗嘱消息的 topic,必须是 UTF-8 编码的字符串。
Will Message 遗嘱消息的内容。
User Name 用户名,必须是 UTF-8 的字符串。
Passowrd
由两个 Byte 表示的二进制数据标识。最长可以为 65535Bytes 的二进制。
响应
网络建立后,服务端在指定时间内没有收到客户端的 CONNECT 消息,服务端应该关闭连接。
服务端必须按照以上规范来验证 CONNECT 报文,如果不符合规范,服务端必须关闭连接,而且不需要发送 CONNACK。
服务端可以检查 CONNECT 的内容,如果任何一个检查没有通过,应该返回一个指定的 CONNACK 的报文,并且必须关闭该连接。
服务端检验通过后的处理
如果又相同 ClientId 的连接,服务端必须关闭已有的连接。
服务端需要处理和清理会话相关的内容。
返回客户端 CONNACK。
开始消息投递和保活监控。 备注:客户端可以不等待 CONNACK 的返回,直接发送其他的控制报文,如果服务端拒绝了相应的连接,服务端必须不处理 CONNECT 报文之后的任何协议。
CONNACK
服务端返回给客户端的第一个协议必须是 CONNACK 报文。
协议格式
固定报头
保留位必须为 0
当前会话 SP
A. 服务端如果没有之前的会话时,该值为 0。
B. 如果 returnCode=0(连接被接收),服务端存在之前的会话,该值为 1。
C. 该标志能够是客户端和服务端是否又已存在会话保持一致,如果两者不一致,客户端可以选择断开连接或者继续连接。客户端可以通过断开连接,清理会话设置为 1,再次连接,然后再次断开连接的方式来丢弃客户端和服务端的会话状态。
D. 如果返回码非 0,会话状态必须为 0。
连接返回码
2 总结
连接报文是 MQTT 连接建立之后的第一个报文,如果不是将会断开连接,并且连接报文也只能发送一次。
清理会话的设置也是通过连接报文进行设置,可以通过重新连接,设置清理会话的标志来保持客户端和服务端的会话信息。
遗言是跟连接绑定的,在用户非正常 DISCONNECT 的情况下将触发遗言的发布。
保持连接是检测客户端发送到服务端的消息的间隔时间,协议固定在 1.5 倍的保持连接的时间,会断开连接。现在使用的 EMQ 的保持连接的机制,KeepAlive * backoff = CheckTime,从客户端建立连接开始进行循环检测,连续两次没有检测到 Socket 报文的话,则认为超时,故真实的超时时间为 CheckTime * 2 < RealTimeout < CheckTime * 3。
本文转载自公众号 360 云计算(ID:hulktalk)。
原文链接:
https://mp.weixin.qq.com/s/lq7uN8CUf3m9CmnqLxK01A
评论