CoAP 受限应用协议
CoAP 受限应用协议
一、概述
CoAP(Constrained Application Protocol,受限应用协议)是专为资源受限的物联网节点设计的应用层协议,基于 UDP,采用 RESTful 风格(类似 HTTP),报文头最小仅 4 字节,非常适合 NB-IoT、ZigBee、低功耗设备。
一句话核心:CoAP 把 HTTP 的请求/响应模型"搬"到 UDP 上并极度精简——用 4 字节头 + 方法(GET/POST/PUT/DELETE)+ 资源 URI,通过 CON/NON 实现可靠或不可靠传输。
二、为什么需要 CoAP
| HTTP 的问题 | CoAP 的解决 |
|---|---|
| 基于 TCP,开销大 | 基于 UDP,无连接成本 |
| 报文头大(几十字节) | 报文头最小 4 字节 |
| 面向复杂 Web | 面向受限设备(内存<10KB) |
| 不支持组播 | 支持多播 |
| 被动(客户端主动) | 支持观察(服务器推送) |
关键理解:CoAP 是"物联网版的 HTTP"——在资源受限环境下用最少资源实现 RESTful 资源操作。
三、CoAP vs HTTP 对照
| 概念 | HTTP | CoAP |
|---|---|---|
| 传输 | TCP | UDP |
| 报文头 | 大 | 最小 4 字节 |
| 方法 | GET/POST/PUT/DELETE | 相同(编号 1~4) |
| 响应码 | 200/404/... | 2.05/4.04/...(十进制编码) |
| 可靠性 | TCP 保证 | CON/NON 控制 |
| 推送 | 轮询 | 观察(Observe) |
| 发现 | 无 | /.well-known/core |
| 安全 | TLS | DTLS |
四、CoAP 报文格式
┌────┬────┬────┬────┬───────────────────┐
│Ver │Type│TKL │ Code │ Message ID │ ← 固定头4字节
│ 2位 │ 2位│ 4位 │ 8位 │ 16位 │
├────┴────┴────┴────┴───────────────────┤
│ Token(0-8字节) │ ← 可选
├───────────────────────────────────────┤
│ 选项(Option)... │ ← URI/内容格式
├───────────────────────────────────────┤
│ Payload(前有0xFF分隔) │ ← 数据
└───────────────────────────────────────┘| 字段 | 长度 | 作用 |
|---|---|---|
| Ver | 2 位 | 版本(当前 1) |
| Type | 2 位 | 消息类型(CON/NON/ACK/RST) |
| TKL | 4 位 | Token 长度 |
| Code | 8 位 | 方法或响应码 |
| Message ID | 16 位 | 消息标识(去重/匹配) |
| Token | 0~8B | 事务标识(区分请求/响应) |
| 选项 | 可变 | URI、内容格式、观察等 |
五、四种消息类型
CoAP 用 Type 字段区分四种消息:
| 类型 | 值 | 说明 |
|---|---|---|
| CON(确认) | 0 | 需确认(可靠),收方必须回 ACK |
| NON(非确认) | 1 | 不需确认(不可靠,类似 UDP) |
| ACK(确认响应) | 2 | 对 CON 的确认 |
| RST(复位) | 3 | 拒收/出错 |
关键理解:CoAP 的"可靠"靠 CON + ACK 重传(在 UDP 上模拟 TCP 的确认):
- CON 发后等 ACK,超时重传(指数退避);
- NON 发了不管(不可靠,适合传感高频数据)。
六、CON 请求/响应交互
例:CON GET(可靠)
客户端 服务器
CON GET /sensor/temp (mid=0x1234)
────────────────────────────────►
◄────────────────── 2.05 (mid=0x1234)
(同 mid 的响应即该请求的确认)例:CON 请求 + 独立 ACK + 响应
客户端 服务器
CON GET /sensor/temp ───────────►
◄── ACK (空确认,先应答) (处理中)
◄──────────── 2.05 Content 2.05 (处理完再发响应)非可靠(NON):
客户端 服务器
NON GET /sensor/temp ───────────►
◄──────────── 2.05 (无确认)关键理解:CON 的确认可以是"直接响应"(同 mid 即 ACK),也可以是"先空 ACK 再单独响应"。NON 则无确认,适合容忍丢失的场景。
七、方法(Code)
CoAP 请求的方法编码在 Code 字段:
| 方法 | Code | 含义 |
|---|---|---|
| GET | 0.01 | 获取资源 |
| POST | 0.02 | 创建资源 |
| PUT | 0.03 | 更新资源 |
| DELETE | 0.04 | 删除资源 |
八、响应码
CoAP 响应码是两位十进制编码(class.detail):
| 响应码 | 值 | 含义 |
|---|---|---|
2.01 | 65 | 已创建 |
2.03 | 67 | 有效(GET 成功) |
2.04 | 68 | 已更改 |
2.05 | 69 | 内容(响应带数据) |
4.00 | 128 | 坏请求 |
4.01 | 129 | 未授权 |
4.04 | 132 | 未找到 |
4.05 | 133 | 方法不允许 |
4.13 | 141 | 请求实体过大 |
5.00 | 160 | 服务器内部错误 |
数值演算(Code 编码):CoAP 的 class.detail 两位十进制编码存在同一个 1 字节 Code 字段中,位结构如下:
Code 字段 = 1 字节 (8 位)
┌────┬────┬────┬────┬────┬────┬────┬────┐
│ class(高3位) │ detail(低5位) │
└────┴────┴────┴────┴────┴────┴────┴────┘
class 最大 7 (2^3-1) detail 最大 31 (2^5-1)
编码公式: Code = class << 5 | detail
= class × 32 + detail例 1:2.05 编码为 0x45(class=2,detail=5):
class=2 → 二进制 010
detail=5 → 二进制 00101
拼接: 010 | 00101 = 01000101b
十六进制: 01000101b = 0x45
十进制: 0x45 = 69
用公式验证:
Code = class << 5 | detail = (2 << 5) | 5
= 2×32 + 5 = 64 + 5 = 69 = 0x45 ✓例 2:4.04 编码为 0x84(class=4,detail=4):
class=4 → 二进制 100
detail=4 → 二进制 00100
拼接: 100 | 00100 = 10000100b = 0x84
验证: 4×32 + 4 = 128 + 4 = 132 = 0x84 ✓例 3:请求方法 0.01(GET)编码为 0x01(class=0,detail=1):
class=0 → 二进制 000
detail=1 → 二进制 00001
拼接: 000 | 00001 = 00000001b = 0x01
验证: 0×32 + 1 = 1 = 0x01 ✓演算要点:因为 class 占高 3 位、占 32 的倍数,detail 占低 5 位、直接是余数,所以 Code = class×32 + detail。识别时,把 1 字节十六进制值拆开:高 3 位读出 class、低 5 位读出 detail。如
0x45→0x45 = 69,69 ÷ 32 = 2 余 5,即2.05。
九、URI 与选项
CoAP 用选项(Option)传输 URI,常用选项:
| 选项 | 编号 | 作用 |
|---|---|---|
| Uri-Host | 3 | 主机 |
| Uri-Path | 11 | 路径(可多个) |
| Uri-Query | 15 | 查询参数 |
| Content-Format | 12 | 内容格式 |
| Observe | 6 | 观察(订阅资源变化) |
| Block2 | 23 | 块传输(响应分块) |
例:URI 与选项(请求 URI:coap://example.com/sensor/temp?unit=c):
请求 URI: coap://example.com/sensor/temp?unit=c
→ Uri-Host: "example.com" // 主机名
→ Uri-Path: "sensor" // 每个路径段一个 Uri-Path
→ Uri-Path: "temp"
→ Uri-Query: "unit=c" // 查询参数易错点:一个 URI 可能映射出多个 Uri-Path / Uri-Query 选项,每个
/或&分隔段各一个,不能拼成一个。另外Uri-Host在"主机名与 CoAP 目的地址一致"时可省略(规范允许),但若要与请求 URI 一一对应则应显式列出;非默认端口还需加Uri-Port选项。
补充:Uri-Path 多段拆分示例:
请求 URI: coap://example.com/a/b/c
→ Uri-Path: "a"
→ Uri-Path: "b"
→ Uri-Path: "c"
对比(错误):Uri-Path: "a/b/c" ← 错误,不能把整条路径拼成一个选项资源发现:CoAP 内置发现机制——客户端 GET /.well-known/core,服务器返回资源列表(CoRE Link Format,RFC 6690)。这样客户端无需预先知道资源地址,先"问有哪些资源"再访问:
客户端 服务器
GET /.well-known/core ───────────►
◄────────── 2.05 Content
</sensor/temp>;rt="temperature";if="sensor";ct=0,
</sensor/light>;rt="light",
</led>;rt="switch";obs,
... (每个资源一行, 逗号分隔)返回格式(link-format)逐段解释:
</sensor/temp> ;rt="temperature" ;if="sensor" ;ct=0
└─资源路径─────┘ └─资源类型────────┘ └─接口──────┘ └─内容格式──┘| 属性 | 含义 |
|---|---|
<路径> | 资源地址,如 /sensor/temp |
rt | resource type,资源类型(如 temperature) |
if | interface,接口描述 |
ct | content-format,内容格式(0 = text/plain) |
obs | 该资源可被 Observe 观察 |
带条件的资源发现(筛选):可通过查询参数 ?rt=... 让服务器只返回匹配的资源:
请求: GET /.well-known/core?rt=temperature
响应: </sensor/temp>;rt="temperature";if="sensor";ct=0
(只返回 rt 为 temperature 的资源, 缩小列表)易错点:返回的是资源元数据列表,不是资源数据本身。要读温度得再向
<sensor/temp>发一次 GET;rt是资源类型字符串(自定义标识),ct才是内容格式编号(0=text/plain、50=application/json 等),两者别混淆。
十、观察(Observe,服务器推送)
CoAP 支持观察资源,实现"服务器主动推送变化"(类似订阅):
客户端 服务器
GET /sensor/temp (Observe:0) ──► 登记观察
◄──── 2.05 (observe=1) 值1
◄──── 2.05 (observe=2) 值2 (资源变化时推送)
◄──── 2.05 (observe=3) 值3
(取消: 发 GET with Observe:1 或 RST)关键理解:Observe 让服务器在资源变化时主动推送,设备无需频繁轮询——省电且实时(类似 MQTT 订阅,但基于请求/响应扩展)。
十一、块传输(Blockwise)
CoAP 报文小(UDP 限制),大资源需分块传输(Block2 选项):
客户端 服务器
GET /big-file (Block2: 0/0) ──►
◄── 2.05 (Block2: 0/1) [块0]
GET /big-file (Block2: 1/0) ──►
◄── 2.05 (Block2: 1/1) [块1]
...应用:固件下载、大配置等大块数据传输。
十二、CoAP vs MQTT 对比
| 维度 | CoAP | MQTT |
|---|---|---|
| 传输 | UDP | TCP |
| 模型 | 请求/响应(REST) | 发布/订阅 |
| 报文头 | 最小 4 字节 | 最小 2 字节 |
| 可靠性 | CON/NON | QoS 0/1/2 |
| 推送 | Observe | 订阅 |
| 开销/功耗 | 更低(UDP) | 较高(TCP) |
| 场景 | NB-IoT 受限设备 | 通用物联网双向 |
关键理解:CoAP 更轻(UDP + 4 字节头),适合资源受限 + 低功耗 + 类 REST;MQTT 更强实时双向,适合需要服务端推送 + 复杂双向的场景。两者都可用于物联网。
十三、常见坑与排查
| 问题 | 可能原因 | 排查 |
|---|---|---|
| 收不到响应 | CON 超时未确认 | 检查重传,确认服务器地址 |
| 4.04 未找到 | URI-Path 选项错 | 核对路径选项 |
| 4.05 方法不允许 | 用错方法 | 确认 GET/POST/PUT/DELETE |
| 大资源收不全 | 未用块传输 | 用 Block2 分块 |
| 乱码 | Content-Format 未指定 | 设内容格式(如 json) |
| 观察失效 | 服务器不支持 Observe | 用轮询替代 |