http://www.blogjava.net/yongboy/archive/2014/02/09/409630.html

2014

前言

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

CONNECT

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

Message Type(1)DUP flagQoS levelRETAIN

byte 10001xxxx

byte 2Remaining Length

Variable header/可变头部

Protocol Name

byte 1Length MSB (0)00000000

byte 2Length LSB (6)00000110

byte 3'M'01001101

byte 4'Q'01010001

byte 5'I'01001001

byte 6's'01110011

byte 7'd'01100100

byte 8'p'01110000

Protocol Version Number

byte 9Version (3)00000011

Connect Flags

User Name FlagPassword FlagWill RetainWill QoSWill FlagClean SessionReserved

byte 101100111x

Keep Alive timer

byte 11Keep Alive MSB (0)00000000

byte 12Keep Alive LSB (10)00001010

Payload/消息体

Client Identifier(客户端ID)1-23个字符长度,客户端到服务器的全局唯一标志,如果客户端ID超出23个字符长度,服务器需要返回码为2,标识符被拒绝响应的CONNACK消息。 处理QoS级别1和2的消息ID中,可以使用到。 必填项。

Will TopicWill Flag值为1,这里便是Will Topic的内容。QoS级别通过Will QoS字段定义,RETAIN值通过Will RETAIN标识,都定义在可变头里面。

Will MessageWill 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才会被读取,此时消息体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”,大概如下:Description76543210byte 1Length MSB (0)00000000

byte 2Length LSB (4)00000100

byte 3'a' (0x61)01100001

byte 4'b' (0x62)01100010

byte 5'c' (0x63)01100011

byte 6'd' (0x64)01100100

有一点需要记住,PUBLISH的Payload/消息体中以二进制编码保存。

某刻客户端异常关闭触发服务器会PUBLISH此消息。那么服务器会直接把byte3-byte6之间字符取出,保存为二进制,附加到PUBLISH消息体中,大概存储如下:Description76543210byte 1'a' (0x61)01100001

byte 2'b' (0x62)01100010

byte 3'c' (0x63)01100011

byte 4'd' (0x64)01100100

另外,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消息大致如下:Description76543210Fixed header/固定头部

byte 1Message type (2)DUP flagQoS flagsRETAIN

0010xxxx

byte 2Remaining Length (2)

00000010

Variable header/可变头部

Topic Name Compression Response

byte 1Reserved values. Not used.xxxxxxxx

Connect Return Code

byte 2Return Code

可变头部第一个字节为保留,无甚用处。第二个字节为连接握手返回码:返回值16进制含义00x00Connection Accepted

10x01Connection Refused: unacceptable protocol version

20x02Connection Refused: identifier rejected

30x03Connection Refused: server unavailable

40x04Connection Refused: bad user name or password

50x05Connection Refused: not authorized

6-255Reserved for future use

只有0-5目前被使用到,其他值有待日后使用。一般返回值为0x00,表示连接建立。非法的请求,需要返回相应的数值。

从上面看出,一个CONNACT,四个字节表示。一个正常的CONNACT消息实际内容可能如下:

0x20 0x02 0x00 0x00

若是在私有协议中,两个字节就足够了。

很多时候,客户端和服务器端在没有消息传递时,会一直保持着连接。虽然不能依靠TCP心跳机制(比如SO_KEEPALIVE选项),业务层面定义心跳机制,会让连接状态检测、控制更为直观。

PINGREQ

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

byte 1Message type (12)DUP flagQoS flagsRETAIN

1100xxxx

byte 2Remaining Length (0)

00000000

客户端会在一个心跳周期内发送一条PINGREQ消息到服务器端。

心跳频率在CONNECT可变头部“Keep Alive timer”中定义时间,单位为秒,无符号16位short表示。

PINGRESP

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

byte 1Message type (13)DUP flagQoS flagsRETAIN

1101xxxx

byte 2Remaining Length (0)

00000000

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

若客户端发送PINGREQ之后的一个心跳周期内接收不到PINGRESP消息,可考虑关闭TCP/IP套接字连接。

DISCONNECT

客户端主动发送到服务器端,表明即将关闭TCP/IP连接。此时要求服务器要完整、干净的进行断开处理,不能仅仅类似于关闭连接描述符类似草草处理之。

需要两个字节,值固定:Description76543210Fixed header/固定头部

byte 1Message type (14)DUP flagQoS flagsRETAIN

1110xxxx

byte 2Remaining Length (0)

00000000

服务器要根据先前此客户端在发送CONNECT消息可变头部Connect flag中的“Clean session flag”所设置值,再次复习一下:值为0,服务器必须在客户端断开之后继续存储/保持客户端的订阅状态。这些状态包括:存储订阅的消息QoS1和QoS2消息

正在发送消息期间连接丢失导致发送失败的消息

