文章目录
  1. 1.
  2. 2. CONNECT
    1. 2.1. 可变头部
    2. 2.2. 连接标志(Connect Flags)
    3. 2.3. Clean Session
    4. 2.4. Will Flag
    5. 2.5. Will Qos
    6. 2.6. Will Retain
    7. 2.7. User name 和 password Flag
    8. 2.8. Payload/消息体
    9. 2.9. 心跳时间(Keep Alive timer)
    10. 2.10. Will Message编码
    11. 2.11. 连接异常中断通知机制
  3. 3. CONNACK
  4. 4. PINGREQ
  5. 5. PINGRESP
  6. 6. DISCONNECT

  本篇会把连接(CONNECT)、心跳(PINGREQ/PINGRESP)、确认(CONNACK)、断开连接(DISCONNECT)和在一起进行介绍。

在阅读本文之前推荐大家先读一下我的前两篇文章:MQTT协议及应用场景 MQTT协议之头部信息介绍

CONNECT

  MQTT v3.1.1有关字符串部分采用的修改版的UTF-8编码,CONNECT可变头部中协议名称、消息体都是采用修改版的UTF-8编码。前面基本上可变头部内容不多,下面是一个较为完整的CONNECT消息结构:

mqtt connect消息结构

协议变化
MQTT 3.1.1中CONNECT消息可变头部协议名称MQIsdp被改为MQTT。
MQTT 3.1.1中所有字符串明确规定使用UTF-8编码,包括客户端标识符(Client Identifier)。
MQTT 3.1.1中针对客户端标识符( client identifier)限制由原来的23个字节上限改为65535个字节。
MQTT 3.1.1中CONNECT消息可变头部协议版本号,由0x03变成了0x04 QoS 0类型PUBLISH消息DUP标记必须被设置为0
MQTT 3.1.1中MQTT Over WebSocket 被定义,互联网地址编码分配机构(Internet Assigned Numbers Authority)分配标识符为mqtt。虽在MQTT 3.1规范通篇没有提到WebSocket,但因其二进制属性可以很容易的在WebSocket通道传输。


