浅析物联网应用层协议 CoAP 您所在的位置:网站首页 copa协议 浅析物联网应用层协议 CoAP

浅析物联网应用层协议 CoAP

2024-04-18 02:28| 来源: 网络整理| 查看: 265

浅析物联网应用层协议 CoAP

文/徐凯

CoAP 协议专为物联网中资源受限型设备设计,与 HTTP 协议在设计原理、工作模式方面有诸多相似,却又不尽相同。本文针对 CoAP 协议的工作模式、消息结构、软件实现框架等进行了详细的讲解,并列举了具体实例来展示 CoAP 协议的使用。

物联网与 CoAP

CoAP 是 Constrained Application Protocol(受限制的应用协议)的简称。随着近几年物联网技术的发展,越来越多的设备接入互联网,对人来说这非常方便,但对于那些低功耗受限制设备而言却异常困难。在当前由 PC 机和智能手机组成的世界里,信息交换多是通过 TCP 和应用层协议 HTTP 实现的,但让低功耗受限制设备实现 TCP 和 HTTP 协议显然是一个过分而苛刻的要求,为了让这些设备可以接入互联网,CoAP 协议应运而生。CoAP 是一种应用层协议,它运行于 UDP 协议之上,而且非常小巧,最小的数据包仅为4字节。

CoAP 协议概述

CoAP 协议并不能孤立存在,它是 TCP/IP 协议族的一部分。作为一种物联网应用层协议, CoAP 与当前已有的 HTTP 和 MQTT 有所不同,它们之间的区别与联系如图1所示。

CoAP 协议借鉴了 HTTP 协议大量的成功经验,与 HTTP 协议一样均使用请求响应工作模式。通常由客户端发送 CoAP 请求,服务器一旦侦听到该请求便会根据请求内容返回响应码和响应内容,图2可以很好地说明 CoAP 请求响应工作模式的大致流程。虽然 CoAP 协议和 HTTP 协议有很多相似之处,但 CoAP 协议专为低功耗受限制设备设计,比 HTTP 协议简单很多。

CoAP 与 HTTP 比较

狭义上讲,CoAP 协议是二进制版本的 HTTP 协议。CoAP 协议采用完全的二进制首部,而 HTTP1.X 协议采用文本首部,这使得 CoAP 协议的首部更短,传输效率更高。二者均工作于请求响应模式,CoAP 协议为受限制设备而生,一个内存仅有 20KB 的单片机也可以实现 CoAP 服务器或客户端。

CoAP 与 MQTT 比较

大多数情况下,CoAP 协议采用请求响应模式,而 MQTT 协议采用订阅发布模式。CoAP 协议中一般存在两个角色——客户端和服务器端;而 MQTT 协议中一般存在三个角色——订阅者、发布者和消息代理器。MQTT 协议提供三种不同的消息发布服务质量——QoS=0 表示最多一次传输;QoS=1 表示至少一次传输;QoS=2 表示恰好一次传输。从这个角度来说MQTT协议可以保证消息的可靠性。

MQTT 协议具有非常多的优势,但 CoAP 协议与 MQTT 协议却有着不同的应用领域:CoAP 协议适用于传感器数据上报场景,而 MQTT 协议更适用于设备远程控制。另外,相比于 CoAP 协议与 HTTP 协议,MQTT 协议缺少媒体类型,也不太符合 REST 架构,物联网系统应根据实际需求“混合”使用 CoAP 协议和 MQTT 协议。

CoAP 使用 UDP 作为传输层协议

多数网络相关的技术图书中会花费大量篇幅介绍 TCP 的各种特点以及工作机制,例如连接管理、窗口控制与重发控制、流控制和拥塞控制等等。这让大多数读者产生了一个“误解”:传输层只有 TCP 协议才可以保证可靠性,而 UDP 协议“一无是处”或“漏洞百出”。此时我们比较使用 UDP 和 TCP 实现的 ECHO 服务器,ECHO 服务器会原样返回客户端请求的内容,此处的有效数据为两次“Hello World!”,共24个字节。

从图3中可以发现,UDP 协议非常简单直接,请求报文和响应报文仅包含有效数据“Hello World!”,而 TCP 协议还需要额外的连接阶段和关闭阶段。在这种情况下,UDP 的传输效率约为22%,而 TCP 只有4%左右。TCP 协议通过应答机制提高可靠性,UDP 协议也可以通过应答机制提高可靠性,不过 UDP 协议本身没有应答机制,需要更上一层协议来进行确认。CoAP 正是采用这种折中策略,即保证了传输的效率也保证了传输的可靠性。

RFC 文档汇总

