MQTT 协议学习:004-MQTT建立通信与 CONNECT 、CONNACK 报文
title: protocol-app-mqtt-4-setup-connection
date: 2020-02-03 14:18:51
categories:
tags:
- mqtt
- protocol
背景
title: protocol-app-mqtt-4-setup-connection
date: 2020-02-03 14:18:51
categories:
tags:
- mqtt
- protocol
上一讲 MQTT 协议学习:通信报文的构成介绍了在MQTT通信中,各报文的通信流程;从本讲开始,我们开始介绍实际中使用的报文,以及它们的组成。
CONNECT - 连接请求 报文
客户端到服务端的网络连接建立后,客户端发送给服务端的第一个报文必须是CONNECT, 连接服务端
报文。
在一个网络连接上,客户端只能发送一次CONNECT报文。服务端必须将客户端发送的第二个CONNECT报文当作协议违规处理并断开客户端的连接。
服务端可以检查CONNECT报文的内容是不是满足任何进一步的限制,可以执行身份验证和授权检查。如果任何一项检查没通过,它应该发送一个适当的、返回码非零的CONNACK响应,并且必须关闭这个网络连接。
有效载荷包含一个或多个编码的字段。包括客户端的唯一标识符,Will主题,Will消息,用户名和密码。除了客户端标识之外,其它的字段都是可选的,基于标志位来决定可变报头中是否需要包含这些字段。
CONNECT 报文的固定头
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | MQTT报文类型 (0x1) | 保留位(0x0) |
CONNECT 的 可变报头
CONNECT报文的可变报头按下列次序包含四个字段:协议名(Protocol Name),协议级别(Protocol Level),连接标志(Connect Flags)和保持连接(Keep Alive)。
0040 10 30 00 04 4d 51 54 54 04 c2 00 3c 00 17 6d 6f .0..MQTT...<..mo
0050 73 71 2d 66 5a 4a 69 30 75 51 78 38 4d 6b 55 64 sq-fZJi0uQx8MkUd
0060 55 61 42 52 5a 00 05 61 64 6d 69 6e 00 04 72 6f UaBRZ..admin..ro
0070 6f 74 ot
协议名称 Protocol Name
协议名称(Protocol Name):值固定为字符 “MQTT”的UTF-8编码的字符串,MQTT规范的后续版本不会改变这个字符串的偏移和长度。占用6个字节。
说明 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
---|---|---|---|---|---|---|---|---|---|
协议名 | |||||||||
byte 1 | 长度 MSB (0) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
byte 2 | 长度 LSB (4) | 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 |
协议级别 Protocol Level
协议版本(Protocol Level):对 MQTT 3.1.1 来说,值为 4。占用1个字节。
对于3.1.1版协议,协议级别字段的值是4(0x04)。如果发现不支持的协议级别,服务端必须给发送一个返回码为0x01(不支持的协议级别)的CONNACK报文响应CONNECT报文,然后断开客户端的连接
协议级别 | 说明 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|
byte 7 | Level(4) | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
连接标志 Connect Flags
连接标志字节包含一些用于指定MQTT连接行为的参数。它还指出有效载荷中的字段是否存在。 byte8[0]必须为0,否则断开连接。
bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
连接标志 | User Name Flag | Password Flag | Will Retain | Will Qos | Will Qos | Will Flag | Clean Session | Reserver |
byte 8 | X | X | X | X | X | X | X | 0 |
关于 遗嘱(byte8[5:2]) 有关的知识可以参考《MQTT 协议学习:Retained(保留消息) 和LWT(最后遗嘱)》
关于 会话(byte8[1]) 有关知识可以参考 《MQTT 协议学习:003-MQTT协议中的Qos等级》
用户名标识(User Name Flag):消息体中是1否0有用户名字段。
密码标识(Password Flag):消息体中是1否0有密码字段。
遗嘱消息 Retain 标识(Will Retain):标识遗嘱消息是1否0是 Retain 消息。
如果 遗嘱标识 被设置为0,遗嘱保留(Will Retain)标志也必须设置为0。
如果遗嘱标志被设置为1:
- 如果遗嘱保留被设置为0,服务端必须将遗嘱消息当作非保留消息发布。
- 如果遗嘱保留被设置为1,服务端必须将遗嘱消息当作保留消息发布。
遗嘱消息 QOS 标识(Will Qos):标识遗嘱消息的 Qos,2bit。
如果遗嘱标志被设置为0,遗嘱QoS也必须设置为0(0x00)
遗嘱标识(Will Flag):标识是1否0使用遗嘱消息。
会话清除标识(Clean Session):标识 Client 是0否1建立一个持久化的会话。当 Clean Session 的标识设为 0 时,代表 Client 希望建立一个持久会话的连接,Broker 将存储该 Client 订阅的主题和未接受的消息,否则(设置为1) Broker 不会存储这些数据,同时在建立连接时清除这个 Client 之前存在的持久化会话所保存的数据。持久会话只在 QoS 等级 大于等于 1 的消息 中有效。
下面是引用中文文档的2段描述:
- 如果清理会话(Clean Session)标志被设置为0,服务端必须基于当前会话(使用客户端标识符识别)的状态恢复与客户端的通信。如果没有与这个客户端标识符关联的会话,服务端必须创建一个新的会话。在连接断开之后,当连接断开后,客户端和服务端必须保存会话信息。当清理会话标志为0的会话连接断开之后,服务端必须将之后的QoS 1和QoS 2级别的消息保存为会话状态的一部分,如果这些消息匹配断开连接时客户端的任何订阅。服务端也可以保存满足相同条件的QoS 0级别的消息。
- 如果清理会话(Clean Session)标志被设置为1,客户端和服务端必须丢弃之前的任何会话并开始一个新的会话。会话仅持续和网络连接同样长的时间。与这个会话关联的状态数据不能被任何之后的会话重用。
小技巧:
要确保不丢失连接断开期间的消息,需要使用QoS 1或 QoS 2级别,同时将清理会话标志设置为0。
清理会话标志0的客户端连接时,如果打算在之后的某个时间点重连到这个服务端,客户端连接应该只使用清理会话标志0。当客户端决定之后不再使用这个会话时,应该将清理会话标志设置为1最后再连接一次,然后断开连接。
连接保活(Keep Alive): 设置一个单位为秒的时间间隔,指在 client 传输完成一个控制报文的时刻到发送下一个报文的时刻,client 与 broker 两者之间允许空闲的最大时间间隔。,可以参考:《MQTT 协议学习:Keep Alive 和连接保活》。
- Keep Alive 的最大值为 18 小时 12 分 15 秒(65535秒,实际上就是 0xff, 1个字);
- Keep Alive 值如果设为 0 的话,代表不使用 Keep Alive 机制。
Bit | 7 6 5 4 3 2 1 0 |
---|---|
byte 9 | 保持连接 Keep Alive MSB |
byte 10 | 保持连接 Keep Alive LSB |
CONNECT 的 有效载荷 Payload
CONNECT报文的有效载荷(payload)包含一个或多个以长度为前缀的字段,可变报头中的标志决定是否包含这些字段。
如果包含的话,必须按这个顺序出现:客户端标识符,遗嘱主题,遗嘱消息,用户名,密码。
客户端标识符 Client Identifier
客户端标识符 (Client Id) 必须存在而且必须是CONNECT报文有效载荷的第1个字段,必须用UTF-8进行编码。
这个字段由一个两字节的长度和客户端标识符的实际部分组成,表示为零字节或多个字节序列。长度给出了跟在后面的数据的字节数,不包含长度字段本身占用的两个字节。
服务端使用客户端标识符 (Client Id) 识别客户端。连接服务端的每个客户端都有唯一的客户端标识符(Client Id)。客户端和服务端都必须使用ClientId识别两者之间的MQTT会话相关的状态,它是在处理QoS级别1和2的消息ID中的关键。
服务端必须允许1到23个字节长的UTF-8编码的客户端标识符,客户端标识符只能包含大小写字母和数字
即 : "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
UTF-8 编码字符串的结构 Structure of UTF-8 encoded strings
二进制位 | 7-0 |
---|---|
byte 1 | 字符串长度的最高有效字节(MSB) |
byte 2 | 字符串长度的最低有效字节(LSB) |
byte 3 …. | 如果长度大于0,这里是UTF-8编码的字符数据。 |
服务端可以允许编码后超过23个字节的客户端标识符 (ClientId)。服务端可以允许包含非大小写字母和数字字符的客户端标识符。
服务端可以允许客户端提供一个零字节的客户端标识符 (ClientId) ,如果这样做了,服务端必须将这看作特殊情况并分配唯一的客户端标识符给那个客户端。然后它必须假设客户端提供了那个唯一的客户端标识符,正常处理这个CONNECT报文。
如果服务端拒绝了这个ClientId,它必须发送返回码为0x02(表示标识符不合格)的CONNACK报文响应客户端的CONNECT报文,然后关闭网络连接。
如果客户端提供了一个零字节的客户端标识符,它必须同时将清理会话标志设置为1。
如果客户端提供的Client Id为零字节且清理会话标志为0,服务端必须发送返回码为0x02(表示标识符不合格)的CONNACK报文响应客户端的CONNECT报文,然后关闭网络连接。
对于服务端来说:如果Client Id表明客户端已经连接到这个服务端,那么服务端必须断开原有的客户端连接。
遗嘱主题 Will Topic
如果遗嘱标志被设置为1,有效载荷的下一个字段是遗嘱主题(Will Topic)。遗嘱主题必须UTF-8编码字符串。
这个字段由一个两字节的长度和遗嘱主题的实际部分组成,表示为零字节或多个字节序列。长度给出了跟在后面的数据的字节数,不包含长度字段本身占用的两个字节。
二进制位 | 7-0 |
---|---|
byte 1 | 字符串长度的最高有效字节(MSB) |
byte 2 | 字符串长度的最低有效字节(LSB) |
byte 3 …. | 如果长度大于0,这里是UTF-8编码的字符数据。 |
遗嘱消息 Will Message
如果遗嘱标志被设置为1,有效载荷的下一个字段是遗嘱消息。遗嘱消息定义了将被发布到遗嘱主题的应用消息。
这个字段由一个两字节的长度和遗嘱消息的有效载荷组成,表示为零字节或多个字节序列。长度给出了跟在后面的数据的字节数,不包含长度字段本身占用的两个字节。
二进制位 | 7-0 |
---|---|
byte 1 | 字符串长度的最高有效字节(MSB) |
byte 2 | 字符串长度的最低有效字节(LSB) |
byte 3 …. | 如果长度大于0,这里是UTF-8编码的字符数据。 |
遗嘱消息被发布到遗嘱主题时,它的有效载荷只包含这个字段的数据部分,不包含开头的两个长度字节。
用户名 User Name
如果用户名(User Name)标志被设置为1,有效载荷的下一个字段就是它。用户名必须是UTF-8编码字符串。服务端可以将它用于身份验证和授权。
这个字段由一个两字节的长度和用户名的实际数据组成,表示为零字节或多个字节序列。长度给出了跟在后面的数据的字节数,不包含长度字段本身占用的两个字节。
Bit | 7 - 0 |
---|---|
byte 1 | 数据长度 MSB |
byte 2 | 数据长度 LSB |
byte 3 …. | 如果长度大于0,这里就是数据部分 |
密码 Password
如果密码(Password)标志被设置为1,有效载荷的下一个字段就是它。密码字段包含一个两字节的长度字段,长度表示二进制数据的字节数(不包含长度字段本身占用的两个字节),后面跟着0到65535字节的二进制数据。
Bit | 7 - 0 |
---|---|
byte 1 | 数据长度 MSB |
byte 2 | 数据长度 LSB |
byte 3 …. | 如果长度大于0,这里就是数据部分 |
CONNECT 连接成功以后的响应
服务器对于 CONNECT 请求的验证步骤:
1)网络连接建立后,如果服务端在合理的时间内没有收到CONNECT报文,服务端应该关闭这个连接。
2)服务端必须按照3.1节的要求验证CONNECT报文,如果报文不符合规范,服务端不发送CONNACK报文直接关闭网络连接。
3)服务端可以检查CONNECT报文的内容是不是满足任何进一步的限制,可以执行身份验证和授权检查。如果任何一项检查没通过,按照3.2节的描述,它应该发送一个适当的、返回码非零的CONNACK响应,并且必须关闭这个网络连接。
如果验证成功,服务端会执行下列步骤。
1)如果Client Id表明客户端已经连接到这个服务端,那么服务端必须断开原有的客户端连接。
2)服务端必须按照会执行清理会话的过程。
3)服务端必须发送返回码为零的CONNACK报文作为CONNECT报文的确认响应。
4)开始消息分发和保持连接状态监视。
允许客户端在发送CONNECT报文之后,可以不需要等待服务端的CONNACK报文立即发送其它的控制报文。如果服务端拒绝了客户端的CONNECT请求,那么它不能处理客户端在CONNECT报文之后发送的任何数据。
CONNACK – 确认连接请求 报文
服务端发送CONNACK报文响应从客户端收到的CONNECT报文。服务端发送给客户端的第一个报文必须是CONNACK。
如果客户端在合理的时间内没有收到服务端的CONNACK报文,客户端应该关闭网络连接。
CONNACK 的固定头中的 剩余长度为2。(byte2 = 0x02)
# 抓包情况
MQ Telemetry Transport Protocol, Connect Ack
Header Flags: 0x20, Message Type: Connect Ack
Msg Len: 2
Acknowledge Flags: 0x00
0000 000. = Reserved: Not set
.... ...0 = Session Present: Not set
Return Code: Connection Accepted (0)
0040 20 02 00 00 ...
CONNACK 的 可变报头
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
---|---|---|---|---|---|---|---|---|
连接确认标志 | ||||||||
byte 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | SP |
连接返回码 | ||||||||
byte 2 | X | X | X | X | X | X | X | X |
连接确认标志 Connect Acknowledge Flags
连接确认标志 Connect Acknowledge Flags 只用了第0位。
byte1[7:1] 是保留位且必须设置为0;第0 (也叫 SP)位(byte1[0]) 是当前会话(Session Present)标志。
如果服务端收到清理会话(Clean Session)标志为1的连接,除了将CONNACK报文中的返回码设置为0之外,还必须将CONNACK报文中的当前会话设置(Session Present)标志为0 。
如果服务端收到一个Clean Session为0的连接,当前会话标志的值取决于服务端是否已经保存了ClientId对应客户端的会话状态。
- 如果服务端已经保存了会话状态,它必须将CONNACK报文中的SP设置为1。
- 如果服务端没有已保存的会话状态,它必须将CONNACK报文中的SP设置为0;还需要将CONNACK报文中的返回码设置为0。(此时代表连接成功)
SP使服务端和客户端在是否有已存储的会话状态上保持一致。
一旦完成了会话的初始化设置,已经保存会话状态的客户端将期望服务端维持它存储的会话状态。如果客户端从服务端收到的当前的值与预期的不同,客户端可以选择继续这个会话或者断开连接。客户端可以丢弃客户端和服务端之间的会话状态,方法是,断开连接,将清理会话标志设置为1,再次连接,然后再次断开连接。
如果服务端发送了一个包含非零返回码的CONNACK报文,它必须将当前会话标志设置为0
连接返回码 Connect Return code
连接返回码 Connect Return code 是 :连接返回码字段使用一个字节的无符号值,在下表中列出。
值 | 返回码响应 | 描述 |
---|---|---|
0 | 0x00连接已接受 | 连接已被服务端接受 |
1 | 0x01连接已拒绝,不支持的协议版本 | 服务端不支持客户端请求的MQTT协议级别 |
2 | 0x02连接已拒绝,不合格的客户端标识符 | 客户端标识符是正确的UTF-8编码,但服务端不允许使用 |
3 | 0x03连接已拒绝,服务端不可用 | 网络连接已建立,但MQTT服务不可用 |
4 | 0x04连接已拒绝,无效的用户名或密码 | 用户名或密码的数据格式无效 |
5 | 0x05连接已拒绝,未授权 | 客户端未被授权连接到此服务器 |
6-255 | 保留 |
如果服务端收到一个合法的CONNECT报文,但出于某些原因无法处理它,服务端应该尝试发送一个包含非零返回码(表格中的某一个)的CONNACK报文。如果服务端发送了一个包含非零返回码的CONNACK报文,那么它必须关闭网络连接。
CONNACK 的 有效载荷
无。
DISCONNECT –断开连接
DISCONNECT报文是客户端发给服务端的最后一个控制报文。表示客户端正常断开连接。
DISCONNECT
数据包没有可变头(Variable header)和消息体(Payload),那么,DISCONNECT
报文的全部内容(共2个字节)就是 : 0xe0 0x00
MQTT 协议学习:004-MQTT建立通信与 CONNECT 、CONNACK 报文的更多相关文章
- MQTT 协议学习:007-Keep Alive 连接保活 与 对应报文(PINGREQ、PINGRESP)
背景 keep alive 是 CONNECT 报文中可变头的一部分. 我们提到过 Broker 需要知道 Client 是否非正常地断开了和它的连接,以发送遗愿消息.实际上 Client 也需要能够 ...
- MQTT 协议学习: 总结 与 各种定义的速查表
背景 经过几天的学习与实操,对于MQTT(主要针对 v3.1.1版本)的学习告一段落,为了方便日后的查阅 本文链接:<MQTT 协议学习: 总结 与 各种定义的速查表> 章节整理 MQTT ...
- MQTT协议学习研究 & Mosquitto简要教程(安装和使用)
若初次接触MQTT协议,可先理解以下概念: [MQTT协议特点]——相比于RESTful架构的物联网系统,MQTT协议借助消息推送功能,可以更好地实现远程控制. [MQTT协议角色]——在RESTfu ...
- Tsung MQTT协议简介及MQTT xml文档配置介绍
MQTT协议简介及MQTT xml文档配置介绍 by:授客 QQ:1033553122 1. MQTT协议介绍 MQTT(Message Queuing Telemetry Transport,消息队 ...
- MQTT 协议学习:001-搭建MQTT通信环境,并抓包测试
背景 目的:了解MQTT 通信的有关概念与流程:方便推算某些数据与文档描述是否一致. 为了能够在保证学习质量的前提下,降低配置环境的门槛,我们将服务器搭建在windwos中,实行内网间的MQTT协议访 ...
- MQTT 协议学习:000-有关概念入门
背景 从本章开始,在没有特殊说明的情况下,文章中的MQTT版本均为 3.1.1. MQTT 协议是物联网中常见的协议之一,"轻量级物联网消息推送协议",MQTT同HTTP属于第七层 ...
- MQTT 协议学习:002- 通信报文的构成
背景 之前工作中参与有关协议调试的时候,发现对于协议帧的解析是比较重要的. 参考:<MQTT协议 -- 消息报文格式>.<基于STM32实现MQTT>.<MQTT协议从服 ...
- MQTT 协议学习:008-在STM32上移植MQTT
前言 通过前面了解MQTT有关概念.分析了有关的报文,我们对于这个协议也有了更深的认识.但纸上谈来终觉浅,绝知此事要躬行. 本文参考:<STM32+W5500+MQTT+Android实现远程数 ...
- MQTT协议学习笔记
1.前沿 万物联网的时代即将到来,物联网也由当初的概念开始进一步落实.随着无线网络技术飞速发展,各种设备都可以连接网络,实现远程控制.例如智能家居最近非常火爆,智能插座.智能LED灯.智能摄像头等.在 ...
随机推荐
- NFS文件服务器
NFS文件服务器 NFS介绍 应用场景 NFS安装部署 NFS共享 客户端NFS共享挂载 一.NFS介绍 NFS(Network File System)即网络文件系统,它允许网络中的计算机之间通过T ...
- python爬虫(九) requests库之post请求
1.方法: response=requests.post("https://www.baidu.com/s",data=data) 2.拉勾网职位信息获取 因为拉勾网设置了反爬虫机 ...
- ie9下浏览器 cosole.log()会阻止j下面的s加载
ie9下浏览器 cosole.log()会阻止j下面的s加载,删掉多余的console.log().
- Go语言的map
map一般是以库的方式提供,在C++和C#和JAVA中都需要引用相应的库而Go语言不需要引入库,可以直接方便使用 定义:map是一堆键值对的未排序集合.无序 1.声明变量: map的声明基本上没有多余 ...
- Centos7 nginx的目录结构与nginx主配置文件解析
一.nginx的目录结构 [root@node nginx_116]# ls client_body_temp conf fastcgi_temp html logs proxy_temp ...
- Lucene_solr
1.总结 https://pan.baidu.com/s/1pMAWk0z 密码:ekhx 2.代码 https://pan.baidu.com/s/1nxmTWy1 密码:65ec 3.资料 ...
- Java 后端压缩图片
import java.io.*;import java.util.Date;import java.awt.*;import java.awt.image.*;import javax.imagei ...
- C语言书籍入门---第三章
=======变量和数据类型========= 说 明:字符型 短整型 整型 长整型 单精度浮点型 双精度浮点型 无类型 数据类型:char short int long float d ...
- 新闻网大数据实时分析可视化系统项目——12、Hive与HBase集成进行数据分析
(一)Hive 概述 (二)Hive在Hadoop生态圈中的位置 (三)Hive 架构设计 (四)Hive 的优点及应用场景 (五)Hive 的下载和安装部署 1.Hive 下载 Apache版本的H ...
- 伪类:after,:before的用法
:after和:before是css3中的伪类元素.用法是像元素的前或者后插入元素.以after为例: li:after{ content: ''; color: #ff0000; } 意思是向li元 ...