IoT开发实战:CoAP卷 学习笔记

第一章

REST风格

REST(具象状态传输)是Roy Thomas Fielding博士于2000年在他的博士论文“Architectural Styles and the Design of Network-based SoftwareArchitectures”中提出来的一种Web软件架构风格。目前在三种主流的Web服务实现方案中,REST模式与复杂的SOAP和XML-RPC相比更加简洁,越来越多的Web服务开始采用REST风格设计和实现。REST是设计风格而不是标准,REST通常基于使用HTTP、URI、XML、JSON和HTML这些现有的协议和标准来实现。
REST风格具有以下特点:
1)资源一般由URI来指定。
2)无状态通信。
3)对资源的操作包括创建、获取、修改和删除等,这些操作对应HTTP的GET、POST、PUT和DELETE方法。
4)资源的表现形式可以是XML、JSON或HTML格式文件。
在这里插入图片描述

CoAP简介

CoAP是受限制的应用协议(Constrained Application Protocol)的简称。CoAP是一种应用层协议,它运行于UDP之上而不是像HTTP那样运行于TCP之上。CoAP非常小巧,最小的数据包仅为4字节。CoAP是TCP/IP协议族的一部分。CoAP和HTTP一样均使用请求/响应工作模式。通常由客户端发送CoAP请求,服务器一旦侦听到该请求便会根据请求内容返回响应码和响应内容。图1-6可以很好地说明CoAP请求/响应工作模式的大致流程。
在这里插入图片描述

MQTT协议简介

MQTT协议由IBM牵头制定,而HTTP与CoAP均由IETF组织制定。MQTT协议采用订阅/发布模式,这与HTTP和CoAP的请求/响应模式存在明显区别。

MQ遥测传输(MQTT[插图])是轻量级基于代理的发布/订阅模式的消息传输协议,MQTT协议开放、简单、轻量级且易于实现。该协议的特点有:
1)使用发布/订阅消息模式,提供一对多的消息发布,解除应用程序耦合。
2)对负载内容屏蔽的消息传输。
3)使用TCP/IP提供网络连接。
4)小型传输,网络传输开销非常小(固定长度的头部是2字节),协议交换最小化以降低网络流量。
5)使用Last Will和Testament特性通知有关各方客户端异常中断的机制。

MQTT协议包含一个主题的概念,MQTT的主题与HTTP中的资源URI较为相似。MQTT协议通过主题对消息进行分类,主题本质上是一个UTF-8编码的字符串,可通过斜杠表示多个层级关系。主题还可以使用通配符进行过滤。其中,“+”可以过滤一个层级,而“#”只能出现在主题最后,表示过滤任意级别的层级。
下面是几个常见的MQTT主题:
1)building-b/floor-5:表示B栋楼第5层。
2)+/floor-5:表示任何一栋楼的第5层。
3)building-b/#:表示B栋楼的所有楼层。

针对不同的应用场景,MQTT协议提供三种不同消息发布服务质量:
QoS=0“最多一次”:服务质量级别QoS0是最快的传输方式,有时称为“触发并忘记”。消息将最多传递一次,或者可能完全不会传递。网络中的传递不会得到确认,并且不会存储消息。如果客户机断开连接或者服务器发生故障,那么消息可能会丢失。以某个时间间隔发送实时数据时,可使用服务质量级别QoS0。丢失单条消息实际上不会产生很大影响,因为之后很快将发送包含较新数据的另一条消息。在此场景中,使用较高服务质量会带来额外成本,却不会获得任何实际优势。QoS=0的情况如图1-7a所示。
❑ QoS=1“至少一次”:使用服务质量级别QoS1时消息会始终至少传递一次。如果发布者收到应答之前消息传递失败,那么一条消息可能会传递多次。该消息必须存储在发布者本地,直到发布者收到关于接收者已发布此消息的确认消息为止。QoS=1的情况如图1-7b所示。
❑ QoS=2“恰好一次”:服务质量级别QoS2是最安全也是最慢的传输方式。消息始终传递恰好一次,并且必须存储在发布者本地,直到发布者收到关于接收者已发布此消息的确认消息为止。使用服务质量级别QoS2时会采用比QoS1更复杂的握手和应答序列,以确保消息不会重复。QoS=2的情况如图1-7c所示。
在这里插入图片描述

与HTTP和CoAP的请求/响应模式不同,MQTT协议这样的订阅/发布模式总是存在三个不同的角色——发布者、代理器(Broker)和订阅者。订阅者向MQTT代理器订阅单个或一系列主题,发布者发布某个主题的具体消息。在MQTT代理器的协调下,所有订阅者将及时收到该主题的消息。MQTT协议的工作过程经常被称为“推送”,在推送过程中某些消息必须保证稳定可靠,而某些消息允许丢失或收到重复内容。为了实现这种灵活可变的机制,MQTT协议提供了以上三种不同的消息发布服务质量。

第三章

IPv4首部

IPv4首部如图3-5所示,IPv4各字段的含义说明如下:
在这里插入图片描述
❑ 版本(4位):该字段表示IP协议的具体版本,对于IPv4来说该值固定为0x04。
❑ 首部长度(4位):此处的首部长度包含了首部选项与填充部分,并且以32位(4字节)为最小单位。如果没有首部选项和填充部分,那么该字段的值固定为0x05,也就是说IPv4的首部长度为20字节。
❑ 服务类型:表示所希望的服务质量。该字段在IPv4中并不常用。
❑ 总长度:表示IP数据包的总长度。该字段占2字节,也就是说单个IP数据包的最大长度为65536字节。❑ 标识:在IP分片中使用,同一个IP数据包的所有分片具有相同的标识编号。
❑ 标志(3位):同样在IP分片中使用。
❑ 分片偏移(13位):同样在IP分片中使用,标识该分片在原始报文中的位置,分片偏移以8字节为最小单位,所以需要将该值乘以8才可以获得原来的位置。
❑ 生存时间:用于防止数据包在路由器之间无限期的流动。每经过一个路由器该值递减1。如果该值减至0,该数据包将被路由器丢弃。
❑ 协议:表示IP负载中的协议类型。例如,协议值为1表示IP负载为ICMP协议,协议值为6表示IP负载为TCP协议,协议值为17表示IP负载为UDP协议。
❑ 首部校验:对于IP首部计算的16位校验和。
❑ 源地址:创建该IP数据分包的设备的32位IP地址。
❑ 目标地址:该IP数据分包的接收设备的32位IP地址。
❑ 选项:长度可变,通常用于实验与诊断。该字段包含安全级别、源路径、路径记录和时间戳等信息。
❑ 填充:如果包括选项部分,那么选项部分的长度必须是32位(4字节)的整数倍,否则需要使用填充部分。

IPv6首部

IPv6首部包含以下内容:
在这里插入图片描述

❑ 版本(4位):表示IP的版本号,此处该字段为固定值0x06。
❑ 流量类别:该字段与IPv4中的服务类型比较相似,由于服务类型在IPv4领域中并没有发挥实际的作用,所以原计划在IPv6中删除该部分,但是出于兼容和其他方面的考虑最后还是在IPv6中保留了该字段。
❑ 流标签(20位):流标签字段用于记录从源节点发送至多个目标节点的一系列IPv6数据包的序列,源节点可以利用该字段标志那些请求IPv6路由器进行特殊处理的数据包序列。流标签可以标识同一个流中的所有数据包,从而保证所有的数据包都可以得到IPv6路由器的相同处理。
❑ 净载荷长度:该字段表示IPv6首部之后的负载长度。如果IPv6数据包有一个或多个扩展头,那么该字段也包括这些扩展头的长度。IPv6的净载荷长度和IPv4的数据包总长度存在明显区别。IPv4的数据包总长度包括IPv4首部和IPv4负载,而IPv6净载荷长度为IPv6的有效负载的长度。换句话说,IPv6的首部长度并不需要指示,它的长度总是为40字节。
❑ 下一个首部:下一个首部有两个作用,如果IPv6数据包只有基本首部而没有扩展首部的话,那么下一个首部就是表示IPv6负载中所承载的协议,这一点与IPv4中的协议类型字段非常相似,而且该字段与IPv4首部中的协议类型使用相同的协议值,如下一个首部取值为6时表示IPv6负载为TCP协议,取值为17时表示IPv6负载为UDP协议,取值为58时表示IPv6负载为ICMPv6协议。图3-8很好地解释了下一个首部和IPv6负载之间的关系。
在这里插入图片描述
❑ 跳数限制:跳数限制字段与IPv4首部中的生存时间非常类似。该字段的值每经过一个路由器便递减1。❑ 源地址:IPv6数据包发起设备的128位IPv6地址。❑ 目标地址:该IPv6数据包的预计接收设备的128位IPv6地址。与IPv4不同,IPv6并没有广播地址,取而代之的是IPv6组播地址。