俗话说“没有规矩不成方圆”,TCP/IP 相关协议均由 IETF 组织讨论并制定,IETF 组织是一个坚持开发性和适用性的国际标准化组织,该组织产生的标准化文档被成为 RFC(Request For Comment)文档,所有 RFC 文档完全公开,其中不仅记录了协议规范内容,还包括协议的实现和运用的相关信息。若需要熟悉并了解 CoAP 协议,可从 RFC 文档入手,了解它的“前世今生”。CoAP 协议包括核心协议 RFC7252 和扩展协议 RFC7641、RFC6690、RFC7959 等部分,具体内容如表1所示。

CoAP 核心内容 CoAP 首部

和 HTTP1.X 协议不同,CoAP 协议是一个完整的二进制应用层协议,CoAP 首部包括版本编号 Ver、CoAP 报文类型 T、标签长度指示 TKL、CoAP 行为准则 Code、 CoAP 报文编号 Message ID、标签 Token、CoAP 选项和 CoAP 首部与负载固定分隔符 0xFF 等几个部分,其中版本编号 Ver、CoAP 报文类型 T、标签长度指示 TKL、 CoAP 行为准则 Code 和 CoAP 报文编号 Message ID 为必要部分。

版本编号 Ver

CoAP 版本编号区域占2个比特位,在 RFC7252 规范中该区域必须为固定值0b01。

报文类型 T

CoAP 报文类型区域占2个比特位。 CoAP 协议中共定义了4种不同的报文类型,它们分别是:

Confirmable:需要被确认的报文,简称 CON 报文,此时 T=0b00; Non-Confirmable:不需要被确认的报文,简称 NON 报文,此时 T=0b01; Acknowledgement:应答报文,简称 ACK 报文,此时T=0b10; Reset:复位报文,简称 RST 报文,此时 T=0b11。

在 HTTP 协议中,一个 HTTP 请求对应一个 HTTP 响应,但在 CoAP 协议中,如果 CoAP 客户端发送 NON 类型的 CoAP 请求,那么 CoAP 服务器可选择不返回 CoAP 响应。换句话说, CoAP 客户端并不关心请求是否到达 CoAP 服务器。NON 类型报文是 CoAP 协议中一个“特色”,通过这种设计允许设备“犯错”。

标签长度指示 TKL

CoAP 标签长度指示 TKL 占4个比特位,该区域用于指示 CoAP 标签区域的具体长度。由于 CoAP 协议是一个二进制协议,对于非固定长度的区域都需要长度指示。 CoAP 报文中可以具备 CoAP 标签也可以省略 CoAP 标签,若 CoAP 报文中省略 CoAP 标签,那么此时的 TKL=0b0000;若 CoAP 报文中包含 CoAP 标签,那么TKL的取值可以为0b0001(1)、0b0010(2)或0b0100(4)。

准则 Code

CoAP 准则 Code 占1个字节(8个比特位)。虽然该区域仅占8个比特位,但该区域在 CoAP 请求和响应报文中却包含大量有用信息。Code 部分分为高3位 Class 部分和低5位 Detail 部分,为了更方便描述和表达,Code 部分采用 c.dd 的形式描述,其中c的取值范围为0到7,dd 的取值范围为0到31。“c”部分和“dd”部分均采用十进制形式描述,当c等于0时表示 CoAP 请求,当 c 不等于0时表示 CoAP 响应。

CoAP 请求

CoAP 请求报文中 Code 区域用于指示 CoAP 请求方法,CoAP 请求方法和 HTTP 请求方法非常相似,共有4种——GET 方法、POST 方法、PUT 方法和 DELETE 方法。

Code=0.01表示 GET 方法;

Code=0.02表示 POST 方法;

Code=0.03表示 PUT 方法;

Code=0.04表示 DELETE 方法。

和 HTTP 协议不同,CoAP 协议使用二进制方式表示请求方法,这些请求方法仅占1个字节,而在 HTTP 协议中请求方法采用字符串形式表达,“GET”占3个字节,“POST”占4个字节。CoAP 通过这样的设计即减轻了 CoAP 首部长度又和 HTTP 协议保持相同的请求语义。

CoAP 响应

CoAP 响应报文中Code区域用于指示 CoAP 响应状态。 CoAP 响应码和 HTTP 协议中的状态码非常相似,CoAP 协议中定义了4种不同类型的响应报文——空报文、正确响应、客户端错误响应和服务器错误响应。

Code=0.00 表示空报文; Code=2.xx 表示正确响应; Code=4.xx 表示客户端错误; Code=5.xx 表示服务器错误。

