1. 长连接
互联网推送消息主要基于通信双方建立长连接,从而实现实时推送效果。普通的socket连接对服务器的消耗太大,所以出现了类似MQTT这种轻量级、低消耗的协议来维护长连接。维护长连接需要采用心跳机制,客户端发送一个心跳数据包给服务器,服务器返回给客户端一个心跳应答,从而完成一次客户端-服务器握手,这个握手是让双方都知道他们之间的连接是没有断开的。如果超过一个时间阈值,客户端没有收到服务器的心跳应答,或者服务器没有收到客户端的心跳请求,那么表示通信双方连接已经不存在。对客户端来说,则断开与服务器的连接重新建立一个连接,对服务器来说只要断开这个连接即可。
2. MQTT通信过程
如上图所示,客户端A连接到消息代理(message broker),消息代理返回确认消息。客户B发布消息温度25度,客户A订阅‘温度’,消息代理吧消息推给客户A,客户A发布温度20度,但客户B没有订阅,消息代理不推送。消息B又发布了温度38度,客户A就再次收到订阅的消息38度,最后客户端断开连接。以上过程包含如下:
1 CONNECT – 连接服务端:客户端到服务端的网络连接建立后, 客户端发送给服务端的第一个报文必须是CONNECT报文
2 CONNACK – 确认连接请求:服务端发送CONNACK报文响应从客户端收到的CONNECT报文。 服务端发送给客户端的第一个报文必须是CONNACK。如果客户端在合理的时间内没有收到服务端的CONNACK报文, 客户端应该关闭网络连接。合理的时间取决于应用的类型和通信基础设施。
3 PUBLISH – 发布消息:PUBLISH控制报文是指从客户端向服务端或者服务端向客户端传输一个应用消息。
4 PUBACK –发布确认:PUBACK报文是对QoS 1等级的PUBLISH报文的响应。
5 PUBREC – 发布收到( QoS 2, 第一步):PUBREC报文是对QoS等级2的PUBLISH报文的响应。 它是QoS 2等级协议交换的第二个报文。
6 PUBREL – 发布释放( QoS 2, 第二步):PUBREL报文是对PUBREC报文的响应。 它是QoS 2等级协议交换的第三个报文。
7 PUBCOMP – 发布完成( QoS 2, 第三步):PUBCOMP报文是对PUBREL报文的响应。 它是QoS 2等级协议交换的第四个也是最后一个报文。
8 SUBSCRIBE - 订阅主题:客户端向服务端发送SUBSCRIBE报文用于创建一个或多个订阅。 每个订阅注册客户端关心的一个或多个主题。 为了将应用消息转发给与那些订阅匹配的主题, 服务端发送PUBLISH报文给客户端。 SUBSCRIBE报文也( 为每个订阅) 指定了最大的QoS等级, 服务端根据这个发送应用消息给客户端。
9 SUBACK – 订阅确认:服务端发送SUBACK报文给客户端, 用于确认它已收到并且正在处理SUBSCRIBE报文。
10 UNSUBSCRIBE –取消订阅:客户端发送UNSUBSCRIBE报文给服务端, 用于取消订阅主题。
11 UNSUBACK – 取消订阅确认:服务端发送UNSUBACK报文给客户端用于确认收到UNSUBSCRIBE报文。
12 PINGREQ – 心跳请求:客户端发送PINGREQ报文给服务端的。 用于:1. 在没有任何其它控制报文从客户端发给服务的时, 告知服务端客户端还活着。2. 请求服务端发送 响应确认它还活着。3. 使用网络以确认网络连接没有断开。
13 PINGRESP – 心跳响应:服务端发送PINGRESP报文响应客户端的PINGREQ报文。 表示服务端还活着。
14 DISCONNECT –断开连接:DISCONNECT报文是客户端发给服务端的最后一个控制报文。 表示客户端正常断开连接。
2.1 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 |
|
Payload/消息体 |
|
|||||||||
Client Identifier(客户端ID) 1-23个字符长度,客户端到服务器的全局唯一标志,如果客户端ID超出23个字符长度,服务器需要返回码为2,标识符被拒绝响应的CONNACK消息。 |
|
|||||||||
Will Topic Will Flag值为1,这里便是Will Topic的内容。QoS级别通过Will QoS字段定义,RETAIN值通过Will RETAIN标识,都定义在可变头里面。 |
|
|||||||||
Will Message Will Flag若设为1,这里便是Will Message定义消息的内容,对应的主题为Will Topic。如果客户端意外的断开触发服务器PUBLISH此消息。 |
|
|||||||||
User Name 如果设置User Name标识,可以在此读取用户名称。一般可用于身份验证。协议建议用户名为不多于12个字符,不是必须。 |
|
|||||||||
Password 如果设置Password标识,便可读取用户密码。建议密码为12个字符或者更少,但不是必须。 |
|
2.2 CONNACT
服务端接收到客户端的CONNECT消息之后,应该返回一个CONNACK响应消息:
1、若客户端绕过CONNECT消息直接发送其它类型消息,服务器应关闭此非法连接。若客户端发送CONNECT之后未收到CONNACT,需要关闭当前连接,然后重新连接。
2、相同Client ID客户端已连接到服务器,先前客户端必须断开连接后,服务器才能完成新的客户端CONNECT连接。客户端发送无效非法CONNECT消息,服务器需要关闭。
2.3 PINGREQ
pingreq消息由客户端发送到服务器端,表示客户端还一直存活。两个字节,固定值。
客户端会在一个心跳周期内发送一条PINGREQ消息到服务器端。
心跳频率在CONNECT可变头部“Keep Alive timer”中定义时间,单位为秒,无符号16位short表示。
2.4 PINGRESP
服务器收到客户端的PINGREQ请求之后,会立即响应一个两个字节固定格式的PINGRESP消息。
服务器一般若在1.5倍的心跳周期内接收不到客户端发送的PINGREQ,可考虑关闭客户端的连接描述符。此时的关闭连接的行为和接收到客户端发送DISCONNECT消息的处理行为一致,但对客户端的订阅不会产生影响(不会清除客户端订阅数据),这个需要牢记。
若客户端发送PINGREQ之后的一个心跳周期内接收不到PINGRESP消息,可考虑关闭TCP/IP套接字连接。
2.5 DISCONNECT
客户端主动发送到服务器端,表明即将关闭TCP/IP连接。此时要求服务器要完整、干净的进行断开处理,不能仅仅类似于关闭连接描述符类似草草处理之。 需要两个字节,值固定:
服务器要根据先前此客户端在发送CONNECT消息可变头部Connect flag中的“Clean session flag”所设置值,再次复习一下:
1、值为0,服务器必须在客户端断开之后继续存储/保持客户端的订阅状态。这些状态包括:
存储订阅的消息QoS1和QoS2消息
正在发送消息期间连接丢失导致发送失败的消息
以便当客户端重新连接时以上消息可以被重新传递。
2、值为1,服务器需要立刻清理连接状态数据。
有一点需要牢记,服务器在接收到客户端发送的DISCONNECT消息之后,需要主动关闭TCP/IP连接。