前言
本篇会把连接(CONNECT)、心跳(PINGREQ/PINGRESP)、确认(CONNACK)、断开连接(DISCONNECT)和在一起。
CONNECT
像前面所说,MQTT有关字符串部分采用的修改版的UTF-8编码,CONNECT可变头部中协议名称、消息体都是采用修改版的UTF-8编码。前面基本上可变头部内容不多,下面是一个较为完整的CONNECT消息结构:
Fixed header/固定头部 | |||||||||
Message Type(1) | DUP flag | QoS level | RETAIN | ||||||
byte 1
|
0 | 0 | 0 | 1 | x | x | x | x | |
byte 2 | Remaining Length | ||||||||
Variable header/可变头部 | |||||||||
Protocol Name | |||||||||
byte 1 | Length MSB (0) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
byte 2 | Length LSB (6) | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 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 | 'I' | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 1 |
byte 6 | 's' | 0 | 1 | 1 | 1 | 0 | 0 | 1 | 1 |
byte 7 | 'd' | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 0 |
byte 8 | 'p' | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 |
Protocol Version Number | |||||||||
byte 9 | Version (3) | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
Connect Flags | |||||||||
User Name Flag | Password Flag | Will Retain | Will QoS | Will Flag | Clean Session | Reserved | |||
byte 10
|
1 | 1 | 0 | 0 | 1 | 1 | 1 | x | |
Keep Alive timer | |||||||||
byte 11 | Keep Alive MSB (0) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
byte 12 | Keep Alive LSB (10) | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 |
Playload/消息体 | |||||||||
Client Identifier(客户端ID) 1-23个字符长度,客户端到服务器的全局唯一标志,如果客户端ID超出23个字符长度,服务器需要返回码为2,标识符被拒绝响应的CONNACK消息。处理QoS级别1和2的消息ID中,可以使用到。 必填项。 |
|||||||||
Will Topic Will Flag值为1,这里便是Will Topic的内容。QoS级别通过Will QoS字段定义,RETAIN值通过Will RETAIN标识,都定义在可变头里面。 |
|||||||||
Will Message Will Flag若设为1,这里便是Will Message定义消息的内容,对应的主题为Will Topic。如果客户端意外的断开触发服务器PUBLISH此消息。长度有可能为0。 在CONNECT消息中的Will Message是UTF-8编码的,当被服务器发布时则作为二进制的消息体。 |
|||||||||
User Name 如果设置User Name标识,可以在此读取用户名称。一般可用于身份验证。协议建议用户名为不多于12个字符,不是必须。 |
|||||||||
Password 如果设置Password标识,便可读取用户密码。建议密码为12个字符或者更少,但不是必须。 |
可变头部
协议名称和协议版本都是固定的。
连接标志(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才会被读取,此时消息体Playload中要出现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,表示连接/订阅需要授权。
Playload/消息体
消息体定义的消息顺序(如上表所示),约定俗成,不得更改,否则将可能引起混乱。
若Will Flag值为0,那么在Playload中,Client Identifer后面就不会存在Will Topic和Will Message内容。
若User Name和Password都为0,意味着Playload/消息体中,找不到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”,大概如下:
byte 1 | Length MSB (0) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
byte 2 | Length LSB (4) | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
byte 3 | 'a' (0x61) | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 1 |
byte 4 | 'b' (0x62) | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 0 |
byte 5 | 'c' (0x63) | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 1 |
byte 6 | 'd' (0x64) | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 0 |
有一点需要记住,PUBLISH的Payload/消息体中以二进制编码保存。
某刻客户端异常关闭触发服务器会PUBLISH此消息。那么服务器会直接把byte3-byte6之间字符取出,保存为二进制,附加到PUBLISH消息体中,大概存储如下:
byte 1 | 'a' (0x61) | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 1 |
byte 2 | 'b' (0x62) | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 0 |
byte 3 | 'c' (0x63) | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 1 |
byte 4 | 'd' (0x64) | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 0 |
另外,MQTT 3.1协议对Will message的说明很容易引起误解,3.1.1草案已经得到修正。
相关说明:
http://mqtt.org/wiki/doku.php/willmessageutf8_support
https://tools.oasis-open.org/issues/browse/MQTT-2
连接异常中断通知机制
CONNECT消息一旦设置在可变头部设置了Will flag标记,那就启用了Last-Will-And-Testament特性,此特性很赞。
一旦客户端出现异常中断,便会触发服务器发布Will Message消息到Will Topic主题上去,通知Will Topic订阅者,对方因异常退出。
接收CONNECT后的响应动作
接收到CONNECT消息之后,服务器应该返回一个CONNACK消息作为响应:
- 若客户端绕过CONNECT消息直接发送其它类型消息,服务器应关闭此非法连接 若客户端发送CONNECT之后未收到CONNACT,需要关闭当前连接,然后重新连接
- 相同Client ID客户端已连接到服务器,先前客户端必须断开连接后,服务器才能完成新的客户端CONNECT连接 客户端发送无效非法CONNECT消息,服务器需要关闭
CONNACK
一个完整的CONNACK消息大致如下:
Fixed header/固定头部 | |||||||||
byte 1 | Message type (2) | DUP flag | QoS flags | RETAIN | |||||
0 | 0 | 1 | 0 | x | x | x | x | ||
byte 2 | Remaining Length (2) | ||||||||
0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | ||
Variable header/可变头部 | |||||||||
Topic Name Compression Response | |||||||||
byte 1 | Reserved values. Not used. | x | x | x | x | x | x | x | x |
Connect Return Code | |||||||||
byte 2 | Return Code |
可变头部第一个字节为保留,无甚用处。第二个字节为连接握手返回码:
返回值 | 16进制 | 含义 |
0 | 0x00 | Connection Accepted |
1 | 0x01 | Connection Refused: unacceptable protocol version |
2 | 0x02 | Connection Refused: identifier rejected |
3 | 0x03 | Connection Refused: server unavailable |
4 | 0x04 | Connection Refused: bad user name or password |
5 | 0x05 | Connection Refused: not authorized |
6-255 | Reserved for future use |
只有0-5目前被使用到,其他值有待日后使用。一般返回值为0x00,表示连接建立。非法的请求,需要返回相应的数值。
从上面看出,一个CONNACT,四个字节表示。一个正常的CONNACT消息实际内容可能如下: 0x20 0x02 0x00 0x00
若是在私有协议中,两个字节就足够了。
很多时候,客户端和服务器端在没有消息传递时,会一直保持着连接。虽然不能依靠TCP心跳机制(比如SO_KEEPALIVE选项),业务层面定义心跳机制,会让连接状态检测、控制更为直观。
PINGREQ
由客户端发送到服务器端,证明自己还在一直连接着呢。两个字节,固定值。
Fixed header/固定头部 | |||||||||
byte 1 | Message type (12) | DUP flag | QoS flags | RETAIN | |||||
1 | 1 | 0 | 0 | x | x | x | x | ||
byte 2 | Remaining Length (0) | ||||||||
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
客户端会在一个心跳周期内发送一条PINGREQ消息到服务器端。
心跳频率在CONNECT可变头部“Keep Alive timer”中定义时间,单位为秒,无符号16位short表示。
PINGRESP
服务器收到PINGREQ请求之后,会立即响应一个两个字节固定格式的PINGRESP消息。
Fixed header/固定头部 | |||||||||
byte 1 | Message type (13) | DUP flag | QoS flags | RETAIN | |||||
1 | 1 | 0 | 1 | x | x | x | x | ||
byte 2 | Remaining Length (0) | ||||||||
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
服务器一般若在1.5倍的心跳周期内接收不到客户端发送的PINGREQ,可考虑关闭客户端的连接描述符。此时的关闭连接的行为和接收到客户端发送DISCONNECT消息的处理行为一致,但对客户端的订阅不会产生影响(不会清除客户端订阅数据),这个需要牢记。
若客户端发送PINGREQ之后的一个心跳周期内接收不到PINGRESP消息,可考虑关闭TCP/IP套接字连接。
DISCONNECT
客户端主动发送到服务器端,表明即将关闭TCP/IP连接。此时要求服务器要完整、干净的进行断开处理,不能仅仅类似于关闭连接描述符类似草草处理之。 需要两个字节,值固定:
Fixed header/固定头部 | |||||||||
byte 1 | Message type (14) | DUP flag | QoS flags | RETAIN | |||||
1 | 1 | 1 | 0 | x | x | x | x | ||
byte 2 | Remaining Length (0) | ||||||||
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
服务器要根据先前此客户端在发送CONNECT消息可变头部Connect flag中的“Clean session flag”所设置值,再次复习一下:
-
值为0,服务器必须在客户端断开之后继续存储/保持客户端的订阅状态。这些状态包括:
- 存储订阅的消息QoS1和QoS2消息
- 正在发送消息期间连接丢失导致发送失败的消息
- 以便当客户端重新连接时以上消息可以被重新传递。
-
值为1,服务器需要立刻清理连接状态数据。
有一点需要牢记,服务器在接收到客户端发送的DISCONNECT消息之后,需要主动关闭TCP/IP连接。
转载自: http://www.blogjava.net/yongboy/archive/2014/02/09/409630.html
相关推荐
MQTT协议笔记-打印版, 来自:聂永的博客 熟读此笔记, 基本上对MQTT协议会很熟悉! 7. MQTT 3.1.1,值得...2. MQTT协议笔记之连接和心跳 nieyong 2014-02-09 13:41 1. MQTT协议笔记之头部信息 nieyong 2014-02-07 17:35
MQTT 协议 5.0 的技术委员会成员包括 Brian Raymor(Microsoft)、Richard Coppen(IBM)、Andrew Banks(IBM)、Ed Briggs(Microsoft)、Ken Borgendale(IBM)和 Rahul Gupta(IBM)。 MQTT 协议 5.0 的相关文档...
使用自己用C语言实现的MQTT协议来连接阿里云平台。 并利用多线程相关知识,创建一个线程专门接收来自阿里云平台的数据。 在demo中,给出了 CONNECT连接 PUBLISH发布消息 SUBSCRIBE订阅 UNSUBSCRIBE取消订阅 PING保活...
总的来说,利用C语言实现MQTT客户端连接阿里云物联网平台,需要深入理解MQTT协议,熟悉C语言编程,并能够正确使用选择的MQTT库。同时,对阿里云物联网平台的API和规则要有清晰的认识,以便编写出可靠的物联网应用。
为了深入了解和实施此项目,你需要掌握STM32的嵌入式编程,了解基本的TCP/IP协议和MQTT协议,熟悉ESP8266的AT指令集,以及OneNet云平台的API和设备管理。压缩包中的资源将为你提供必要的代码和指导,但具体实现可能...
MQTT协议的英文全称和缩写经常在文档、论坛和开发者交流中被提及,是物联网和移动设备通信中不可忽视的标准协议之一。 在MQTT协议的控制报文中,具体包含了如下报文类型及其作用: - CONNECT:客户端向服务器发起...
总的来说,C++实现MQTT协议主要涉及理解MQTT协议的基本概念,选择合适的客户端库(如Eclipse Paho),以及正确地使用库提供的接口来建立连接、订阅和发布消息。通过这种方式,你可以构建出高效且可靠的物联网应用,...
MQTT协议广泛应用于机器与机器的通信(M2M)和物联网环境(IoT)。 MQTT协议的主要特点有: * 轻量级:MQTT协议的设计思想是轻巧的,易于实现和部署。 * 基于publish/subscribe模式:MQTT协议采用publish/...
MQTT(Message Queuing Telemetry Transport)是一种轻量级的发布/订阅消息协议,主要用于物联网(IoT)领域,尤其适合资源有限的设备和低带宽、高延迟或不可靠的网络环境。这个压缩包文件包含了关于MQTT协议的相关...
**MQTT协议详解** MQTT(Message Queuing Telemetry ...总的来说,MQTT协议凭借其轻量、高效、可靠的特点,已经成为物联网领域中不可或缺的通信协议之一。随着物联网技术的发展,MQTT的应用场景也将不断拓展和深化。
在"4G通过MQTT协议ONENET"的项目中,你将学习到如何结合4G通信技术和MQTT协议,利用ONENET平台实现物联网设备的远程管理和数据交换。这个过程不仅涉及到网络编程,还涵盖了物联网设备的硬件接口、数据处理以及云平台...
MQTT协议因其轻量级、高效的特性,成为物联网和M2M通信领域的首选协议之一。理解其核心概念、操作流程和安全措施,对于开发和维护与MQTT相关的应用程序至关重要。通过提供的中文文档,开发者可以更方便地学习和掌握...
在这个“Android基于MQTT协议的长连接demo”中,我们将深入探讨如何在Android应用中集成和使用MQTT服务。 首先,我们需要一个MQTT服务提供商或自建MQTT服务器,如Mosquitto、HiveMQ等。服务器负责处理客户端的订阅...
在本文中,我们将深入探讨如何在STM32 F103VE型号上实现MQTT协议功能,以及如何与迪发物联(DeveIoT)平台进行连接。 MQTT(Message Queuing Telemetry Transport)是一种轻量级的发布/订阅模式的网络协议,特别...
MQTT协议支持心跳机制,客户端可以通过设置Keep Alive参数来维持连接。如果在Keep Alive时间内没有收发任何报文,服务器会认为客户端断开,并可能触发断线重连。 ### 5. 持久化订阅(Will Message) 客户端在连接...
- MQTT协议支持客户端和服务端架构,通过网络连接进行消息传输。 - 该协议被设计为轻巧、开放、简单、规范,易于实现,并且对资源限制的环境特别友好。 - 协议可以运行在TCP/IP上,或者其他提供有序、可靠、双向...
基于STM32执行的MQTT协议 源程序与资料 基于STM32执行的MQTT协议 源程序与资料 基于STM32执行的MQTT协议 源程序与资料 基于STM32执行的MQTT协议 源程序与资料 基于STM32执行的MQTT协议 源程序与资料 基于STM32执行的...
通过心跳报文`PINGREQ`和`PINGRESP`维持客户端和服务端的连接状态,防止因网络问题导致的连接超时。 7. **Last Will and Testament (LWT)** LWT允许客户端在连接断开时,预先设置一条遗嘱消息,当服务端检测到...
MQTT 协议 3.1.1 中文版 MQTT(Message Queuing Telemetry Transport)是一...MQTT 技术委员会是 OASIS 下面的一个技术委员会,负责制定和维护 MQTT 协议的标准。技术委员会成员包括来自 IBM、StormMQ 等公司的专家。