MQTT的连接和心跳

最近学习MQTT协议和与Onenet平台的数据交互。MQTT中有几个方法,想借此机会将每个方法的流程梳理一下

一.MQTT数据包结构

在MQTT协议中,一个MQTT数据包由:固定头(Fixed header)、可变头(Variable header)、消息体(payload)三部分构成。MQTT数据包结构如下:

                  MQTT的连接和心跳_第1张图片

(1)固定头(Fixed header)。存在于所有MQTT数据包中,表示数据包类型及数据包的分组类标识。

(2)可变头(Variable header)。存在于部分MQTT数据包中,数据包类型决定了可变头是否存在及其具体内容。

(3)消息体(Payload)。存在于部分MQTT数据包中,表示客户端收到的具体内容。

 

二.CONNECT

     2.1 Fixed Header

Bit

7

6

5

4

3

2

1

0

byte 1

MQTT Packet Type

0

0

0

0

byte2 - 5

Remaining Length(该字段占用1-4个字节)

        MQTT数据包类型:位于Byte1的第7~4位

名字

流向

描述

CONNECT

1

C->S

客户端请求与服务端建立连接

CONNACK

2

S->C

服务端确认连接建立

PUBLISH

3

双向

发布消息

PUBACK

4

双向

收到发布消息确认

PUBREC

5

双向

发布消息收到

PUBREL

6

双向

发布消息释放

PUBCOMP

7

双向

发布消息完成

SUBSCRIBE

8

C->S

订阅请求 

SUBACK

9

S->C

订阅确认

UNSUBSCRIBE

10

C->S

取消订阅

UNSUBACK

11

S->C

取消订阅确认

PING

12

C->S

客户端发送PING(连接保活)命令

PINGRSP

13

S->C

PING命令回复

DISCONNECT

14

C->S

断开连接

       从上面我们可以看到CONNECT应该是1

     2.2 Variable Header

 

Description

7

6

5

4

3

2

1

0

byte 1-2

ProtocolName Length

0

0

0

0

0

0

0

0

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

Byte7

Protocol Level

0

0

0

0

0

0

0

1

Byte8

Connect Flag

User

flag

Password

flag

WillRetain

Flag

WillQos Flag

WillFlag

CleanSession

Flag

Reserve

Byte9-10

KeepAlive

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Clean Session

0,表示如果订阅的客户机断线了,要保存为其要推送的消息(QoS为1和QoS为2),若其重新连接时,需将这些消息推送(若客户端长时间不连接,需要设置一个过期值)。

session需保持的内容包含:

  1. 客户端订阅的topic列表。
  2. 还未完成确认的Qos1、Qos2级别的publish消息

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 namepassword Flag

用于授权,两者要么为0要么为1,否则都是无效。否则认为协议错误,平台将会断开连接。都为0,表示客户端可自由连接/订阅,都为1,表示连接/订阅需要授权。

      2.3 PAYLOAD

 

Description

是否必须存在

格式

Field1

Client Identifier

2字节字串长度 + utf8字串

Field2

UserName

2字节字串长度 + utf8字串

Field3

UserPassword

2字节字串长度 + utf8字串

        与鉴权相关的字段包含client id,username和password,支持鉴权方式。

        设备ID + APIKey(项目ID也需要填写)

字段设置

消息示例

client_id设置为平台创建设备时的设备id

username设置为“项目ID”

password设置为“鉴权信息(auth_info)”

client_id=”123”

username=”433223”

password=Api Key

               项目ID:在平台添加项目时平台生成的ID

               APIKey:在平台上创建产品时生成的APIKey

         2.4 代码流程

                                                              MQTT的连接和心跳_第2张图片

 MQTT_PacketConnect()是组Connect数据包的过程。按照上面说的各个不同字段意思进行组建。

三.CONNACK

           接收到CONNECT消息之后,服务器应该返回一个CONNACK消息作为响应。

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

    3.1 Fixed Header

Bit

7

6

5

4

3

2

1

0

byte 1

MQTT Packet Type

0

0

0

0

byte2 - 5

Remaining Length(该字段占用1-4个字节)

    3.2 Variable Header

 

Description

7

6

5

4

3

2

1

0

byte 1

Acknowledge Flags

0

0

0

0

0

0

0

Sp

byte 2

Return Code

x

x

x

x

x

x

x

x

        返回码说明:

返回码

描述

0

成功

1

协议版本错误

2

非法的clientid

3

服务不可用

4

用户名或密码错误

5

非法链接(比如token非法)

        失败:

                如果connect包不符合协议内容约束,则直接断掉连接,而不需要发送connack包.

                如果鉴权或授权失败,回复一个带非0错误码的connack包.

        成功:

               必须断掉重复的clientid.

               执行cleansession 对应的操作.

               必须回复一个connack,回复码为0.

               开始消息传递,并加入keepalive的监视. 

         PS客户端需要等到服务端的connack报文,才能发送后续的数据包.

四.PINGREQ

      当和平台连接成功后,每隔一段时间clint要向server发送pingreq,证明还连接着。一共两个字节。

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

 五.PINGRESP

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

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

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

你可能感兴趣的:(网络协议,物联网,服务器,c语言)