TCP首部

TCP首部的结构如图3-14所示。
在这里插入图片描述
❑ 源端口号(2字节):源设备上发送数据包进程的16位端口号。一般来说,对于请求数据包,它是一个客户端进程;对于应答数据包,它是一个服务器端进程。
❑ 目标端口号(2字节):目标设备上接收进程的16位端口号。一般来说,对于请求报文,它是一个服务器进程;对于应答报文,它是一个客户端进程。
❑ 序号(4字节):由滑动窗口确认系统使用,作为数据分包的第一字节的序号。在SYN位置为1的情况下,它表示初始序号。
❑ 确认号(4字节):如果ACK位置1,该字段有效并包含设备用于对接收到的数据包进行确认的序号。
❑ 数据偏移(4位):表示数据包的开始位置与TCP首部的开始处偏移多少个32位(4字节)的偏移量。该字段的值乘以4才可以得到字节形式表示的偏移量。
❑ 控制位(6位):用于相关控制信息。这些控制位包括URG紧急位、ACK确认位、PUSH推送位、RST复位位、SYN同步位和FIN结束位。
❑ 窗口:用于流控制,表示该数据包的发送者在一定时间内能从其他设备接收的字节数。
❑ 校验和:用于检测错误的16位校验和。
❑ 紧急指针:如果URG位置1,该字段包含紧急数据后面的“正常”数据的第一字节的序列号。

TCP工作流程

TCP的工作流程如图3-15所示。
在这里插入图片描述

1.建立连接阶段
连接请求报文(SYN报文)由一个SYN位置1的TCP报文构成,该报文中包含了用于连接的初始序列号。服务器收到客户端的连接请求之后必须向客户端发送一个ACK位置1的报文进行确认。由于服务器也必须将自己的初始序号通知客户端,所以为了不发送两个独立的报文,所以把报文中的SYN位置1,这样就出现了一个SYN位和ACK位同时置1的报文。最后客户端收到服务器发来的SYN报文之后,同样需要对其进行确认。这就是“TCP三次握手”。
2.数据传输阶段数据传输阶段与UDP通信类似,在本例中应用数据报文中的PSH位置1,那么TCP将会立即把应用数据发送给服务器,服务器接收到应用数据之后将会返回ACK报文用于接收报文确认,表示服务器完整地接收到客户端的报文。同样,服务器原样返回客户端的请求数据也采用PSH发送、ACK应答的方式。TCP的天生应答的机制可以保证数据传输的可靠性。
3.关闭连接阶段关闭连接从一个FIN位置1的报文开始,一旦设备接收到FIN报文,必须对其进行确认,也就是说接收设备必须返回ACK报文。关闭连接时双方都需要发送FIN报文,同时双方都需要对FIN报文进行确认。这就是“TCP的4次挥手”。

HTTP简介

1.请求报文和响应报文结构
与TCP和UDP首部不同,HTTP首部为一个文本类型首部,而TCP和UDP首部为一个二进制首部。HTTP请求报文和HTTP响应报文的结构稍有不同,请求报文中第一行总是请求行而响应报文的第一行总是状态行。HTTP首部中没有指定首部长度,所以在HTTP首部和负载之间存在一个空行,该空行使用“回车+换行”表示。HTTP的首部结构如图3-23所示。
在这里插入图片描述
2.请求行HTTP首部中的请求行包括HTTP方法、URI和HTTP版本三部分。HTTP首部中的请求行的结构如图3-24上半部分所示。
在这里插入图片描述
❑ HTTP方法:包括GET、POST、PUT和DELETE等。
❑ URI:用于描述服务器资源位置,它与浏览器中使用的URL并不完全等同,URI仅包括域名之后的部分,如URL为http://www.example.com/user/12,那么此时的URI为/user/12。
❑ HTTP版本:指定客户端在其请求中使用的HTTP版本,其合法值为HTTP/1.0或HTTP/1.1。
3.状态行
HTTP首部中的状态行包括HTTP版本、状态码和原因短语三部分。HTTP首部中的状态行的结构如图3-24下半部分所示。
❑ HTTP版本:指定服务器在其响应中使用的HTTP版本,其合法值为HTTP/1.0或HTTP/1.1。
❑ 状态码和原因短语:状态码反映服务器处理客户端请求的结果,该信息用一个3位数字表示,而原因短语使用文本形式表示。
下面是一个非常受欢迎的状态行,如果获取该状态行表示服务器成功处理了请求。
在这里插入图片描述
4. HTTP请求报文和响应报文举例
结合本节入门示例给出一组比较完整的HTTP请求和响应示例,虽然省略了部分内容但是依然可以反映HTTP请求与响应过程的全貌。
(1)HTTP请求报文——异步提交内容
在Windows浏览器中点击“提交”按钮,浏览器使用Ajax方式把内容POST至服务器。
在这里插入图片描述
(2)HTTP响应报文——返回JSON内容
Web服务器通过JSON格式包装响应结果,并返回给浏览器客户端。
在这里插入图片描述

HTTP请求方法

根据HTTP标准,HTTP请求可以使用多种请求方法。HTTP 1.0标准定义了三种请求方法:GET、POST和HEAD方法。HTTP 1.1标准又新增了五种请求方法,即OPTIONS、PUT、DELETE、TRACE和CONNECT方法。如表3-1所示。
在这里插入图片描述

HTTP状态码

HTTP状态码是当客户端向服务器发送HTTP请求时,描述返回的请求结果。借助状态码客户端可以知道服务器是正常处理了请求还是出现了某种错误。如表3-2所示为HTTP状态码和原因短语。
在这里插入图片描述

HTTP首部字段

HTTP首部字段是构成HTTP报文的要素之一,在客户端与服务器之间的HTTP请求响应过程中,无论是请求还是响应都会使用首部字段。HTTP首部字段使用首部字段名和字段值构成,中间使用冒号“:”风格,例如:
在这里插入图片描述
HTTP首部字段按照实际用途分为通用首部字段(General Header Fields)、请求首部字段(Request Header Fields)、响应首部字段(Response HeaderFields)和实体首部字段(Entity Header Fields)4个部分:
❑ 通用首部字段:包括Connection、Data和Upgrade等。
❑ 请求首部字段:包括Host、User-Agent、Accept、If-Match和Range等。
❑ 响应首部字段:包括Age、Location和Set-Cookie等。
❑ 实体首部字段:包括Content-Encoding、Content-Length、Content-Type、Content-Range和Etag等。

第五章 COAP核心

CoAP首部分析

CoAP是一个完整的二进制应用层协议,CoAP首部包括版本编号Ver、报文类型T、标签长度指示TKL、准则Code、报文序号Message ID、标签Token、选项Options、分隔符0xFF和负载Payload等几个部分。
其中版本编号Ver、报文类型T、标签长度指示TKL、准则Code和报文序号Message ID为必要部分,也就是说这几部分一定会出现在CoAP请求或响应中。而标签Token、选项Options、分隔符0xFF和负载Payload为非必要部分,这些部分均为可选部分。CoAP首部结构如图5-1所示。
在这里插入图片描述

版本编号Ver

CoAP版本编号区域占2位。在RFC 7252规范中该区域必须为固定值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)。图5-2a中TKL=0b0001(1)时,Token区域包含长度为1字节的内容;图5-2b中TKL=0b0100(4)时,Token区域包含长度为4字节的内容。
在这里插入图片描述

准则Code

CoAP准则占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响应。

1.CoAP请求

在CoAP请求报文中Code区域用于指示CoAP请求方法,CoAP请求方法和HTTP请求方法非常相似,CoAP中共有4种不同的请求方法—GET方法、POST方法、PUT方法和DELETE方法。
❑ Code=0.01表示GET方法
❑ Code=0.02表示POST方法
❑ Code=0.03表示PUT方法
❑ Code=0.04表示DELETE方法

2.CoAP响应

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

报文序号Message ID

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

标签Token

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

选项Options

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等部分。

分隔符0xFF

