首页 > 其他分享 >MQTT协议笔记之连接和心跳

MQTT协议笔记之连接和心跳

时间:2023-05-21 14:08:35浏览次数:42  
标签:笔记 Will CONNECT MQTT 消息 心跳 服务器 连接 客户端


前言

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

CONNECT

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

 

Description

7

6

5

4

3

2

1

0

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 Message

Will Flag若设为1,这里便是Will Message定义消息的内容,对应的主题为Will Topic。如果客户端意外的断开触发服务器PUBLISH此消息。

长度有可能为0。

在CONNECT消息中的Will Message是UTF-8编码的,当被服务器发布时则作为二进制的消息体。

User Name

Password

可变头部

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

连接标志(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”,大概如下:

 

Description

7

6

5

4

3

2

1

0

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消息体中,大概存储如下:

 

Description

7

6

5

4

3

2

1

0

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消息作为响应:

  1. 若客户端绕过CONNECT消息直接发送其它类型消息,服务器应关闭此非法连接 若客户端发送CONNECT之后未收到CONNACT,需要关闭当前连接,然后重新连接
  2. 相同Client ID客户端已连接到服务器,先前客户端必须断开连接后,服务器才能完成新的客户端CONNECT连接 客户端发送无效非法CONNECT消息,服务器需要关闭

CONNACK

一个完整的CONNACK消息大致如下:

 

Description

7

6

5

4

3

2

1

0

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

由客户端发送到服务器端,证明自己还在一直连接着呢。两个字节,固定值。

 

Description

7

6

5

4

3

2

1

0

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消息。

 

Description

7

6

5

4

3

2

1

0

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连接。此时要求服务器要完整、干净的进行断开处理,不能仅仅类似于关闭连接描述符类似草草处理之。 需要两个字节,值固定:

 

Description

7

6

5

4

3

2

1

0

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”所设置值,再次复习一下:

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

有一点需要牢记,服务器在接收到客户端发送的DISCONNECT消息之后,需要主动关闭TCP/IP连接。

 


标签:笔记,Will,CONNECT,MQTT,消息,心跳,服务器,连接,客户端
From: https://blog.51cto.com/u_2700990/6319167

相关文章

  • JavaScript学习笔记:模块
    前言在js编程中,模块指的是按照一定格式将代码以功能拆分后作为独立文件存在的一个实体。早期的JS并没有规定模块应该如何设计,核心语言也没有针对模块提供相关支持。早期的代码使用IIFE来实现一个模块,它是通过向全局对象添加属性来实现与其他模块来交互的。(function(){v......
  • 为知笔记服务器迁移
    举个例子:原有的老服务器的为知笔记的docker启动脚本是dockerrun--namewiz1--restart=always-it-d-v/mnt/wizdata:/wiz/storage-v/etc/localtime:/etc/localtime-p80:80-p9268:9269/udpwiznote/wizserver那么把原来老服务器的docker映射出来的数据路径目录,也就......
  • 计算机图形学入门——GAMES101第一课笔记
    一、光栅化将三维空间的几何形体显示在屏幕上,就是光栅化(Rasterization)。 虎书中有这么一段话: Theprocessoffindingallthepixelsinanimagethatareoccupiedbyageometricprimitiveiscalledrasterization;即光栅化就是找到所有被几何原型所占据的所有像素点......
  • C#学习笔记 -- 对象初始化语句、索引器、访问器的修饰符
    1、对象初始化语句扩展语法有如下两种扩展语法,第一种当类中没有声明构造器或者声明了无参构造器才能用第二种当类中声明了有参构造器才能用newExampleClass{FieldOrProp=InitProp,FieldOrProp=InitProp,...};newExampleClass(ArgList){FieldOrProp=I......
  • Makefile学习笔记
    ​目录一、概述1.1 Makefile介绍1.2规则1.3核心1.4示例1.5定义命令1.6 make是如何工作的1.7、makefile中使用变量1.8让make自动推导1.9、另类风格的makefile1.10、清空目标文件的规则二、Makefile总述2.1、Makefile里有什么?2.2、 makefile文件名2.3、引用其......
  • JavaScript基础知识笔记
    JavaScript是属于Web的编程语言,对网页行为进行编程。参考教程:https://www.w3school.com.cn/js/index.asphttps://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Language_overviewJavaScript能够改变HTML内容document.getElementById("demo").innerHTML="Hell......
  • C#学习笔记 -- 类的属性
    属性属性代表类实例或类中数据项的成员,使用属性就像写入或读取一个字段,语法相同,从语法上无法区分他们(0)属性的特征是命名的类成员有类型可以被赋值和读取与字段不同,属性是一个函数成员不一定为数据存储分配内存执行代码属性是两个匹配的、命名的......
  • GO性能优化指南笔记
    今天的学习内容时关于GO性能优化的问题。今天的讲师讲的关于GO在项目实操过程中的各种优化案例可以说是目前来说我见过的最好的了,以下我从几个方面说明一下1,GO语言在编程过程都要注意的问题:简单性(以能理解并运行为第一要素,拒绝冗杂的代码,以简单清晰的逻辑编辑代码)可读性(代码是......
  • 树相关知识点--零碎笔记
    深入理解前中后序二叉树的前中后序遍历是什么?前中后序遍历,即二叉树结构的前中后位置前序遍历-即刚刚进入一个节点的时候中序遍历-即进入节点之后未离开节点之前后序遍历-即即将离开第一个节点的时候前中后序是遍历二叉树过程中处理每一个节点的三个特殊时间点前--刚刚进入一个二......
  • redis-cli 使用lua脚本笔记
    前言众所周知,redis可以执行lua脚本,至于为什么要用lua脚本来操作redis,自行百度咯先来讲一下最简单的方式,关于如何在javaspringboot里用lua脚本,请查看我另一篇文章:https://www.cnblogs.com/daen/p/17418024.html更为详细的资料请参考以下文章https://blog.csdn.net/jiayibingd......