CoAP简介

265 阅读6分钟

COAP (Constrained Application Protocol)是一种在物联网世界的类 HTTP 的协议,使用在资源受限的物联网设备上,它的详细规范定义在 RFC 7252。

由于物联网设备大多都是资源限制型的,比如 CPU、RAM、Flash、网络宽带等。对于这类设备来说,想要直接使用现有网络的TCP和HTTP来实现设备实现信息交换是不现实的。为了让这部分设备能够顺利接入网络,CoAP 协议应运而生。

COAP运行于UDP之上,与MQTT一样都是比较成熟的物联网协议。

image.png

特性

  • 在受限条件下满足M2M需求的Web协议环境
  • 拥有可选的可靠性特性的UDP,支持单播与组播请求
  • 异步信息交互
  • 低报头开销与低解析复杂性
  • 支持URI与Content-type
  • 简单的代理与缓存功能

这篇文章只简单介绍CoAP的交互模型与数据结构

交互模型

+----------------------+
|      Application     |
+----------------------+
+----------------------+  \
|  Requests/Responses  |  |
|----------------------|  | CoAP
|       Messages       |  |
+----------------------+  /
+----------------------+
|          UDP         |
+----------------------+

在UDP之上,一共有两种交互模型,一个是消息模型Messages,一个是请求响应模型Request/Response。首先我们需要知道消息有4种类型,分别是:

  • Confirmable(CON),可确认消息
  • Non-confirmable(NON),无需确认消息
  • Acknowledgement(ACK),确认消息
  • Reset(RST),重置消息

消息模型

消息的可靠性是通过可确认消息(CON)来提供的。可确认消息(CON)使用默认的超时机制进行传输与使用指数级退避机制进行重传,直到接收者回复携带了Message ID(在这例子中,就是0x7d34)的确认消息(ACK)。见下图,当接收者无法处理可确认消息(CON)时,回复重置消息(RST)。

Client              Server
   |                  |
   |   CON [0x7d34]   |
   +----------------->|
   |                  |
   |   ACK [0x7d34]   |
   |<-----------------+
   |                  |

如果消息不需要可靠性,可以发送无需确认消息(NON)。这情景下,没有确认消息(ACK),但还是拥有Message ID(在这例子中,就是0x01a0)来检测消息是否重复。见下图,当接收者无法处理无确认消息(NON)时,它应该回复重置消息(RST)。

Client              Server
   |                  |
   |   NON [0x01a0]   |
   +----------------->|
   |                  |

请求与响应模型

请求携带于可确认消息(CON)或无需确认消息(NON),而且如果响应的足够的快,该请求的响应可以携带于确认消息(ACK)中。这种形式,称为piggybacked response(这里暂译为同步响应)(这里不需要单独的对同步响应进行ACK,因为当客户端没接收到同步响应时会重传请求)

        Client              Server       Client              Server
           |                  |             |                  |
           |   CON [0xbc90]   |             |   CON [0xbc91]   |
           | GET /temperature |             | GET /temperature |
           |   (Token 0x71)   |             |   (Token 0x72)   |
           +----------------->|             +----------------->|
           |                  |             |                  |
           |   ACK [0xbc90]   |             |   ACK [0xbc91]   |
           |   2.05 Content   |             |  4.04 Not Found  |
           |   (Token 0x71)   |             |   (Token 0x72)   |
           |     "22.5 C"     |             |   "Not found"    |
           |<-----------------+             |<-----------------+
           |                  |             |                  |

而如果服务器不能马上对可确认消息(CON)中的请求进行响应,它可以简单回复ACK消息,以便客户端停止重传请求。当响应就绪时,服务器可以发送一个新的可确认消息(CON)(这时反过来需要客户端ACK确认)

                        Client              Server
                           |                  |
                           |   CON [0x7a10]   |
                           | GET /temperature |
                           |   (Token 0x73)   |
                           +----------------->|
                           |                  |
                           |   ACK [0x7a10]   |
                           |<-----------------+
                           |                  |
                           ... Time Passes  ...
                           |                  |
                           |   CON [0x23bb]   |
                           |   2.05 Content   |
                           |   (Token 0x73)   |
                           |     "22.5 C"     |
                           |<-----------------+
                           |                  |
                           |   ACK [0x23bb]   |
                           +----------------->|
                           |                  |

如果请求是无需确认消息(NON),则可以使用无需确认消息(NON)进行响应,尽管服务器可能使用可确认消息(CON)响应。

                        Client              Server
                           |                  |
                           |   NON [0x7a11]   |
                           | GET /temperature |
                           |   (Token 0x74)   |
                           +----------------->|
                           |                  |
                           |   NON [0x23bc]   |
                           |   2.05 Content   |
                           |   (Token 0x74)   |
                           |     "22.5 C"     |
                           |<-----------------+
                           |                  |

数据结构

CoAP由四大部分组成:分别是Header、Token、Options与Payload。

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |Ver| T |  TKL  |      Code     |          Message ID           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |   Token (if any, TKL bytes) ...
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |   Options (if any) ...
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |1 1 1 1 1 1 1 1|    Payload (if any) ...
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Header

固定4个字节,分别由Ver、T、TKL、Code与MessageID组成。

Ver

2位无符号整数。表示CoAP的版本号。该规范的实现必须设置该字段为1(即0b01)。其他值保留给未来的版本使用。版本号未知的消息必须被静默地忽略。

T

2位无符号整数。表示该消息是否为类型:

  • Confirmable(0)
  • Non-confirmable(1)
  • Acknowledgement(2)
  • Reset(3)

Token