和 CoAP 请求方法的设计原理相似, CoAP 响应码也与 HTTP 状态码保持相似的语义,例如 HTTP 协议中 2XX 表示正确响应,而 CoAP 协议中2.xx也表示正确响应。在 CoAP 协议中,“2.05 Content”与 HTTP 协议中“200 OK”的含义几乎相同,但是 CoAP 协议中表示成功仅占1个字节,HTTP 协议中“200 OK”却占6个字节。

报文序号 Message ID

CoAP 报文序号占2个字节,并采用大端格式描述。由于 CoAP 采用 UDP 作为传输层协议,UDP 协议不能保证 CoAP 报文的到达顺序,如果没有报文序号,那么无论客户端还是服务器都无法建立报文之间准确一一对应关系。 CoAP 协议中规定,一组对应的 CoAP 请求和 CoAP 响应必须使用相同的 Message ID。

标签 Token

标签 Token 是一个长度可变的区域,该区域的长度由 TKL 定义,一般为1字节、2字节或4字节。在 CoAP 协议中,标签可以理解为另一种形式的报文序号 Message ID。CoAP 协议中定义了两种不同形式的请求响应工作模式,一种为携带模式,另一种为分离模式,标签在分离模式中发挥重要的作用,但在携带模式中往往可以省略。

选项 Option

CoAP 请求或响应中可携带一组或多组 CoAP 选项, CoAP 选项和 HTTP 协议中的通用首部字段、请求首部字段、响应首部字段和实体首部字段功能相似。CoAP 选项是 CoAP 核心协议中较为复杂的部分,但选项部分也给 CoAP 协议的应用带来了诸多灵活性。CoAP 选项包括Uri-Host、Uri-Port、Uri-Path、Uri-Query、Content-Format、Accept、Etag、If-Match和If-None-Match 等部分。

分隔符

CoAP 首部和 CoAP 负载之间使用固定分隔符 0xFF,占一个字节。在 HTTP 协议中,HTTP 协议首部和负载之间也有一个明显的分隔标记——空行(回车与换行,共占两个字节)。

CoAP 工作模式

CoAP 协议虽然参考了 HTTP 协议的设计思路,但也根据受资源限制设备的具体情况改良了诸多设计细节,增加了很多实用功能。

逻辑分层模式

CoAP 协议的数据交互方式和 HTTP 的请求响应工作方式非常相似。CoAP 请求一般由客户端发起,服务器根据客户端请求中的 URI 定位资源在服务器中的具体位置,通过客户端请求中的请求方法确定如何操作该资源,例如读取资源、创建资源、修改资源或者删除资源等。CoAP 服务器处理请求之后将返回一个 CoAP 响应,CoAP 响应中包含响应码,也有可能包含响应负载。

与 HTTP 协议采用 TCP 作为传输层不同,CoAP 协议使用 UDP 作为传输层协议。UDP 协议并不是一个面向连接的传输层协议,所以 CoAP 协议定义了4种不同的报文类型:CON(Confirmable)、NON(Non-Confiremable)、ACK(Acknowledgement)和RST(Reset)。本质来说,CoAP 协议采用了双层结构——消息层和请求响应层。消息层处理端点之间的数据交换,并为 CON、NON、ACK 和 RST 报文类型提供重传机制, CoAP 协议通过增加消息层的方式弥补 UDP 传输的不可靠性。 CoAP 的逻辑分层结构如图5所示。

可靠传输

CON 报文和 ACK 报文可保证 CoAP 请求响应交互过程的可靠性。当客户端发送一个 CON 报文时,报文的接收者必须返回一条 ACK 报文来确认其已经正确收到了该 CON 报文。需要特别指出的是,CON 报文中的 Message ID 必须和 ACK 报文中的 Message ID 完全一致。在图6中,CoAP 客户端发送一个 CON 报文,报文的 Message ID 为0x7d34,接收者收到该CON报文之后返回一个 ACK 报文,ACK 报文的 Message ID 同为 0x7d34。

非可靠传输

在物联网应用领域并不是所有的发送者报文都需要被确认,NON 报文是一种非常轻量级的替换方案。图7是一个非可靠传输示例, CoAP 客户端发送一条 NON 报文,NON 报文的 Message ID 为0x01a0,而服务器什么都没有返回。

CoAP 实现 CoAP 软件实现框架

随着 CoAP 标准的完善和开源社区地不断努力,市面上出现了多种 CoAP 软件实现框架,这些软件实现框架既可以运行在 Windows 或 Linux 等非受限制平台,也可以运行在诸如Arduino或低功耗无线传感网终端等受限制设备中。除了运行平台的多样性之外,用户还可以使用不同的编程语言实现 CoAP 的各种功能,包括 Java、C、Python 和 Node.js 等。面对不同的平台与不同的使用场景,各种开源实现框架并不一定包括 CoAP 的所有功能,各种开源实现框架往往只是 CoAP 众多标准的一些子集,在实际开发的过程中,需要根据团队的技术偏好和具体需求灵活选择。 CoAP 实现框架的功能概述和实现特性如表2所示。