以便当客户端重新连接时以上消息可以被重新传递。

值为1,服务器需要立刻清理连接状态数据。

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

java http心跳_MQTT协议笔记之连接和心跳相关推荐

  1. java nio socket长连接_nio实现Socket长连接和心跳

    前段时间用bio方式,也就是传统io实现了socket的长连接和心跳,总觉着服务端开启多线程管理socket连接的方式过于消耗资源,数据并发的情况下可能会影响到性能,因此就尝试使用nio改进原来的代码 ...

  2. JAVA实现长连接(含心跳检测)Demo

    实现原理: 长连接的维持,是要客户端程序,定时向服务端程序,发送一个维持连接包的.        如果,长时间未发送维持连接包,服务端程序将断开连接. 客户端:        Client通过持有So ...

  3. java android长连接_基于Java Socket的自定义协议,实现Android与服务器的长连接(一)...

    一.基础知识准备 在正式给大家介绍自定义协议之前,我们先对网络传输和协议解析的相关知识点做一个基本的介绍,尽管这些知识点我们在学校里学过,但难免会有所遗忘,这里先做一个简单的介绍,以便对后文的内容理解 ...

  4. MQTT协议之连接和心跳

    前言 本篇会把连接(CONNECT).心跳(PINGREQ/PINGRESP).确认(CONNACK).断开连接(DISCONNECT)和在一起. CONNECT 像前面所说,MQTT有关字符串部分采 ...

  5. 聊聊 TCP 长连接和心跳那些事

    1 前言 可能很多 Java 程序员对 TCP 的理解只有一个三次握手,四次挥手的认识,我觉得这样的原因主要在于 TCP 协议本身稍微有点抽象(相比较于应用层的 HTTP 协议):其次,非框架开发者不 ...

  6. 趣谈网络协议笔记-二(第十三讲)

    趣谈网络协议笔记-二(第十三讲) 套接字Socket:Talk is cheap, show me the code 前言 这只是笔记,是为了整理刘超大神的极客时间专栏的只是而存在的! 经常会在网络上 ...

  7. Java TCP/IP Socket 编程 笔记

    http://jimmee.iteye.com/blog/617110 http://jimmee.iteye.com/category/93740 Java TCP/IP Socket 编程 笔记( ...

  8. Java中使用KCP协议

    Java中使用KCP协议_何小成的成长记录-CSDN博客_java kcp Java中使用KCP协议--性能测试及应用_何小成的成长记录-CSDN博客_kcp协议 传统游戏项目一般使用TCP协议进行通 ...

  9. JAVA基础与高级学习笔记

    JAVA基础与高级学习笔记 /记录java基础与高级,除了较简单的内容,没有必要记录的没有记录外,其余的都记录了/ java初学者看这一篇就够了,全文 6万+ 字. JAVA基础 java会出现内存溢 ...

最新文章

  1. 助你进大厂,这些Mysql索引底层知识你是必须知道的。
  2. Docker 容器技术 — 软件架构
  3. 欧蓝德 (660) -(警车内被乔丹体育)_几款豪华SUV的油耗与空间的巅峰对决!欧蓝德还是奇骏...
  4. 干加个偏旁可以变成什么字_面试官:“干”字加一笔,变成什么字?回答王和午字不对...
  5. 转——回归企业家精神本为
  6. [Poj 2187] 计算几何之凸包(二) {更高效的算法}
  7. eigen之eigen中文文档
  8. 大家知道,我的废话很多,所以来凑热闹
  9. python如何上传文件_python请求文件上传
  10. FPGA中case语句4选1数据选择器
  11. 测试minio集群的上传下载速度
  12. GitHub 上 100K+ Star 的前端面试开源项目汇总(进大厂必备)
  13. 求10个数中的最大值及其对应的下标
  14. 前端面试题型汇总(适合应届/社招1年水平)
  15. EXCL模板报盘数据导入
  16. CString 使用详解
  17. Ubuntu14.04+Texlive2014+LYX-Linux下LYX的中文配置方案
  18. JS一元运算符(前++,后++)详解
  19. 活化酯类和马来酰亚胺双功能偶联剂-瑞禧小编ADC抗体偶联
  20. hqchartPy2指标选股 - KDJ选股

热门文章

  1. Oracle常见问题一千问
  2. Assertion-Based Verification01-----Introduction to OVL
  3. php file_put_contents 根目录权限,关于php:file_put_contents权限被拒绝
  4. bi软件用来做什么?
  5. 基于滴滴云搭建 Ceph 开发环境
  6. Windows下设置桌面壁纸的代码(QT版)
  7. 写博客能月入10K?
  8. 如何申请百度地图用户Key
  9. 《庄子·外篇·天运第十四》
  10. 渗透测试-安全岗位面试题总结(含答案)