概述
本篇文章主要介绍了物联网中主流的两个通信协议:CoAP 协议和 MQTT 协议。
CoAP 协议
目前 PC 间的信息交换通常都是通过 TCP 和应用层协议 HTTP 实现。但是对小型设备而言,这种方法的资源占用还是太大了。而 CoAP (Constrained Application Protocol,受限制应用协议)则是为了为了使小型设备也能接入互联网而被设计出来。它是一种运行在 UDP 协议上的应用层协议,特点是非常小巧。最小的数据包仅占 4 字节。
CoAP 协议特点
- 满足首先环境下 M2M 需求的协议
- CoAP 协议基于 UDP
- 异步消息交换
- 轻量级头部,解析复杂度低
- 支持 URI 和 Content-Type
- 能实现简单的数据缓存和代理
- 无状态的 HTTP 映射,可以构建代理服务器,使 CoAP 资源可以用 HTTP 协议访问,也可以使 HTTP 接口实现于 CoAP 协议之上
- 支持 DTLS
CoAP 协议采用 RESTful 架构。REST (Representational State Transfer)是表现层状态转化,表现层指资源的表现层。REST 的意思是:资源在网络中可以以某种表现形式进行状态转移,中间涉及几个概念:
- Resource:资源(数据)
- Representational:某种表现形式,如 JSON、XML 等
- State Transfer:状态变化,通过动词(POST,GET,PUT,DELETE)实现
CoAP 报文结构
CoAP 和其他 TCP/IP 协议一样,以头 (header)的形式出现在负载之前,负载和 CoAP 头之间采用单字节 0xFF 分离。下面进行分部分介绍:
- 消息头:CoAP 协议报文第一行是消息头,占 4 个字节。(消息头为必须,其他部分均为可选,所以最小的 CoAP 包占 4 个字节),消息头包含以下部分:
字段 | 尺寸 | 名称 | 功能描述 |
---|---|---|---|
Ver | 2bits | 版本编号 | 指示 CoAP 协议版本号 |
T | 2bits | 报文类型 | CoAP 协议规定了4种不同形式报文,包括 CON、NON、ACK、RST |
TKL | 4bits | Token 长度 | 表示 Token 长度 |
Code | 8 bits | 表现形式 | Code 在 CoAP 请求保温和响应保温中具有不同的表现形式 |
Message Id | 16 bits | 报文编号 | 用于重复消息检测、匹配消息类型等。单次会话中报文 ID 不变,对话结束后 ID 会被回收利用 |
- 剩余部分:
字段 | 尺寸 | 名称 | 功能描述 |
---|---|---|---|
Token | 不定,由 TKL 决定 | 标识符具体内容 | 是 ID 的另一种表现 |
Options | 不定 | 报文类型 | 通过报文选项可设定 CoAP 主机,CoAP URI, CoAP 请求参数和负载媒体类型等 |
1111 1111 (0xff) | 8 bits(1 byte) | 分割符 | CoAP 报文和具体负载之间的分割符 |
Payload | 不定 | 交互数据 | 真正有用的被交互的数据 |
CoAP 字段具体介绍
下面针对几个字段做具体介绍:
-
code: 占一个字节。共两部分,分别是前 3 位(0 ~ 7)和后 5 位(0 ~ 31)。通过可以表示为 c.dd 形式。其中 0.XX 表示 CoAP 请求的某种方法,2.XX、4.XX、5.XX 表示 CoAP 相应的某种具体表现。具体如下:
- Request 方法(0.XX)
- 0.01 -> GET 方法,用于获得某资源
- 0.02 -> POST 方法,用于创建某资源
- 0.03 -> PUT 方法,用于更新某资源
- 0.04 -> DELETE 方法
- 对于不能识别的请求(不是上面任意一种)返回 4.05 (method not allowed) reponse
- 响应码 - 2.XX
- 2.01 -> Created,用于回应 POST 和 PUT; Response 中可能包括一个操作结果 Representation, not cacheable
- 2.02 -> Deleted:用于会与 POST 和 DELETE,not cacheable
- 2.03 -> Valid:用于指示 Request 中 ETag 指定的 Response 是有效的,Response 必须包含 ETag,不能包含 payload
- 2.04 -> Changed:用于回应 POST 和 PUT:not cacheable
- 2.05 -> Content:回应 GET,reponse 中包含 target resource 的 Representation,cacheable
- 响应码 - 4.XX
- 4.00 -> Bad Request 请求错误,类似 HTTP 400
- 4.01 -> Unauthorized 没有范围权限,类似 HTTP 401
- 4.02 -> Bad Option 请求中包含错误选项
- 4.03 -> Forbidden 服务器拒绝请求,类似 HTTP 403
- 4.04 -> Not Found 服务器找不到资源,类似与 HTTP 404
- 4.05 -> Method Not Allowed 非常请求方法, 类似 HTTP 405
- 4.06 -> Not Acceptable 请求选项和服务器生成内容选项不一致,类似 HTTP 406
- 4.12 -> Precondition Failed 请求参数不足,类似 HTTP 412
- 4.15 -> Unsuppor Conten-Type 请求中的媒体类型不被支持,类似 HTTP 415
- 响应码 - 5.XX
- 5.00 -> Internal Server Error 类似 HTTP 500
- 5.01 -> Not Implemented 服务器无法支持请求内容,类似 HTTP 501
- 5.02 -> Bad Gateway 服务器作为网关时,收到错误响应,类似 HTTP 502
- 5.03 -> Service Unavailable 服务器过载或者维护停机,类似 HTTP 503
- 5.04 -> Gateway Timeout 服务器作为网关时,执行请求发生超时错误,类似 HTTP 504
- 5.05 -> Proxying Not Supported 服务器不支持代理功能
- Request 方法(0.XX)
-
Option: 可选项,可能有 0 个或多个,请求消息和回应消息都可以有 0 个或多个 options。主要用于描述请求或相应对应的各个属性,类似参数或者特征描述,比如是否用到代理服务器,目的主机的端口等。主要分为两类:Critical Option: 接收方必须理解的 Option,否则消息无法正常处理,Elective Option,接收放不识别该 Option 时可以忽略。Option 个是如下所示:
- Option Delta(4bits): Option 在 message 的实例中必须按照编号大小顺序存放,这个值表示 Option 编号的增量(用于找出 Option 名称,可能是 Uri-Path, ETag 等等):所有的 Option 有:
- Content-Format:指定 payload 格式(多媒体类型):text/charset/link-format/xml/exi/josn 等等
- ETag
- Location-Path
- Location-Query
- Max-Age
- Proxy-Uri
- Proxy-scheme
- Uri-Host:目标资源主机
- Uri-Path:目标资源绝对路径
- Uri-Port:目标资源端口
- Uri-Query:制定 Uri 参数的一部分
- Accept
- If-Match
- If-No-Match
- Size1
- Option Length(4bits):Option 长度
- Option Delta Extended (0 ~ 2 bytes)
- Option Length Extended (0 ~ 2 bytes)
- Option Value ( >= 0 bytes)
- Option Delta(4bits): Option 在 message 的实例中必须按照编号大小顺序存放,这个值表示 Option 编号的增量(用于找出 Option 名称,可能是 Uri-Path, ETag 等等):所有的 Option 有:
CoAP 协议逻辑分层模型
CoAP 协议模型分为两层:
- 应用层
- (CoAP)资源请求/响应层(Request/Response):负责传输资源操作的请求和响应
- 可靠传输请求的响应方式:
- 同步可靠相应模式(piggybacked response):通过 Token 匹配
- 异步可靠相应模式(Separate Resonpnse):跨多对 Msg 的请求/响应通过 Token 匹配(先通过 ACK 消息确认收到,一定时间后返回响应)
- 非可靠传输请求的响应方式:通过 Token 匹配,通过 NON 承载的请求可以也选择通过 CON 返回响应
- 可靠传输请求的响应方式:
- (CoAP)消息层(Messages):负责控制端到端的报文交互,包括 Request、Response、Empty Message。
- 两种模式:
- Reliability Mode:可靠消息传输。Confirmable 消息需要 Acknowledgement Message 确认以及通过 Message ID 匹配
- Non-Reliability Mode:非可靠消息传输,不需要 Acknowledgement Message 确认
- 类型(由 T 字段确定):
- CON (Confirmable Message):消息需要确认,由接收方回复 Acknowledge 或 Reset
- NON (Non-confirmable Message):不需要确认,接收方也可有回复 Reset
- ACK (ACK Message):用于向发送放确认 CON 消息已收到,可以携带 Piggybacked Reponse
- RST (Reset Message):用于回复说道的无法处理的 Message
- 两种模式:
- UDP
CoAP 块传输代理和传输安全
- Proxy 是一种在 CoAP Clients 驱动下代表它们执行请求的 Endpoint,按照功能分类有:
- 前向代理(Forward-proxy):被客户端显式指定,转发客户端请求至服务端或下一个代理。也可以在本地 cache 中查询 response 直接返回给 client
- 反向代理(Reverse-proxy):代表服务端执行客户端的请求,反向代理背后一般隐藏多个原始服务端。反向代理根据 request-URI 和配置策略决定将请求发到哪个原始服务端进行请求。也可以从本地 cache 查询响应返回给客户端
- 按照上述两个方式代理可分为两种工作模式:
- CoAP-HTTP Proxying:将 CoAP 响应通过 Proxy 转为 HTTP 响应发至 HTTP 服务端
- HTTP-CoAP Proxying:将 HTTP 请求通过 Proxy 转为 CoAP 请求发至 CoAP 服务端
- Block Transfer 块传输:通常 CoAP 协议的特点是传输小内容,但偶尔也需要传输较大数据。次数可以使用 CoAP 中某个选项设定分块传输大小。此时服务端和客户端可以完成组装和分片两个动作。
- CoAP 安全:CoAP 支持 DTLS 加密。在 CoAP 通信前需要建立 TLS session,让消息作为 DTLS 的 payload 处理,增加了 DTLS header 开销(13 byte),消息除了要进行 ID 匹配外,还要满足处于同一个 DTLS session 内的请求/响应层。请求和响应也是类似。
MQTT 协议
MQTT (Message Queuing Telemetry Transport,消息队列遥测传输协议) 是一个基于客户端和服务端发布/订阅模式的消息的消息传输协议,建立在 TCP/IP 协议上,是发布/订阅型通讯协议。特点有:
- 使用发布/订阅模式,提供了一对多的消息分发,实现应用间的解耦
- 消息传输不需要知道负载内容
- 提供三种等级的服务质量
- 很小的传输消耗和协议数据交换,可以减少网络流量
- 异常连接断开发生时,能通知相关各方
MQTT 发布/订阅模式
- 客户端连接到代理,订阅代理中的任何消息主题,中间涉及两个角色:
- 客户端:可以是发布者或订阅者;可以是一个微控制器或者一个完全成熟的服务器等等(即对性能没有强制要求),在设备上运行 MQTT 库并通过任何网络连接到 MQTT 代理服务器
- 代理服务器(Broker):是任何发布/订阅协议的核心;负责接收所有消息,将消息发送给所有订阅的客户端;需要保持所有持续链接的客户端的会话,包括订阅和丢失的消息;另外需要对客户端进行认证和授权
- 客户端通过将消息和主题发送给代理,发布某个主题范围内的消息:
- 发布:当 MQTT 客户端和代理建立连接后就可以发布消息了,每个消息必须在一个主题下,通常每个消息拥有 payload,其中包含以字节格式传输的实际数据
- 代理然后将消息转发给所有订阅该主题的客户端
- 订阅:订阅包含一个主题过滤器(Topic Filter)和一个最大服务质量等级(QoS);订阅和单个会话关联,单个会话可以包含多于一个订阅,会话中的每个订阅都有一个不同的主题过滤器。
- 发布/订阅解耦了消息的发布者和订阅者的连接
MQTT 主题和服务质量 0、1、2
- 主题:主题是一个 UTF-8 字符串,可以由一个多个主题级别组成,每个主题主题级别之间用主题层级分割符(正斜杠)分割。例如:
"home/bedroom/humidity"
包含三个层级。- 主题通配符:有以下两种
- 单级:
+
,只能用于单个主题层级匹配的通配符,例如A/+/B
可以匹配A/C/B
但是不能匹配A/C/D/B
- 多级:
#
, 可以匹配任意层级通配符,必须是过滤器中最后一个字符,并且前面是分割符。如A/B/#
可以匹配A/B/C
也可以匹配A/B/C/D
- 单级:
- 主题通配符:有以下两种
- 服务质量(QoS)是一种发布者和订阅者之间消息传递的保证协议,MQTT 中 QoS 有三种级别:
- QoS 0:最多分发一次;最低级别:接收者不会发送相应,发送者也不会重试(哪怕消息没送达)
- QoS 1:至少分发一次;确保消息至少送达一次,PUBLISH 报文中包含一个报文标识符,需要 PUBACK 报文确认(使用的最多)
- QoS 2:仅分发一次;最高等级,消息丢失和重复都不可接受,开销大。
MQTT 协议报文结构、连接的建立
MQTT 协议中,数据包由三部分组成:
- 固定头(Fixed Header,必须,占 2 bytes)
- (Byte 1)前 4 位:用于指定控制报文的标志位
- (Byte 1)后 4 位:MQTT 控制报文类型,包括以下类型:
- (Byte 2) 指定剩余长度(可变报头 + 消息体)
名字 | 值 | 报文流动方向 | 描述 |
---|---|---|---|
Reserved | 0 | 禁止 | 保留 |
CONNECT | 1 | 客户端到服务端 | 客户端请求连接服务端 |
CONNACK | 2 | 服务端到客户端 | 连接报文确认 |
PUBLISH | 3 | 两个方向都允许 | 发布消息 |
PUBACK | 4 | 两个方向都允许 | QoS 1消息发布收到确认 |
PUBREC | 5 | 两个方向都允许 | 发布收到(保证交付第一步) |
PUBREL | 6 | 两个方向都允许 | 发布释放(保证交付第二步) |
PUBCOMP | 7 | 两个方向都允许 | QoS 2消息发布完成(保证交互第三步) |
SUBSCRIBE | 8 | 客户端到服务端 | 客户端订阅请求 |
SUBACK | 9 | 服务端到客户端 | 订阅请求报文确认 |
UNSUBSCRIBE | 10 | 客户端到服务端 | 客户端取消订阅请求 |
UNSUBACK | 11 | 服务端到客户端 | 取消订阅报文确认 |
PINGREQ | 12 | 客户端到服务端 | 心跳请求 |
PINGRESP | 13 | 服务端到客户端 | 心跳响应 |
DISCONNECT | 14 | 客户端到服务端 | 客户端断开连接 |
Reserved | 15 | 禁止 | 保留 |
- 可变头(Variable Header,非必须),结构为:
- 协议级别
- 连接标志
- 保持连接
- 消息体(payload, 非必须)
MQTT 协议基于 TCP/IP,客户端和代理都需要 TCP/IP 栈。客户端向代理发送连接消息(CONNECT),代理会响应CONNACK 消息和状态码。连接建立之后只要客户端不发送断开连接命令或失去联系,代理会保持连接。
- CONNECT 消息包括以下部分:
- ClientID,客户端标识符
- Clean Session,占一个 bit。指示是否要建立持续会话,设为 false 时代理提供 QoS 1 或 2 的客户端订阅消息以及所有错过消息;设为 true 时代理不会存储客户端的任何信息,并清除之前持续会话的所有消息
- Username/Passward:用户名和密码
- Will Message:遗嘱消息。当某个客户端恶意断开连接时,代理将遗嘱消息转发给其他客户端;
- KeepAlive:表示一个时间间隔,客户端有规律地向代理服务器发送 PING Request 消息
- CONNACK 消息包括一下部分:
- Session Present Flag:会话表示标志,表示是否建立持久会话
- Connect Acknowledge Flag:确认连接标志(如果不成功根据该标志确认原因)
MQTT 保留消息、遗嘱和心跳
MQTT 保留消息是一条将保留标志 (retained flag)置为 true 的普通 MQTT 消息。Broker 会针对主题依照 QoS 保留最后一条消息。保留消息的意义是保证订阅这无须等待发布者下一条消息时可以立即收到消息;一个主题的保留消息是最新的有效消息。
MQTT 遗嘱消息:MQTT 使用遗嘱功能通知其他客户端某个客户端意外断开连接了。每个客户端可以在连接时制定最新遗嘱(一个具备主题、保留标志、QoS 的普通 MQTT 消息)。
MQTT 心跳包:客户端通过 KeepAlive 参数设置保活周期,在无报文发送时,按 KeepAlive 周期定时发送两字节的 PINGREQ 心跳报文;服务端收到时回复2字节的PINGRESP 报文。如果服务端在 1.5 个心跳周期内没收到客户端发布订阅报文也没有说道 PINGREQ 心跳报文时会主动心跳断开客户端 TCP 连接并发送遗嘱消息(如果客户端指定遗嘱的情况下)。
MQTT 协议除了支持 TCP 作为传输层以外还支持使用 WebSockets 进行传输。如果使用 WebSocket 时,必须使用 binary 模式,并携带子协议 Header mqttv 3.1 或 mqttv3.1.1