CoAP首部和CoAP负载之间使用固定分隔符0xFF,占1字节。在HTTP中,HTTP首部和负载之间也有一个显式的分隔标记——空行(回车与换行,共占2字节)。CoAP中也保留了类似的分隔符,在CoAP首部的其他部分并没有首部长度的指示,且首部长度也不是一个固定值,那么就需要0xFF这样的固定分隔符区分CoAP首部和CoAP负载。
在一个具有CoAP负载的报文中,固定分隔符将会出现在标签Token或者CoAP选项Options之后。如果固定分隔符出现在标签Token之后,由于CoAP首部中有标签长度指示,即使标签Token区域中最后一字节为0xFF也不会影响报文的解析;如果固定分隔符出现在选项Options之后,由于每个单独的选项也具有长度指示,那么即使最后一个选项以0xFF结尾也不会影响报文解析。在上述两种极端情况下,将在CoAP报文中连续出现两个0xFF,但不会影响报文的相关解析。
在图5-3中,Token区域的最后一个字符为0xFF,在CoAP报文中连续出现了两个0xFF,但由于TKL指示Token区域的长度为4字节,通过TKL就可以正确识别出哪一个0xFF属于Token区域,哪一个0xFF属于CoAP首部与CoAP负载之间的分隔符。
在这里插入图片描述

负载Payload

CoAP负载包含与具体应用直接相关的内容。CoAP负载包含多种不同的媒体类型,包括二进制负载、文本负载、XML负载、JSON负载、CBOR负载等。CoAP所支持的媒体类型中并不包括HTML类型,换句话说CoAP并不包含与网页相关的内容,这点与HTTP存在很大的区别。

CoAP工作模式

逻辑分层结构

CoAP的数据交互方式和HTTP的请求/响应工作方式非常相似。CoAP请求一般由客户端发起,服务器根据客户端请求中的URI定位资源在服务器中的具体位置,通过客户端请求中的请求方法确定如何操作该资源,如读取资源、创建资源、修改资源或者删除资源等。CoAP服务器处理请求之后将返回一个CoAP响应,CoAP响应中包含响应码,也有可能包含响应负载。
与HTTP采用TCP作为传输层不同,CoAP使用UDP作为传输层协议。UDP并不是一个面向连接的传输层协议,所以CoAP定义4种不同的报文类型:CON(Confirmable)、NON (Non-Confirmable)、ACK(Acknowledgement)和RST(Reset)。本质来说,CoAP采用了双层结构——消息层和请求/响应层。消息层处理端点之间的数据交换,并为CON、NON、ACK和RST报文类型提供重传机制,CoAP通过增加消息层的方式弥补UDP传输的不可靠性。CoAP的逻辑分层结构如图5-4所示。
在这里插入图片描述

报文类型

CoAP定义了四种不同的报文类型,通过这四种不同的报文类型至少可以组合成可靠传输和非可靠传输两种工作方式。
❑ Confirmable Message(CON):在请求/响应交互过程中,CON报文需要被接收者确认。每一个CON报文必须要对应一个准确的ACK报文或RST报文,如果在规定的时间内客户端未接收到ACK报文或RST报文,那么客户端将会触发一次“重传”。
❑ Non-Confirmable Message(NON):相比于CON报文,NON报文的约束条件要宽松许多。单个NON报文不需要对应一个准确的ACK或RST报文。在一些传感器数据上传应用场景中NON报文较为常用,服务器对NON报文的处理也较为灵活。若服务器收到一个来自客户端的NON报文类型的CoAP请求,服务器可选择不返回响应;同时客户端也不会因为一定时间内没有收到来自服务器的ACK类型报文而触发重传机制。
❑ Acknowledgement Message(ACK):ACK报文用于确认CON报文。ACK报文的报文序号(Message ID)需要和CON报文的报文序号保持严格一致,ACK报文可以在一定程度上保证CoAP传输的可靠性。ACK报文中可以不包括响应负载,可能为空。CoAP请求/响应过程定义了携带模式和分离模式,ACK报文负载为空的情况一般出现在分离模式中。
❑ Reset Message(RST):若服务器接收到一个CON报文,但由于报文中上下文缺失导致服务器无法处理该报文,那么服务器将会返回一个RST报文。RST报文的报文序号需要与CON报文的报文序号保持严格一致,而且RST报文的负载一定为空。

请求/响应模式

虽然CoAP参考了很多HTTP的优秀特性,但在具体实现过程中仍和HTTP存在区别。在CoAP中包含至少三种不同的请求/响应工作模式——携带模式、分离模式和非确认模式,其中携带模式和HTTP中的请求/响应模式最为相近。下面通过一个获取温度传感器检测结果的示例说明携带模式、分离模式和非确认模式的区别和联系。从5.2节中可获知,CoAP首部中一定存在报文序号Message ID区域,该区域占2字节,而在CoAP首部中Token区域为可选择项,其内容和长度均由应用决定。在本小节的示例中,包含以下假定条件:
❑ 假定Token的长度为1字节。
❑ 假设温度传感器在服务器的资源URI为“temperature”。
❑ 通过GET方法可获取温度传感器检测结果。
❑ 检测结果采用文本格式表示。

1.携带模式

在图5-7中,CoAP客户端发送一次GET请求,该请求采用CON报文格式,报文序号Message ID为0xbc90, Token为0x71; CoAP服务器接收到GET请求之后返回ACK报文,ACK报文的响应码为“2.05”,表示服务器正确处理请求并返回正确响应,ACK报文的Message ID和Token与GET请求中的Message ID和Token完全相同。此时ACK报文中包含响应负载,响应负载为文本形式。
在这里插入图片描述
携带模式可简单理解为ACK报文中包含响应负载,例如此处的温度传感器检测结果——“22.5 C”(字符串形式描述)。携带模式是最常用的请求/响应工作模式,在这种工作模式下,Message ID和Token的作用几乎相同。为了减少CoAP报文长度,更多的情况下只使用Message ID便可。

2.分离模式

在图5-8中,CoAP客户端发送一次GET请求,该请求同样采用CON报文格式,Message ID为0x7a10, Token为0x73; CoAP服务器接收到GET请求之后立刻返回ACK报文,但ACK报文中并没有包含任何响应负载。一段时间之后,服务器再向客户端发送一个CON报文,该报文的Message ID为0x23bb, Token为0x73,该CON报文的响应码为“2.05”,表示服务器正确处理请求并返回正确响应。该CON报文中还包括响应负载,响应负载为文本形式的“22.5 C”。客户端收到CON报文之后,返回一个ACK报文进行确认。
在这里插入图片描述
分离模式中总共产生两组CON报文和ACK报文。相比于携带模式,分离模式需要进行4次交互,而携带模式只需要2次交互。此处4次交互过程中产生了两个MessageID—0x7a10和0x23bb,但仅包括一个Token—0x73,通过这个例子也可以看出Message ID和Token的区别,Message ID更多地用于报文确认,而Token带有更多的“应用”含义,可以理解为应用确认。CoAP客户端试图获取温度传感器资源,使用一个Token“标记”该应用动作,而CoAP服务器返回温度传感器检测结果时再把该Token“标记”到响应内容中,这样CoAP客户端就可以通过该Token识别应用动作。

3.非确认模式

非确认模式是CoAP中最为松散的请求/响应工作模式。在图5-9中,CoAP客户端发送一次GET请求,但该报文并不是CON报文,而是一个并不需要确认的NON报文。CoAP服务器接收到该NON报文之后,同样返回一个NON报文。非确认模式的其他部分与携带模式、分离模式非常相似。
在这里插入图片描述

CoAP重传机制

(CoAP请求丢失处理、CoAP响应丢失处理、最大重传次数、最大传输耗时、最大等待时间。)

  1. CoAP请求丢失
    2.
  2. CoAP响应丢失
    在这里插入图片描述

传输参数说明