术语变化

  • MQTT代理 -> MQTT服务器(MQTT Broker is now MQTT Server)
  • 消息ID -> 包ID(Message ID is now Packet ID)
  • 消息类型 -> 包类型(Message Type is now Packet Type)
  • 主题路径 -> 主题名称(Subscribe and Unsubscribe take Topic Paths, rather than Topic names)
  • 以前在固定头部,现在在包类型中( Flags in the fixed header are now specific to the packet type
  • 0字节保留信息需要清除 (A zero byte retained message MUST NOT be stored as a retained message on the Server )

可变头部

  协议名称和协议版本都是固定的。

连接标志(Connect Flags)

  一个字节表示,除了第1位是保留未使用,其它7位都具有不同含义。业务上很重要,对消息总体流程影响很大,需要牢记。

Clean Session

  0,表示如果订阅的客户机断线了,要保存为其要推送的消息(QoS为1和QoS为2),若其重新连接时,需将这些消息推送(若客户端长时间不连接,需要设置一个过期值)。 1,断线服务器即清理相关信息,重新连接上来之后,会再次订阅。

Will Flag

  定义了客户端(没有主动发送DISCONNECT消息)出现网络异常导致连接中断的情况下,服务器需要做的一些措施。
  简而言之,就是客户端预先定义好,在自己异常断开的情况下,所留下的最后遗愿(Last Will),也称之为遗嘱(Testament)。 这个遗嘱就是一个由客户端预先定义好的主题和对应消息,附加在CONNECT的可变头部中,在客户端连接出现异常的情况下,由服务器主动发布此消息。
  只有在Will Flag位为1时,Will Qos和Will Retain才会被读取,此时消息体payload中要出现Will Topic和Will Message具体内容,否则,Will QoS和Will Retain值会被忽略掉。 

Will Qos

  两位表示,和PUBLISH消息固定头部的QoS level含义一样。这里先略过,到PUBLISH消息再回过头来看看,会更明白些。若标识了Will Flag值为1,那么Will QoS就会生效,否则会被忽略掉。

Will Retain

  如果设置Will Flag,Will Retain标志就是有效的,否则它将被忽略。当客户端意外断开服务器发布其Will Message之后,服务器是否应该继续保存。这个属性和PUBLISH固定头部的RETAIN标志含义一样,这里先略过。

User name 和 password Flag

  用于授权,两者要么为0要么为1,否则都是无效。都为0,表示客户端可自由连接/订阅,都为1,表示连接/订阅需要授权。

Payload/消息体

  消息体定义的消息顺序(如上表所示),约定俗成,不得更改,否则将可能引起混乱。
  若Will Flag值为0,那么在payload中,Client Identifer后面就不会存在Will Topic和Will Message内容。
  若User Name和Password都为0,意味着Payload/消息体中,找不到User Name和password的值,就算有,也是无效。标志决定着是否读取与否。

心跳时间(Keep Alive timer)

  以秒为单位,定义服务器端从客户端接收消息的最大时间间隔。一般应用服务会在业务层次检测客户端网络是否连接,不是TCP/IP协议层面的心跳机制(比如开启SOCKET的SO_KEEPALIVE选项)。 一般来讲,在一个心跳间隔内,客户端发送一个PINGREQ消息到服务器,服务器返回PINGRESP消息,完成一次心跳交互,继而等待下一轮。若客户端没有收到心跳反馈,会关闭掉TCP/IP端口连接,离线。 16位两个字节,可看做一个无符号的short类型值。最大值,2^16-1 = 65535秒 = 18小时。最小值可以为0,表示客户端不断开。一般设为几分钟,比如微信心跳周期为300秒。

Will Message编码

  Will Message在CONNECT Payload/息体中,使用UTF-8编码。假设内容为“abcd”,大概如下:
mqtt connect will message

注:PUBLISH的Payload/消息体中以二进制编码保存

  在某一时间点客户端异常关闭会触发服务器PUBLISH此消息。那么服务器会直接把byte3-byte6之间字符取出,保存为二进制,附加到PUBLISH消息体中,大概存储如下:
mqtt publish will message

连接异常中断通知机制

  CONNECT消息一旦设置在可变头部设置了Will flag标记,那就启用了Last-Will-And-Testament特性,此特性很赞。
  一旦客户端出现异常中断,便会触发服务器发布Will Message消息到Will Topic主题上去,通知Will Topic订阅者,对方因异常退出。

CONNACK

  收到CONNECT消息之后,服务器应该返回一个CONNACK消息作为响应:

  • 若客户端绕过CONNECT消息直接发送其它类型消息,服务器应关闭此非法连接 若客户端发送CONNECT之后未收到CONNACT,需要关闭当前连接,然后重新连接
  • 相同Client ID客户端已连接到服务器,先前客户端必须断开连接后,服务器才能完成新的客户端CONNECT连接 客户端发送无效非法CONNECT消息,服务器需要关闭。
      一个完整的CONNACK消息大致如下:
    connack
      可变头部第一个字节为保留,无甚用处。第二个字节为连接握手返回码:
    connack response code
      只有0-5目前被使用到,其他值有待日后使用。一般返回值为0x00,表示连接建立。非法的请求,需要返回相应的数值。
      从上面看出,一个CONNACK,四个字节表示。一个正常的CONNACK消息实际内容可能如下:
    0x20 0x02 0x00 0x00 
      若是在私有协议中,两个字节就足够了。很多时候,客户端和服务器端在没有消息传递时,会一直保持着连接。虽然不能依靠TCP心跳机制(比如SO_KEEPALIVE选项),业务层面定义心跳机制,会让连接状态检测、控制更为直观。

PINGREQ

  由客户端发送到服务器端,证明自己还在一直连接着呢。两个字节,固定值。
PINGREQ
  客户端会在一个心跳周期内发送一条PINGREQ消息到服务器端。心跳频率在CONNECT可变头部“Keep Alive Timer”中定义时间,单位为秒,无符号16位short表示。

PINGRESP

  服务器收到PINGREQ请求之后,会立即响应一个两个字节固定格式的PINGRESP消息。

PINGRESP
  服务器一般若在1.5倍的心跳周期内接收不到客户端发送的PINGREQ,可考虑关闭客户端的连接描述符。此时的关闭连接的行为和接收到客户端发送DISCONNECT消息的处理行为一致,但对客户端的订阅不会产生影响(不会清除客户端订阅数据),这个需要牢记。
  若客户端发送PINGREQ之后的一个心跳周期内接收不到PINGRESP消息,可考虑关闭TCP/IP套接字连接。

DISCONNECT

客户端主动发送到服务器端,表明即将关闭TCP/IP连接。此时要求服务器要完整、干净的进行断开处理,不能仅仅类似于关闭连接描述符类似草草处理之。 需要两个字节,值固定。
DISCONNECT
  服务器要根据先前此客户端在发送CONNECT消息可变头部Connect flag中的“Clean session flag”所设置值。
复习一下:

  1. 值为0,服务器必须在客户端断开之后继续存储/保持客户端的订阅状态。这些状态包括:
    • 存储订阅的消息QoS1和QoS2消息
    • 正在发送消息期间连接丢失导致发送失败的消息。
    • 以便当客户端重新连接时以上消息可以被重新传递。
  • 值为1,服务器需要立刻清理连接状态数据。

注意:服务器在接收到客户端发送的DISCONNECT消息之后,需要主动关闭TCP/IP连接。

文章目录
  1. 1.
  2. 2. CONNECT
    1. 2.1. 可变头部
    2. 2.2. 连接标志(Connect Flags)
    3. 2.3. Clean Session
    4. 2.4. Will Flag
    5. 2.5. Will Qos
    6. 2.6. Will Retain
    7. 2.7. User name 和 password Flag
    8. 2.8. Payload/消息体
    9. 2.9. 心跳时间(Keep Alive timer)
    10. 2.10. Will Message编码
    11. 2.11. 连接异常中断通知机制
  3. 3. CONNACK
  4. 4. PINGREQ
  5. 5. PINGRESP
  6. 6. DISCONNECT