通过 CoAP 控制树莓派 LED

下文通过一个示例说明如何使用 CoAP 协议控制树莓派LED。此处选择基于 Python 3的 aio CoAP 框架,树莓派作为 CoAP 服务器而另一台Linux主机作为 CoAP 客户端。树莓派中使用 RPi.GPIO 扩展库控制 LED,该 LED 位于树莓派扩展插座的第1脚相连,高电平可打开 LED,低电平可熄 灭LED。 CoAP 客户端通过 JSON 类型负载控制 LED 点亮或熄灭,JSON 负载包含一组 JSON 对象,对象的键名为“value”,键值为整数类型的0或1,0表示 LED 熄灭而1表示 LED 点亮。aio CoAP 树莓派 GPIO 示例如图7所示。

准备工作

一般情况下树莓派3代中已经默认安装了 RPi.GPIO,如果需要把 RPi.GPIO 升级到最新版本,可在树莓派控制台中输入以下指令:

# 升级RPi.GPIO扩展库 sudo pip3 install RPi.GPIO --upgrade CoAP 服务器实现 代码清单 rpi_gpio_server.py #!/usr/bin/env python3 import logging import asyncio import aiocoap.resource as resource import aiocoap import RPi.GPIO as GPIO import json led_pin = 11 class GPIOResource(resource.Resource): def __init__(self): super(GPIOResource, self).__init__() led_status = {'value': 0} self.content = json.dumps(led_status).encode("ascii") async def render_get(self, request): return aiocoap.Message(code=aiocoap.Code.CONTENT, payload=self.content) async def render_put(self, request): print('PUT payload: %s' % request.payload) led_status = json.loads(request.payload.decode()) if led_status['value'] == 1 : print('open led') GPIO.output(led_pin, GPIO.HIGH) else : print('close led') GPIO.output(led_pin, GPIO.LOW) self.content = json.dumps(led_status).encode("ascii") return aiocoap.Message(code=aiocoap.Code.CHANGED, payload=self.content) logging.basicConfig(level=logging.INFO) logging.getLogger("coap-server").setLevel(logging.DEBUG) def main(): # setup gpio GPIO.setmode(GPIO.BOARD) GPIO.setup(led_pin, GPIO.OUT) # Resource tree creation root = resource.Site() root.add_resource(('.well-known', 'core'), resource.WKCResource(root.get_resources_as_linkheader)) root.add_resource(('gpio',), GPIOResource()) asyncio.Task(aiocoap.Context.create_server_context(root)) asyncio.get_event_loop().run_forever() if __name__ == "__main__": main()

CoAP 请求负载为 JSON 格式,例如{“value”:1}。PUT 请求处理函数中 request.payload 为 bytes 类型,可通过 decode 函数把 bytes 类型转换为字符串类型,再通过 json.loads 函数转化为 Python 字典类型,Python 字典类型和 JSON 类型存在直接对应关系。led _ status对应 LED 具体状态,led _ status[‘value’]对应一个 LED 打开或关闭状态,若led _ status[‘value’]等于1则打开 LED。

动手测试 运行 CoAP 服务器 在树莓派中新建一个控制台,在控制台中输入 python3 rpi_gpio_server.py 运行 CoAP 客户端 在Linux PC主机中通过 CoAP -client 工具点亮 LED,可输入以下指令点亮 LED。 coap-client -m put -e {\"value\":1} coap://192.168.0.6/gpio - -m put表示 CoAP 请求方法为 PUT 方法。 - -e {\"value\":1}表示 CoAP 请求负载为字符串形式的{\"value\":1}。

若熄灭LED,可以把请求负载中的“1”改为“0”。

coap-client -m put -e {\"value\":0} coap://192.168.0.6/gpio CoAP 总结

通过本文的学习可以发现,CoAP 协议借鉴了 HTTP 协议的设计思想但并没有盲目抄袭或压缩 HTTP 协议,CoAP 协议具有以下特点:

CoAP 首部非常短,且采用二进制形式表示; CoAP 报文分为 CON 报文、NON 报文、ACK 报文和 RST 报文; CoAP 请求响应可采用携带模式或分离模式; CoAP 协议同样使用 URI 定位资源; CoAP 协议具有和 HTTP 协议语义相似的请求方法和响应码; CoAP 协议支持多种媒体类型。


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有