CoAP报文重传一般由ACK_TIMEOUT、ACK_RANDOM_FACTOR和MAX_RETRANSMIT三个参数控制。CoAP传输参数见表5-1。
在这里插入图片描述
如果CoAP客户端首次发送CON报文之后,在ACK_TIMEOUT到ACK_TIMEOUTACK_RANDOM_FACTOR时间内仍没有收到ACK报文或者RST报文,那么CoAP客户端将重新发送一次CON报文。ACK_TIMEOUT的典型值为2秒。ACK_RANDOM_FACTOR是一个不能小于1.0的随机数,该参数的典型值为1.5。MAX_RETRANSMIT定义最大重传次数,该参数的典型值为4。在极端情况下,CoAP客户端将会执行4次重传,加上第一次CON报文,总共将产生5次CoAP请求。
CoAP重传机制由超时时间和重传计数器两个参数控制。对于一个CON报文来说,初始的超时时间为ACK_TIMEOUT到ACK_TIMEOUT
ACK_RANDOM_FACTOR之间的随机数,如果ACK_TIMEOUT采用典型值2秒,ACK_RANDOM_FACTOR采用典型值1.5,那么初始超时为2~3秒,如2.45秒、2.95秒都是合理的初始超时时间。重传计数器从0开始递增,一旦在规定的时间内没有收到ACK报文或RST报文,那么CON报文将会被重新发送,重传计数器自动增加。下一次重传超时时间为上一次超时时间的两倍,如初始重传时间为2.45秒,那么第一次重传的超时时间为4.90秒。在CoAP协议中重传超时时间将越来越大。
我们通过一个更加具体的例子说明CoAP重传机制,假设ACK_TIMEOUT取值为2,ACK_RANDOM_FACTOR取值为1.3, MAX_RETRANSMIT取值为4,那么超时等待时间见表5-2。
在这里插入图片描述

最大传输耗时(MAX_TRANSMIT_SPAN)

假设ACK_TIMEOUT采用典型值2秒,ACK_RANDOM_FACTOR采用典型值1.5,MAX_RETRANSMIT同样采用典型值4,超时重传时间的初始值采用随机结果的上限也就是ACK_TIMEOUTMAX_RETRANSMIT。最糟糕的情况下,CoAP客户端传输一次正常的CON报文并且重传4次CON报文,在该过程中相邻CON报文的时间间隔分别为ACK_TIMEOUT1.5、2ACK_TIMEOUT1.5、4ACK_TIMEOUT1.5、8ACK_TIMEOUT1.5,总计时间为:
MAX_TRANSMIT_SPAN=ACK_TIMEOUT1.5(1+2+4+8)=45(秒)
图5-12可以很好地说明该重传过程。CoAP中最大传输耗时的典型值为45秒,在实际使用过程中,由于ACK_RANDOM_FACTOR为一个1~1.5之间的随机值,所以最大传输耗时往往小于45秒。
在这里插入图片描述

最大等待时间(MAX_TRANSMIT_WAIT)

当CoAP客户端执行最后一次重传之后并不意味着整个传输过程已经结束,CoAP客户端还需要等待最后一次超时时间。最后一次超时时间为16ACK_TIMEOUT1.5,那么从CoAP客户端发送第一次CON报文到最后结束等待总共消耗的时间为:
MAX_TRANSMIT_WAIT=ACK_TIMEOUT1.5(1+2+4+8+16)=93(秒)
图5-12可以说明最大传输耗时(MAX_TRANSMIT_SPAN)与最大等待时间(MAX_TRANSMIT_WAIT)之间的联系与区别。

CoAP方法

CoAP共有四种不同的请求方法:GET方法、POST方法、PUT方法和DELETE方法。每一个不能被服务器识别或未被服务器支持的方法通常会导致一个4.05响应码。

GET

GET方法用于查询资源,该资源通过请求中的URI进行识别。由于单个资源服务器可能使用不同的媒体类型展示某资源,所以GET请求中可包括一个或多个Accept选项用于指示客户端期望获得的媒体类型。若GET请求被正确执行,2.05(Content)或2.03(Valid)响应码将会出现在CoAP响应报文中。无论是在HTTP应用中还是在CoAP应用中,GET总是最常用的方法。

POST

POST方法要求CoAP请求中的资源描述内容被CoAP服务器处理。如果POST请求被正确执行,那么在服务器上将创建一个新资源。一旦在服务器上创建一个新资源,服务器返回的响应中将包括一个2.01(Create)响应码并且响应负载中包括一个新建资源的URI,该URI可使用一个或多个Location-Path和Location-Query定义。如果POST请求被成功执行但未在服务器上创建新资源,那么响应消息中应包含一个2.04(Changed)响应码;如果POST请求被成功执行但导致服务器上的目标资源被删除,那么响应消息中应包含一个2.02 (Deleted)响应码。

PUT

PUT方法要求服务器根据CoAP请求中的URI和CoAP请求负载中的资源描述信息更新服务器内的指定资源。如果资源已经存在,那么服务器将会更新指定资源,同时返回一个包含2.04(Changed)响应码的CoAP响应;如果资源不存在,那么服务器将会根据请求中的URI创建一个新的资源,同时返回一个包含2.01(Created)响应码的CoAP响应。如果该资源既不能被创建也不能被更新,那么服务器将返回一个合适的错误响应码。

DELETE

DELETE方法要求服务器根据CoAP请求中的URI删除服务器中的指定资源。如果资源删除成功,那么服务器应返回一个包含2.02(Deleted)响应码的CoAP响应。

CoAP响应码

与HTTP类似,CoAP也包含三种不同类型的响应码—2.xx Success(成功)、4.xxClient Error(客户端错误)和5.xx Server Error(服务器错误)。CoAP响应码和HTTP状态码存在很强的对应关系。

正确响应

若服务器正确执行CoAP请求,将会返回一个表示执行正确的响应码,这些响应码包括2.01 Created、2.02 Deleted、2.03 Valid、2.04 Changed和2.05Content。
2.01 Created
类似于HTTP 201 Created,该响应码只能应用于POST或PUT响应中,表示服务器创建了一个新的资源。2.01 Created和2.04 Changed响应码均可应用于POST或PUT响应中。但2.01 Created表示创建资源,资源的状态从无到有;而2.04Changed表示更新资源,资源的状态从旧到新。
2.02 Deleted
类似于HTTP 204 No Content,该响应码只能应用于DELETE响应中,表示服务器成功删除一个资源。
2.03 Valid
类似于HTTP 304 Not Modified,2.03 Valid和2.05 Content常用于GET响应中,但两者存在一些区别,包含2.03 Valid响应码的CoAP响应中负载内容一般为空,而包含2.05 Content响应码的CoAP响应中一般包含具体负载。
2.04 Changed
类似于HTTP 204 No Content,该响应码只能应用于POST和PUT响应中,表示服务器更新了某个资源。
2.05 Content
类似于HTTP 200 OK,该响应码只能应用于GET响应中,这可能是客户端最愿意看到的响应码。

客户端错误

4.00 Bad Request
类似于HTTP 400 Bad Request,4.00 Bad Request表示通用客户端状态指示,当使用其他4.XX无法描述客户端错误时可直接使用4.00 Bad Request。
4.01 Unauthorized
该响应码表示客户端未获取权限去执行相关操作。客户端在没有提供适当的身份凭证的情况下向服务器中受保护的资源发送请求时,若服务器不想承认该资源存在于服务器,可返回4.04 Not Found代替4.01 Unauthorized。
4.02 Bad option
该响应码表示请求中包含一个或多个未能识别的选项。
4.03 Forbidden
类型于HTTP 403 Forbidden,该响应码表示客户端请求格式正确,但是服务器并不愿意执行该请求。
4.04 Not Found
类似于HTTP 404 Not Found,4.04 Not Found表示服务器无法寻找到地址资源。
4.05 Method Not Allowed
类似于HTTP 405 Method NOT Allowed,4.05 Method Not Allowed表示客户端使用一个未经服务器定义的CoAP方法访问某资源。例如该资源仅支持POST方法,但是客户端使用PUT方法修改资源,此时CoAP服务器将返回4.05 Method NotAllowed。
4.06 Not Acceptable
类似于HTTP 406 Not Acceptable,即客户端请求中带有Accept选项,而服务器无法根据Accept选项返回指定内容。例如服务器内的某资源仅支持JSON格式或文本格式,但CoAP客户端却希望获得XML格式负载,由于CoAP服务器无法满足CoAP客户端的期望,只能返回4.06 Not Acceptable。
4.12 Precondition Failed
类似于HTTP 412 Precondition Failed,客户端在请求中定义了一个或多个先决条件,例如在请求中增加If-Match选项,而服务器只有在满足特定条件下才可以处理该请求,若特定条件无法满足,服务器将返回4.12 Precondition Failed。

服务器错误

5.00 Internal Server Error
类似于HTTP 500 Interal Server Error,5.00 Internal Server Error是一个通用的服务器错误响应,若使用其他5.XX响应码无法正确描述错误可直接使用5.00 InternalServer Error。
5.01 Not Implemented
类似于HTTP 501 Not Implemented,5.01 Not Implemented表示CoAP服务器不支持请求中规定的某些特性。
5.03 Service Unavailable
类似于HTTP 503 Service Unavailable,5.03 Service Unavailable意味着虽然CoAP服务器正常启用,但是某些应用并没有正常工作。例如,众多的CoAP客户端向服务器发送CoAP请求,CoAP服务器无法及时处理所有的请求,在这种情况下CoAP服务器可返回5.03 Service Unavailable响应码。