4位无符号整数。表示可变长Token的长度,能表示0-8字节(4位,能表示0~15)。长度9-15为保留,不能发送,收到则作为格式错误处理。

Code

8位无符号整数。前3位表示class,后5位表示detail。记录为"c.dd ",其中"c"指从0到7的数字即前3位,“dd”是两位数字,00 ~ 31即后5位。

class的值如下:

  • a request(0)
  • a success response(2)
  • a client error response(4)
  • a server error response(5)
  • 其它值保留

具体的范围如下:

  • 0.00 表示是一个空消息

  • 0.01-0.31 表示一个请求

    +------+--------+-----------+
    | Code | Name   | Reference |
    +------+--------+-----------+
    | 0.01 | GET    | [RFC7252] |
    | 0.02 | POST   | [RFC7252] |
    | 0.03 | PUT    | [RFC7252] |
    | 0.04 | DELETE | [RFC7252] |
    +------+--------+-----------+
    
  • 1.00-1.31 保留

  • 2.00-5.31 表示一个响应

    +------+------------------------------+-----------+
    | Code | Description                  | Reference |
    +------+------------------------------+-----------+
    | 2.01 | Created                      | [RFC7252] |
    | 2.02 | Deleted                      | [RFC7252] |
    | 2.03 | Valid                        | [RFC7252] |
    | 2.04 | Changed                      | [RFC7252] |
    | 2.05 | Content                      | [RFC7252] |
    | 4.00 | Bad Request                  | [RFC7252] |
    | 4.01 | Unauthorized                 | [RFC7252] |
    | 4.02 | Bad Option                   | [RFC7252] |
    | 4.03 | Forbidden                    | [RFC7252] |
    | 4.04 | Not Found                    | [RFC7252] |
    | 4.05 | Method Not Allowed           | [RFC7252] |
    | 4.06 | Not Acceptable               | [RFC7252] |
    | 4.12 | Precondition Failed          | [RFC7252] |
    | 4.13 | Request Entity Too Large     | [RFC7252] |
    | 4.15 | Unsupported Content-Format   | [RFC7252] |
    | 5.00 | Internal Server Error        | [RFC7252] |
    | 5.01 | Not Implemented              | [RFC7252] |
    | 5.02 | Bad Gateway                  | [RFC7252] |
    | 5.03 | Service Unavailable          | [RFC7252] |
    | 5.04 | Gateway Timeout              | [RFC7252] |
    | 5.05 | Proxying Not Supported       | [RFC7252] |
    +------+------------------------------+-----------+
    
  • 6.00-7.31 保留

Message ID

16位无符号整数。用于检测消息重复和对Acknowledgement/Rest信息和Confirmable/Non-confirmable进行匹配。能表示65535个数。

Token

用于关联请求与响应。由Header的TKL决定长度,长度为0-8字节。

Options

由0个或多个Option组成。

     0   1   2   3   4   5   6   7
   +---------------+---------------+
   |               |               |
   |  Option Delta | Option Length |   1 byte
   |               |               |
   +---------------+---------------+
   \                               \
   /         Option Delta          /   0-2 bytes
   \          (extended)           \
   +-------------------------------+
   \                               \
   /         Option Length         /   0-2 bytes
   \          (extended)           \
   +-------------------------------+
   \                               \
   /                               /
   \                               \
   /         Option Value          /   0 or more bytes
   \                               \
   /                               /
   \                               \
   +-------------------------------+

Option Delta:4位无符号整型。0-12用于表示Option Delta。3个特殊值:

  • 13: 在初始字节后的8位无符号整型,减去13表示Option Delta。
  • 14: 在初始字节后的16位无符号整型,减去269表示Option Delta。
  • 15:保留给Payload标记

目前Option Delta对应的字段:

                 +--------+------------------+-----------+
                 | Number | Name             | Reference |
                 +--------+------------------+-----------+
                 |      0 | (Reserved)       | [RFC7252] |
                 |      1 | If-Match         | [RFC7252] |
                 |      3 | Uri-Host         | [RFC7252] |
                 |      4 | ETag             | [RFC7252] |
                 |      5 | If-None-Match    | [RFC7252] |
                 |      7 | Uri-Port         | [RFC7252] |
                 |      8 | Location-Path    | [RFC7252] |
                 |     11 | Uri-Path         | [RFC7252] |
                 |     12 | Content-Format   | [RFC7252] |
                 |     14 | Max-Age          | [RFC7252] |
                 |     15 | Uri-Query        | [RFC7252] |
                 |     17 | Accept           | [RFC7252] |
                 |     20 | Location-Query   | [RFC7252] |
                 |     35 | Proxy-Uri        | [RFC7252] |
                 |     39 | Proxy-Scheme     | [RFC7252] |
                 |     60 | Size1            | [RFC7252] |
                 |    128 | (Reserved)       | [RFC7252] |
                 |    132 | (Reserved)       | [RFC7252] |
                 |    136 | (Reserved)       | [RFC7252] |
                 |    140 | (Reserved)       | [RFC7252] |
                 +--------+------------------+-----------+

Option Length:4位无符号整型。0-12用于表示Option Length。3个特殊值:

  • 13: 在初始字节后的8位无符号整型,减去13表示Option Length。
  • 14: 在初始字节后的16位无符号整型,减去269表示Option Length。
  • 15:保留给未来扩展使用

Option Value:文档中定义的选项使用了以下选项值的格式:

  • empty:长度为0的字节序列
  • opaque:不透明的字节序列
  • uint:无符号整型
  • string:UTF-8编码的字节串

Payload

如果存在且长度不为0,则前面固定1个标记(0xFF),用来表示Options块结束和Payload块开始。