mqtt通信协议
阅读数:183 评论数:1
跳转到新版页面分类
网络/通信
正文
一、概述
MQTT(Message Queuing Telemetry Transport Protocal)是一个客户端服务端的发布订阅模式的消息传输协议,适用于机器与机器的通信(M2M)以及物联网环境(IoT)。
目前MQTT主流版本有两个:MQTT3.1.1和MQTT5,MQTT5完全兼容MQTT3.1.1,因为3.1.1在CONNECT的时候指定的Protocol Version为4,所以MQTT3.1.1的后续版本直接变成了5.0。
https://mcxiaoke.gitbooks.io/mqtt-cn/content/mqtt/0301-CONNECT.html
二、MQTT报文格式
每个MQTT消息包含一个固定的报头(fixed header),一些消息还需要一个可变报头(variable header)和有效载荷(payload)
固定报头分为2个字节,第一个字节包含消息类型和标志字段,第二个字节包含剩余长度字段。
第1个字节的前4位表示控制报文的类型。
名字 | 值 | 报文流动方向 | 描述 |
Reserved | 0 | 保留 | |
connect | 1 | 客户端到服务端 | 客户端请求连接服务端 |
connack | 2 | 服务端到客户端 | 连接报文确认 |
publish | 3 | 双向 | 发布消息 |
pubnack | 4 | 双向 | Qos 1消息发布收到确认 |
pubrec | 5 | 双向 | 发布收到(保证交付第一步) |
pubrel | 6 | 双向 | 发布释放(保证交付第二步) |
pubcomp | 7 | 双向 | Qos 2消息发布完成(保证交付第三步) |
subscribe | 8 | 客户端到服务端 | 客户端订阅请求 |
subnack | 9 | 服务端到客户端 | 订阅请求报文确认 |
unsubscribe | 10 | 客户端到服务端 | 客户端取消订阅请求 |
unsuback | 11 | 服务端到客户端 | 取消订阅报文确认 |
pingreq | 12 | 客户端到服务端 | 心跳请求 |
pingresp | 13 | 服务端到客户端 | 心跳响应 |
disconnect | 14 | 客户端到服务端 | 客户端断开连接 |
AUTH | 15 | 双向 | 鉴权 |
这适用于消息的QoS值 大于0,以及需要确认的情况。当DUP位被设置时,可变报头应包括一个消息的ID。
表示传递一个PUBLISH消息所确保的一个级别。
这个标志只用在PUBLISH消息上,当客户端发送一个PUBLISH消息时,如果该标志为1,服务器应该保持该消息直到它已传递到当前订阅的用户那里。
从第2个字节开始,为剩余长度,表示当前报文剩余部分的字节数,包括可变报头和负载的数据,剩余长度不包括用于编码剩余字段本身的字节数。
剩余长度字段使用一个变长度编码方案,对小于128的值它使用单字节编码,更大的值按下面的方式处理,低7位用于编码数据,最高有效位用于指示是否有更多的字节,且按照大端方式进行编码,剩余长度字段最大4个字节,即允许一次消息发送的最大字节数约为256MB。
某些MQTT控制报文包含一个可变报头部分. 它在固定报头和负载之间. 可变报头的内容根据报文类型的不同而不同
很多控制报文的可变报头部分包含一个两字节的报文标识字段, 客户端每次发送一个新的这些类型的报文时都必须分配一个当前未使用的报文标识符, 如果一个客户端要重发这个特殊的控制报文, 在随后重发的报文时, 它必须使用相同的标识符. 当客户端处理完这个报文对应的确认后, 这个报文标识符就释放可重用.
有效载荷是消息的主体,保存消息的内容。
三、MQTT报文类型
客户端与服务端的网络连接建立后,客户端发送给服务端的第一个报文必须是connect报文,在第一次网络连接中,客户端只能发送一次connect报文,服务必须将客户端发送的第二个connect报文当作协议违规处理并断开客户端的连接。
connect报文的可变报头按下列次序包含四个字段:协议名、协议级别、连接标志和保持连接。
(1)Protocol name 协议名是以MQTT的UTF-8编码的字符串。
(2)Protocol version 客户端用8位的无符号值表示协议的修订版本.
(3)Connect flags 总共一个字节,连接标志字段包含一些用于指定MQTT连接行为的参数
clean session(1bit) | 这个二进制位指定了会话状态的处理方式,如果清理会话标志被设置为0,需要做会话保持,如果清理会话标志被设置为1,客户端与服务会话仅持续和网络连接同样长的时间。 |
will flag(1bit) | 遗嘱标志被设置为1,表示如果连接请求被接受了,遗嘱消息必须存储在服务端并且与这个网络连接关联。之后网络连接关闭时,服务端必须发布这个遗嘱消息,除非服务端收到disconnect报文时删除这个遗嘱消息。 |
will qos(2bit) | 用于指定发布遗嘱时使用的服务质量等级。 |
will retain(1bit) | 如果遗嘱消息被发布时需要保留,需要指定这一位的值。 |
user name flag(1bit) | 如果用户标志被设置为0,有效载荷不能包含用户名字段,如果用户名标志被设置为1,有效载荷中必须包含用户名字段。 |
password flag(1bit) | 如查密码标志被设置为0,有效载荷中不能包含密码字段,设置为1,有效载荷中必须包含密码字段。 |
(4)Keep Alive timer 心跳时间,保持连接是一个以秒为单位的时间间隔,表示一个16位的字,它是指客户端传输完成一个控制报文时到发送下一个报文的时候,两者之间允许空闲的最大时间间隔。如果在一个半的心跳时间后还无法完成交互确认,服务端会与客户端断开连接,最大值约为18小时。
服务端发送给客户端的第一报文必须是connack
可变报头
第1个字节是连接确认标志。
第2个字节是连接返回码字段,0表示连接成功,其它表示各类连接状态。
可变报头
可变报头按顺序包含主题名和报文标识符。
topic name 主题名用于识别有效载荷数据应该发布到哪一个信息通道,它必须用UTF-8编码,不能包含通配符。主题名称的最大长度限制为32767个字符数。
puback报文是对Qos1等级的publish报文的响应,可变报头包含等待确认的publish的报文的报文标识符。
pubrec报文是对qos的publish报文的响应,它是qos2等级协议交换的第二个报文。
是对pubrec报文的响应,它是qos 2协议交换的第三个报文。
是对pubrel报文的响应,它是qos2交换的第四个也是最后一个报文。
客户端向服务端发送subscribe报文用于创建一个名多个订阅,每个订阅注册客户端关心的一个或多个主题,subscribe报文也为每个订阅指定了最大qos等级。
suback用于确认它已收到关且正在处理subscribe报文,它包含一个返回码,用于指定subscribe请求的订阅被授予的最大Qos等级。
用于取消订阅主题。
用于确认收到unsubscribe报文。
客户端发送pingreq报文给服务器,用于
(1)在没有任何其它控制报文从客户端发给服务端时,告知服务端客户端还活着。
(2)请求服务端发送响应确认它还活着。
(3)使用网络以确认网络连接还没有断开。
心跳响应,表示服务端还活着,没有可变报头。
是客户端发送给服务端的最后一个控制报文,表示客户端正常断开连接。
四、主题名和主题过滤器
主题层次分隔用于将结构化引入主题名,订阅的主题过滤器可以包含特殊的通配符,允许一次访问多个主题,但是主题名不能使用通配符。
斜杠用于分隔主题和每个层次。
井号(#)这必须是主题过滤的最后一个字符,可以匹配多层主题。例如aaaa/#,不但可以匹配aaaa/bbbb,还可以匹配aaaa/bbbb/cccc/dddd。
只能匹配一层主题,例如aaaa/+可以匹配aaaa/bbbb,但是不能匹配aaaa/bbbb/cccc。
服务端不能将$字符开头的主题名匹配通配符开头的主题过滤器。
(1)所有的主题名和主题过滤必须至少包含一个字符
(2)主题名和主题过滤器是区分大小写的。
(3)主题名和主题过滤器是UTF-8编码,它们不能超过65535字节。
五、mqtt qos
仅发一次包,是否收到完全不管,适合那些不是很重要的数据。
相对于 qos 0,这个交互就是多了一次ack的作用,没有收到ack,就会找时机重发。
(1)如果publish下发出现问题,没有puback回复,服务端将找机会重新下发该msgid的消息。
(2)如果ack回复出现问题,服务器认为没有收到确认,重新找机会下发该msgid的消息。
(3)客户端会收到重复的msgid的消息,需要自行去重。
事实上,publish仍旧可能重复发送多次,但这个协议能够保证协议上的应用层收到准确一次的消息。
(1)接收者收到publish的Qos2的消息后,客户端需要保存一个msgid的记录,并且进入一个状态,即之后不管来几个这个msgid的消息,都不管它,认为是重复的。
(2)接收到publish的Qos2的消息之后事,不能马上投递给上层,而是在本地做持久化的,将消息保存起来。
(3)收到publish的Qos2消息之后,马上回复一个pubrec给发送端。
(4)服务器在收到pubrec之后,应该认为客户端已经收到消息,将publish的消息转入等待pubcomp的阶段,不再重发publish,转而下发pubrel。
(5)客户端收到pubrel之后,正式将消息投递给上层应该层。
(6)投递之后,销毁该msgid,返回pubcomp给服务器,销毁之前的持久化消息。
(7)之后不管服务器来多个pubrel,都没有messageid的记录,只需要回复pubcomp,不需要投递给上层。
分析:
publish下发失败, 服务器重发publish
pubrec上报失败, 服务器重发publish. 这个时候, 客户端仍然是重复收到多冷色publish.
pubrel下发失败, 服务器重发pubrel
pubcomp上报失败,服务器重发pubrel
在pubrel之后, 投递给上层的时候, 还是存在重发投递的, 只是风险小了很多而已, 因为是本地投递 , 如果不使用本地化存储, 程序在中间出问题, 很难处理。
六、MQTT5
http://docs.oasis-open.org/mqtt/mqtt/v5.0/cos01/mqtt-v5.0-cos01.html#_Toc514847900
可以在PUBLISH、CONNECT和带有Return Code的数据包中夹带一个或多个用户属性数据。
在5.0,可以实现Producer/Consumer模式,多个Client可以一起订阅一个共享主题,来自这个主题的消息会依次均衡地发布给这些Client。
3、消息过期(Publication Epiry Interval)
5.0中包含了消息过期的功能,在发布的时候可已认指定这个消息在多久之后过期,Broker不会将已过期的离线消息发送到Client。
在5.05里面,如果你将一条PUBLISH的主题名设为长度为0的字符串,那么Broker会使用你上一次发布的主题。这样降低了多次发布到同一上题的额外开销。
在5.0,CONNACK包含了一些预定的头部数据,用于标识Broker支持哪些MQTT协议功能。
在3.1.1之前,只有Client在主动断开时会向Broker发送DISCONNECT包。如果因为某种错误Broker要断开和Client的连接,它只能直接断开底层的TCP连接,Client则不会知道自己连接断开的原因,也无法解决错误,只是简单的重新连接、被断开、重新连接...
在5.0里面,Broker在主动断开和Client的连接时也会发送DISCONNECT包,同时,从Client到Broker,以及从Broker到Client的DISCONNECT包里面都会包含一个Reason Code.
风不会停息(2023-02-15 15:52:18)
1