CoAP选项

(选项格式、URI选项、Content-Format选项、Accept选项、Etag选项、If-Match选项、If-None-Match选项。)

选项格式

CoAP选项实例由选项偏移量(OptionDelta)、选项长度(Option Length)和选项值(Option Value)组成。CoAP选项的具体格式如图5-13所示。CoAP中不能直接确定选项编号,选项编号必须由上一个选项编号和本次选项偏移量计算得到。
在这里插入图片描述

1.选项偏移量
4位无符号整数。其中0~12用于指示选项的偏移量,13、14和15具有特殊含义。
❑ 13:Option Delta extended区域定义一个8位无符号整数,此时的选项偏移量应为该8位无符号整数+13。
❑ 14:Option Delta extended区域定义一个16位无符号整数,此时的选项偏移量应为该16位无符号整数+269。
❑ 15:保留为将来使用。
选项偏移量描述了当前选项编号与之前选项编号之间的差值,换句话说选项编号可以由当前选项编号与之前的选项编号计算得到。例如,上次选项编号为11(Uri-Path),当前选项偏移量为4,那么当前的选项编号为15(Uri-Query)。
2.选项长度
4位无符号整数。0~12用于指定选项长度,13、14和15具有特殊含义。
❑ 13:Option Length extended区域定义一个8位无符号整数,此时的选项长度应为该8位无符号整数+13。
❑ 14:Option Length extended区域定义一个16位无符号整数,此时的选项长度应为该16位无符号整数+269。
❑ 15:保留为将来使用。
3.选项值
在CoAP中选项值包含四种数据类型—Empty、Opaque、Uint和String。
❑ Empty:选项值长度为0。
❑ Opaque:选项值长度不确定。
❑ Uint:选项值长度为非负整数,该值采用网络字节顺序即大端格式定义。
❑ String:UTF-8编码字符串格式。
4.选项定义
在这里插入图片描述

URI相关选项

在CoAP选项中共有4个选项与URI直接相关—Uri-Host、Uri-Port、Uri-Path和Uri-Query。这些参数都可以用于定位服务器资源,CoAP的资源定位规则也符合URL语法,例如:
在这里插入图片描述
❑ Uri-Host用于定义服务器名称,此CoAP URI示例中Uri-Host等于“wsnccoap.org”。
❑ Uri-Port用于定义服务器CoAP服务端口号,此CoAP URI示例中Uri-Port的值为5683。
❑ Uri-Path用于定义资源在服务器中的相对或绝对位置,在此CoAP URI示例中Uri-PATH等于“devices/1234CDEF”。此时“devices/1234CDEF”被分成了两个Uri-Path:一个为“devices”,另一个为“1234CDEF”。与其他CoAP选项实例一样,CoAP选项编号和选项长度均被完整定义,所以CoAP选项中并没有“/”这样的分隔符,也就是说“devices”和“1234CDEF”之间的“/”绝不会出现在CoAP选项内。
❑ Uri-Query用于定义资源的查询参数,在此CoAP URI示例中Uri-Query等同于“? limit=10&offset=20”,与Uri-Path中的情况相似,像“? ”和“&”这样的分隔符并不会出现在CoAP选项内。此处也有两个Uri-Query:一个为“limit=10”,另一个为“offset=20”。

Content-Format选项

Content-Format选项用于指示CoAP选项中的负载媒体类型,CoAP负载媒体类型采用整数编号的方式定义,该编号采用无符号整数表示,不同的媒体类型采用不同的编号,如二进制负载的媒体类型编号为42, JSON负载的媒体类型编号为50。Content-Format和HTTP中的Content-Type非常相似,在HTTP中若表示负载为JSON格式,需要在HTTP首选项中加入“Content-Type:Application/json\r\n”,在传输过程中该部分至少需要占用31字节,而在CoAP中却只占用3字节。CoAP媒体类型的相关内容请参考5.8节。

Accept选项

Accept选项用于表示CoAP客户端期望接收到的媒体类型格式,Accept选项的负载类型定义和Content-Format选项的负载类型定义完全相同。如果CoAP请求中没有包含Accept选项,那么CoAP服务器将返回默认媒体类型;若CoAP选项中包含Accept选项,CoAP服务器根据客户端期望的媒体类型返回响应负载,如果服务器不能返回指定格式的响应,那么可在CoAP响应中加入4.06(Not Acceptable)响应码。
通常情况下资源可以采用不同的媒体类型描述,例如一个温度传感器资源可以使用文本类型描述,也可以采用JSON格式描述,更可以使用2字节无符号整数这样的二进制方式描述。若采用二进制方式描述响应负载,需要提前定义大小端格式,在网络传输过程中一般采用大端格式。图5-14展示了Accept选项和Content-Format选项如何配合使用。
在这里插入图片描述

Etag选项

与HTTP相似,CoAP中也包含Etag选项,而且CoAP中的Etag选项与HTTP中Etag首部字段的使用方法非常相似。Etag可以理解为资源的实体标记,它用来表示资源的新鲜程度,当资源发生改变时Etag的值也需要更新。无论是在CoAP中还是在HTTP中,Etag并没有统一的计算规则,但是无论如何Etag的具体值都是由服务器分配。Etag可以有很多方法生成,如版本号、校验和、单向散列值和时间参数等。在实际应用中,单向散列值往往是最受欢迎的计算Etag的方法。Etag既可以出现在CoAP请求中,也可出现在CoAP响应中,而且在CoAP中,Etag的具体值还经常与If-Match选项配合使用。虽然CoAP中的Etag和HTTP中的Etag首部字段的用法相似,但是CoAP中Etag的具体值一般为二进制形式,而HTTP的Etag首部选项一般采用字符串形式。下面通过一个例子说明Etag在CoAP中的使用方法,具体过程如图5-15使用。
在这里插入图片描述

If-Match选项

如果请求中包含If-Match选项或If-None-Match选项,称这类CoAP请求为CoAP条件请求,If-Match选项一般用于更新服务器资源,其具体值一般采用Etag选项的具体值。
If-Match选项的使用方法也非常简单,如果服务器收到的If-Match选项值与被更新资源的Etag值相同,则认为该条件请求有效,响应码为“2.04 change”;如果服务器收到的If-Match选项值与被更新资源的Etag值不相同,则认为该条件请求无效,响应码为“4.12 Precondition Failed”。
下面通过一个具体的例子说明If-Match的使用方法,具体过程如图5-16所示。
在这里插入图片描述
1)CoAP客户端通过GET方法试图获取名为“validate”的资源内容。
2)CoAP服务器将返回名为“validate”的资源内容,CoAP响应中除了包含“2.05 Content”响应码之外,还包含Etag选项,此时Etag选项的具体值为二进制形式表示的“0x559d”。3)CoAP客户端将会缓存响应内容,并且将保留服务器返回的Etag。
4)经过一段时间之后CoAP客户端试图修改“validate”资源内容,此时CoAP客户端通过PUT方法更新资源,在CoAP请求负载中加入资源具体内容,在CoAP选项中加入If-Match选项,而If-Match选项的具体值为本地缓存的Etag具体值“0x559d”。
5)CoAP服务器收到该PUT请求之后发现请求首部中If-Match选项的具体值与本地资源的Etag值完全相同,所以认为该PUT请求合法。CoAP服务器将使用客户端请求负载中的内容更新资源,并重新计算该资源的Etag值。CoAP响应中响应码为“2.04 Changed”, CoAP响应中还包括更新之后的Etag具体值“0x6cfa”。
6)CoAP客户端收到该响应,缓存了响应中的Etag具体值“0x6cfa”以备下次使用。
7)在某一时刻其他CoAP客户端修改了“validate”资源,服务器中“validate”资源的Etag具体值被重新计算为“0x2f09”。
8)经过一段时间之后CoAP客户端试图再次更新“validate”资源,在CoAP请求中使用If-Match选项,该选项的具体值为之前缓存的Etag具体值“0x6cfa”。
9)CoAP服务器接收到该请求之后发现If-Match选项的具体值与资源Etag的具体值并不匹配,此时CoAP服务器认为该客户端并不能更新该资源,所以在响应中响应码变更为“4.12 Precondition Failed”。
10)CoAP客户端收到该响应后知晓本次PUT请求失败,如果需要正确更新“validate”资源,需要先发起GET请求获取最新资源内容,重新修改之后再发起PUT请求。
在CoAP中使用If-Match选项需要明确以下几点。
❑ If-Match选项一般和Etag具体值配合使用。
❑ 在CoAP请求中,If-Match选项往往配合PUT请求或POST请求使用,而Etag选项配合GET请求使用。
❑ If-Match选项一般用于更新已经存在的资源,所以往往需要通过GET请求获取资源的Etag具体值。

选项示例

  1. Uri-Path
    第一个示例CoAP选项中包含两组Uri-Path,假设此时CoAP URI为
    在这里插入图片描述
    通过前文的描述获知,在CoAP请求中一般可以省略Uri-Host和Uri-Port,所以本例中仅包括两个Uri-Path:“devices”和“1234CDEF”,图5-18展示了这两个Uri-Path在CoAP首部中的具体排列方式。
    在这里插入图片描述
    ❑ 第一组CoAP选项实例的选项偏移量为11,由于该部分是CoAP首部中的第一个选项,那么此时的选项偏移量就是选项编号,此时的选项编号为Uri-Path(11)。选项长度为7,选项值为字符串形式的“devices”。
    ❑ 第二组CoAP选项实例的选项偏移量为0,此时的选项编号应是上一个选项编号加上本次的选项偏移量,此时的选项编号为11+0=Uri-Path(11)。选项长度为8,选项值为字符串形式的“1234CDEF”。
  2. Uri-Path、Uri-Query和Accept
    下面构造一个更为复杂的示例,该示例不但包括两组Uri-Path,还包括一组Uri-Query和一组Accept。假设此时的CoAP URI为
    在这里插入图片描述
    CoAP客户端的期望负载为JSON格式。图5-19很好地展现了这种复杂组合的实现细节。
    在这里插入图片描述
    ❑ 两组Uri-Path的排列方法与上一个示例完全相同。
    ❑ Uri-Query选项一定排列在所有Uri-Path之后,该CoAP选项实例的选项偏移量为4,此时的选项编号为之前所有选项偏移量之和11+0+4=Uri-Query(15)。选项长度为8,选项值为字符串形式的“limit=10”。
    ❑ Accept选项一定排列在Uri-Path之后,该选项的选项偏移量为1,该CoAP的选项编号为之前所有选项偏移量之和11+0+4+1=Accept(16)。选项长度为2,选项值为2字节无符号整数50。
    3.选项长度扩展
    有时CoAP URI较长,单个Uri-Path的长度可能超过12字节,在下面的URI中,第二个Uri-Path“11223344AABBCCDD”长度为16字节,此时便需要使用选项长度扩展部分:
    在这里插入图片描述
    ❑ 第一个CoAP选项实例与之前的示例完全相同。
    ❑ 第二个CoAP选项实例的选项偏移量为0,选项长度指示为13。当选项长度为13时,选项值之前将增加选项长度扩展区域以表示超过长度13的部分,该扩展区域的值为3,那么此时的选项真实长度为13+3=16字节。选项长度指示被分成了两部分:一部分为固定值13,另一部分为扩展值3,选项的真实长度需将两部分相加。虽然选项长度被分成了两部分,但选项值依然为字符串形式的“11223344AABBCCDD”。如图5-20所示。
    在这里插入图片描述

CoAP媒体类型

(link-format类型、文本类型、二进制类型、JSON类型。)
CoAP支持多种媒体类型,但CoAP支持的媒体类型数量远小于HTTP支持的媒体类型,可以说CoAP的媒体类型是HTTP的媒体类型的微小子集,不过在物联网领域这些媒体类型已经完全足够。CoAP媒体类型采用编号的方式定义,该编号一般采用2字节无符号整数定义。CoAP媒体类型的定义如表5-4所示。
在这里插入图片描述
在物联网实际应用中,文本类型text/plain、二进制类型application/octet-stream、JSON类型application/json应用最为广泛,二进制JSON类型application/cbor也逐渐投入使用。application/link-format是一种专属于CoAP的媒体类型,该媒体类型一般在CoAP资源发现中使用。

link-format类型

link-format是CoAP中引入的新类型,该类型配合CoAP资源发现使用。CoAP服务器中一般包括一个事先约定的路由(/.well-known/core)。CoAP服务器内一般包括较多资源,这些资源由不同的URI定义。若CoAP客户端通过GET方法访问.well-known/core路由,CoAP服务器将相关资源的URI通过link-format格式返回至CoAP客户端,例如:
在这里插入图片描述

文本与二进制类型

文本类型和二进制类型是较为简单的媒体类型。CoAP中的默认媒体类型为文本类型。若负载为文本类型,无论在CoAP请求或CoAP响应中均不需要指定Content-Format。虽然文本类型使用非常简单,但是也存在一定的局限性。
在某些物联网应用中,TLV形式的二进制负载也广受欢迎,TLV中的T表示类型,L代表长度,而V表示具体内容,表面来看TLV形式的二进制类型非常节约空间,但是也带来一定的浪费或麻烦。
❑ T的定义可能与CoAP中的方法重复,例如T=0x01表示数据上传,T=0x02表示数据更新,那么此时T的定义便和CoAP中的POST方法和DELETE方法重复。

❑ V的设计也存在很多“误区”,如整数传输的大小端问题、浮点数长度问题和大小端问题、负数保存的问题等。

JSON类型

JSON(JavaScript Object Notation)[插图]是一种轻量级的数据交换格式。JSON格式易于人类阅读和编写,同时也易于机器解析和生成。JSON采用完全独立于计算机语言的文本格式,但也使用了类似于C语言家族的习惯。这些特性使JSON成为理想的数据交换手段。

第六章 CoAP资源扩展

CoAP资源描述

CoAP专门为设备间通信而设计,在设备通信过程中需要尽可能减少人为干预。为了实现设备在没有人干预的情况下正常工作,CoAP引入了资源发现机制。这种机制可以帮助CoAP客户端理解CoAP服务器有哪些URI已经被支持,并且CoAP客户端可以理解这些URI的具体含义。CoAP建议CoAP服务器总是支持一个/.well-known/core路由,该URI可以被任意CoAP客户端访问。

CoAP资源描述原理

当CoAP客户端请求预先协商好的/.well-known/core路由时,CoAP服务器将返回一系列的URI集合。这些URI集合遵循CoRE链接标准。CoRE链接标准参考《RFC6690 Constraint RESTful Environment(CoRE) Link Format》。参考CoRE标准返回的列表类型被描述为application/link-format,这是CoAP引入的一种全新的媒体类型。CoRE链接标准定义了非常多的选项,但是其中的很多部分在实际使用中并不常见。下面我们通过一个简单的例子来说明CoAP资源发现和application/link-format媒体类型,这个示例的工作流程如图6-1所示。
在这里插入图片描述
CoAP客户端访问/.well-known/core路由之后获得的响应内容如下:
在这里插入图片描述
CoAP服务器告诉CoAP客户端自身有两个资源(“sensors/temp”和“actuators/relay”)可以使用,通过以上例子可以获得以下有用的信息:
1)访问/.well-known/core路由只能通过GET方法,也就是说/.well-known/core路由不支持POST、PUT和DELETE方法。
2)CoAP服务器有两个可以被其他设备访问的资源—“sensor/temp”和“actuators/relay”,资源之间采用“, ”(逗号)分隔,每个资源都有各自不同的属性,这些属性通过“; ”(分号)分隔。
3)CoAP服务中存在一个传感器设备,该设备的URI为sensors/temp,在link-format格式中URI被“<>”包裹;该资源还包括一个title属性,通过title属性可获知该设备的具体用途,此时该资源代表一个温度传感器;sz属性代表该资源的分块大小,该属性在CoAP块传输中格外有用,此时的块大小为64,也就是说当使用GET方法访问该资源时,最大数据包的长度为64字节;最后该资源还具有一个ct属性,此时ct属性的值为50,该属性表示响应负载的媒体类型。在CoAP媒体类型一节可以获知,“50”代表application/json。
4)CoAP服务器中还存在一个可执行设备,该设备的URI为actuators/relay;与传感器资源相似,该资源也包括一个title属性,通过title属性可以获知该设备是一个控制台灯电源的继电器设备;若使用GET方法或POST方法访问该资源时,最大数据包的长度为32字节;与传感器资源相同,返回至CoAP客户端的负载媒体类型同样为application/json格式。

CoAP资源描述详解

1.资源URI
每一个资源必须包括URI,该URI使用“<>”包裹,例如上文示例中的<sensors/temp>和< actuators/relay>。
2.资源类型rt
资源类型rt是“Resource Type”的简写,采用“键名称=键值”这样的方式描述。资源类型rt可以理解为一个与应用直接相关的描述字符串,它是用于描述该资源的特定名词。例如rt=“block”, rt=“Type1 Type2”。资源类型和资源title并不能完全等同,title更方便于人类阅读理解,而资源类型rt更像一个标记。
3.负载类型ct
负载类型ct是“Content Format”的简写,采用“键名称=键值”这样的方式描述。该属性用于指示CoAP响应负载中的媒体格式。此处的键值采用十进制ASCII码表示,该值的范围必须在0~65 535之间,也就是说此处的键值为一个无符号16位整数。例如,ct=40表示负载媒体类型为application/link-format类型,ct=50表示负载媒体类型为application/json类型。
4.负载长度sz
负载长度sz是“Maxium Size Estimae”的简写,也采用“键名称=键值”这样的方式描述。当使用GET方式或其他方式操作资源时,负载长度sz用于指示最大数据包长度。CoAP协议支持块传输,该参数对于需要块传输的应用来说非常有用。例如sz=128表示数据包的最大长度为128。在一些由IEEE 802.15.4组成的网络中,单个数据包的最大长度为127字节,即使通过6LoWPAN这样的头压缩技术,CoAP层可使用的有效负载长度也仅为80字节左右,在这些物联网应用中sz可以等于36或64。
5.可被观察 obs
可被观察obs是“observable”的简写。CoAP支持观察者模式,该模式与互联网应用中的订阅/发布模式较为相似。若希望上文示例中的温度传感器资源也支持观察模式,可在该资源描述中加入obs属性,obs属性只有键名称而没有键值。例如:
在这里插入图片描述

CoAP观察者模式

在物联网应用监控温度或湿度传感器是一个非常常见的需求,为了更好地满足这类需求CoAP引入了观察者模式。CoAP客户端可以发送一个特殊观察请求到CoAP服务器。从该时间点开始计算,服务器将保存客户端的连接信息,一旦温度发生变化,服务器将会把新结果反馈至客户端。如果CoAP客户端不再希望获得温度检测结果,那么CoAP客户端将会发送一个注销请求或RST复位请求,此时CoAP服务器便会清除与客户端的连接信息。

观察者模式原理

在互联网应用中浏览器为了实时获取某个资源,可通过周期性发送Ajax请求的方式,这样的方式称为Ajax轮询。在物联网应用中,监控温度或湿度传感器的变化情况也可以采用轮询的方式,轮询过程如图6-2所示。
在这里插入图片描述
如图6-2所示的监控过程是非常低效的,这种低效主要体现在两个方面。第一,CoAP客户端为了获取服务器的传感器资源,每次都需要发送一次GET请求,而每次GET请求的内容完全相同,这显然是一种浪费;第二,CoAP客户端按照一定的时间间隔请求内容,而该时间间隔可能与服务器的传感器检测更新时间间隔并不匹配,这将会造成多次请求但是获得同一个结果的糟糕情况。为了避免这种情况,CoAP中引入了观察者模式,观察者模式的工作过程如图6-3所示。
在这里插入图片描述
CoAP观察者模式引入了4个基本概念—Observer、Subject、Registration和Notification。
❑ Observer:CoAP观察者是监控某种资源变化的CoAP客户端。
❑ Subject:CoAP主题是CoAP服务器中某个具体资源,该资源将会随着时间的变化频繁更新。CoAP主题正是CoAP观察者感兴趣的部分。
❑ Registration:CoAP注册是一个特殊的CoAP GET请求,该GET请求告知服务器此时CoAP客户端试图获取哪个资源的实时状态。服务器收到这个特殊的GET请求之后,将把CoAP客户端的信息保存到资源观察者列表中。
❑ Notification:CoAP指示,一旦服务的资源发生变化,CoAP服务器将会根据观察者列表中记录的信息,向CoAP客户端反馈资源的当前状态。
在CoAP观察者模式中,CoAP客户端作为观察者,而CoAP服务器作为被观察者,观察过程总是从一个特殊的观察GET请求开始。

CoAP观察选项

为了实现观察与通知功能,CoAP在选项中增加了Observe选项,Observer选项见表6-1。
在这里插入图片描述
当GET请求中出现Observe参数时,CoAP服务器并不会向CoAP客户端反馈请求URI中指定的资源,而是把CoAP客户端和被订阅的资源信息保存到观察者列表中。在GET请求中,Observe有两个可能的取值—0或1。
❑ Observe=0,表示注册操作,如果CoAP客户端信息并不在观察者列表中,那么服务器收到该请求之后会把客户端信息插入到观察者列表中。
❑ Observe=1,表示注销操作,CoAP服务器将会把观察者列表中的客户端信息删除,这也就意味着该订阅过程的结束。
Observe选项也出现在指示响应中,在指示响应中Observe选项类似于序号,该序号将会不断增加。

观察者模式示例

下面通过两个例子说明CoAP观察者模式的详细工作流程,通过示例加深对观察者模式的理解。第一个示例重点说明如何注册动作和注销动作,而第二个示例重点说明Max-Age选项如何与观察者模式配合使用。

  1. CoAP观察者模式示例1
    如图6-4所示是一个非常完整的CoAP观察者模式工作流程,CoAP客户端订阅CoAP服务器中的温度传感器资源,一段时间之后CoAP客户端取消了订阅,整个资源观察过程也就此结束。在该示例中CoAP客户端和服务器都正常工作未出现数据包丢失等异常情况。
    在这里插入图片描述
    1)CoAP客户端向CoAP服务器订阅温度传感器资源。CoAP客户端发送带有Observe选项的GET请求,此时Observe=0,表示订阅注册动作。
    2)CoAP发送订阅请求时,还可以在请求中加入Token,此时的Token长度为1字节,值为0x4a。通过第5章可以获知Token与应用直接相关,那么在CoAP观察过程中Token值应保持不变。
    3)CoAP服务器同意CoAP客户端的资源注册请求,向客户端返回温度传感器检测结果。响应首部中包含“2.05 Content”响应码。除此之外响应首部中还包括Observe选项,Observe选项好比订阅过程的“计数器”,在每个CoAP响应中其值逐渐累加。
    4)Max-Age选项也经常出现在CoAP观察者模式中。在CoAP中资源也有“新鲜”的概念,Max-Age好比资源的保质期,这个保质期使用秒为单位。此处Max-Age=15,表示温度传感器检测结果15秒之后便会过期。Max-Age的具体值可以与传感器检测结果的更新周期一致。
    5)CoAP客户端可向CoAP服务器发送订阅注销请求,此时Observe=1表示CoAP客户端不再关心温度传感器资源。
  2. CoAP观察者模式示例2
    在上一个示例中传输过程并没有数据包丢失这样的异常情况,一旦出现数据包丢失,CoAP观察者模式也可以借助Max-Age选项从这种错误中尽快恢复,如图6-5所示。
    在这里插入图片描述
    1)CoAP资源注册过程与示例1相同。
    2)CoAP服务器响应中包含Max-Age=15,表示温度传感器资源的“保质期”只有15秒。CoAP客户端收到温度传感器资源之后便开始计时,一般情况下在0到Max-Age之间便可收到下一次传感器指示。但是由于某种原因CoAP服务器返回的CoAP响应在传输过程中丢失。
    3)Max-Age超时时间已经到达,传感器数据结果已经不再“新鲜”。CoAP客户端认为数据传输过程发生异常,为了从这种异常状态下恢复,CoAP客户端重新发送了资源注册请求。Max-Age选项使CoAP客户端从错误状态中及时恢复。

第七章 CoAP软件实现

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

libcoap

与Linux平台大多数以lib开头的工具一样,libcoap是一款简单实用但功能完整的开发工具。libcoap不但提供了一个实用的coap-client命令行工具,也提供了一个用于测试目的的coap-server命令行工具。libcoap提供一个动态链接库libcoap.so文件,用户可以使用libcoap提供的API实现各种形式的coap-server或coap-client。
libcoap还是一款非常高效的命令行调试工具,libcoap提供的coap-client工具相当于HTTP领域中的cURL工具,该工具可以实现各种各样的CoAP请求。
CoAP学习笔记——Libcoap安装和使用

ARM交叉编译libcoap

  1. 下载libcoap文件:
    git clone https://github.com/authmillenon/libcoap.git
  2. 安装步骤:
    进入源文件目录:cd libcoap
    生成configure文件:autoconf
    生成Makefile文件:./configure --prefix=/tmp/coap,表示安装生成的文件存放在/tmp/coap
    使用编译器交叉编译:make CC=arm-linux-gnueabi-gcc
    安装:make install,生成的头文件、库文件会存放在/tmp/coap目录下
  3. 测试:
    在libcoap/examples目录下,运行coap-server、coap-client文件测试。
  4. 编译:
    例如:arm-linux-gcc -DWITH_POSIX -o test test.c -lcoap
    注意事项:要增加宏定义、动态链接库,否则编译失败。
libcoap

与Linux平台大多数以lib开头的工具一样,libcoap是一款简单实用但功能完整的开发工具。libcoap不但提供了一个实用的coap-client命令行工具,也提供了一个用于测试目的的coap-server命令行工具。
libcoap提供一个动态链接库libcoap.so文件,用户可以使用libcoap提供的API实现各种形式的coap-server或coap-client。libcoap还是一款非常高效的命令行调试工具,libcoap提供的coap-client工具相当于HTTP领域中的cURL工具,该工具可以实现各种各样的CoAP请求。更多信息可前往libcoap的官方网址和github代码仓库获得。
❑ libcoap官方网址:https://libcoap.net/
❑ libcoap代码仓库:https://github.com/obgm/libcoap
libcoap是一个多功能的工具,虽然使用其他的脚本语言例如Python和Node.js也可以编写出功能相同的CoAP请求,但libcoap工具的使用更加方便灵活。

libcoap安装

CoAP学习笔记——Libcoap安装和使用
libcoap安装过程也相对简单,但具体过程中也会需要处理一些问题,需要根据错误提示耐心修正。一般来说libcoap的源代码安装过程可分为以下几个步骤:
1)autoconf 生成configure可执行文件。
2)./configure 配置软件环境。
3)make 编译源代码。
4)sudo make install 把相关头文件安装至/usr/local/include/libcoap,把相关库文件安装至/usr/local/lib。
5)sudo ldconfig 重新搜索当前系统中的动态链接库。在控制台中依次输入以下指令便可完成libcoap的安装。

libcoap使用详解

libcoap包含两个主要的命令行工具coap-server和coap-client。coap-server工具可快速搭建一个测试用途的CoAP服务器。coap-client类似于HTTP中常用的cURL工具,通过coap-client可以设置CoAP首部中的各种选项,执行CoAP GET或PUT请求等。下面分coap-server工具和coap-client工具两部分说明如何在物联网系统开发实战中使用libcoap工具。

  1. coap-server
    (1)coap-server用法说明
    在这里插入图片描述
    (2)coap-server参数说明
    在这里插入图片描述
    (3)coap-server用法示例
    下面通过几个示例说明coap-server工具的使用方法。
    在这里插入图片描述
    若已知Linux主机的IPv4地址,推荐使用“coap-server -A 192.168.0.103 -p5683”这样的写法,通过-A参数指定主机地址,通过-p参数指定服务端口号。
  2. coap-client
    (1)coap-client用法说明
    相比于coap-server工具,coap-client工具的参数较多,coap-client的具体使用方法如下:
    在这里插入图片描述
    (2)coap-client参数说明
    在这里插入图片描述
    (3)coap-client用法示例

在这里插入图片描述
虽然coap-client的参数较多,但使用过程并不复杂。首先,需要指定CoAP请求方法,通过-m参数便可设定CoAP请求方法,CoAP请求方法包括GET、PUT、POST和DELETE四种;接着,若需要指定CoAP请求负载,可使用-e参数设置请求负载,请求负载的类型为文本类型;最后指定CoAP资源URI, coap-client工具支持IPv6地址,如果使用IPv6形式的主机地址,那么IPv6地址需要包裹在中括号中,如coap://[fd00::212:4b00:42a:fd00]/time;如果URI还包括非默认端口号,请注意一定不能把端口号也放在中括号中,如下的CoAP URI才是正确的:coap://[fd00::212:4b00:42a:fd00]:5684/time。

libcoap入门示例

在入门示例中包含两个Linux设备—Linux主机和树莓派,Linux主机作为CoAP客户端,而树莓派3代作为CoAP服务器,在CoAP服务器中提供一个访问系统时间的路由,使用树莓派的系统时间是因为它是一个不断变化的参数,也就是说每次CoAP请求都可以获得不一样的CoAP响应。通过这种变化可帮助用户更好地了解CoAP本身,了解在实际的应用中哪些细节固定不变,哪些细节将不停地发生变化。libcoap入门示例中将会使用两组不同的coap-client指令:
在这里插入图片描述
1)获得CoAP服务器公开信息coap-client -m get coap://192.168.0.6/.well-known/core。
2)获得CoAP服务器系统时间coap-client -m get coap:// 192.168.0.6 /time。
3)启动观察者模式,观察CoAP服务器系统时间coap-client -m get -s 100coap:// 192. 168.0.6 /time。
1.服务器实现
首先在树莓派中运行coap-server工具。coap-server包括一个time资源,通过GET方法或Observer方法访问该资源可获取树莓派的系统时间。
在这里插入图片描述
运行coap-server时,-A参数用于指定Linux主机IP地址也就是树莓派的IP地址,本例中树莓派的IPv4地址为192.168.0.6。若不清楚树莓派的IP地址,可在树莓派中使用ifconfig指令查看。
2.客户端实现
(1)访问/.well-known/core
在Linux PC主机中重新打开一个控制台,在控制台中输入以下命令。
在这里插入图片描述
❑ -m get 通过-m参数指定访问该资源的CoAP方法,此处为GET方法。
❑ 访问URI为coap://192.168.0.6/.well-known/core。
Linux PC主机将获取以下内容:
在这里插入图片描述
❑ v:1 t:CON c:GET i:2836 给出CoAP请求的首部信息,v:1代表CoAP版本编号,无论如何CoAP的版本编号均为1; t:CON表示CoAP请求为CON类型请求;c:GET表示请求方法为GET请求;i:2836表示报文序号为2836。
❑ 访问.well-known/core可获取服务器中所有资源的提示信息,该CoAP服务器中包含三个资源,分别为</>、和。
❑ </>; title=“General Info”; ct=0:若访问“/”可获取该CoAP服务器的概要信息,概要信息的媒体类型为文本类型。
❑ ; if=“clock”; rt=“Ticks”; title=“Internal Clock”; ct=0; obs:相比于“/”资源,time资源要复杂一些,time资源包含“obs”属性表示该资源可以被观察。
(2)通过GET方法获取系统时间
在Linux PC主机控制台中运行以下指令。
在Linux PC主机控制台中运行以下指令。
在这里插入图片描述
❑ -m get 通过-m参数指定访问该资源的CoAP方法,此处仍为GET方法。
❑ 访问URI为coap://192.168.0.6/time。
运行于树莓派3代中的CoAP服务器将会返回以下内容。
在这里插入图片描述
❑ v:1 t:CON c:GET i:4b5d给出了CoAP请求的首部信息,v:1代表CoAP版本编号;t:CON表示CoAP请求为CON类型请求;c:GET表示请求方法为GET请求;i: 4b5d表示报文编号为4b5d。
❑ Aug 01 06:07:02 表示此时树莓派3的系统时间。
由于time服务中并没有考虑时区和时间表示方式等因素,所以CoAP响应中树莓派的系统时间并不一定与当前时间完全吻合,但这并没有太大的关系,此处仅仅是为了说明libcoap可以在Linux主机和树莓派中正常工作。
(3)通过Observer观察系统时间
通过Observer观察CoAP服务器中的time资源,在Linux PC控制台输入以下指令:
在这里插入图片描述
❑ -m get通过-m参数指定访问该资源的CoAP方法,CoAP中Observer也可以理解为一种特殊的GET方法,但在libcoap中执行Observer一定要在coap-client指令中增加-s参数。
❑ -s 100表示持续观察time资源100秒,在这100秒的时间内CoAP服务器将向CoAP客户端源源不断地返回树莓派3代内部的系统时间。
❑ 访问URI为:coap://192.168.0.6/time。
Linux PC控制台将获得以下内容:
在这里插入图片描述
通过Linux PC控制台的输出可以发现,运行于树莓派中的CoAP服务器每隔2秒钟向CoAP客户端返回指示内容,在CoAP观察者模式中指示报文的时间间隔由CoAP服务器决定,一般在指示报文中包含Max-Age选项,该选项用于告知CoAP客户端被观察资源的更新周期。

  • 3
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值