CoAP轻量级协议深度解析
CoAP轻量级协议深度解析
目录
一句话总结
CoAP(Constrained Application Protocol)是专为物联网受限设备设计的应用层协议,基于UDP实现类似HTTP的RESTful风格,具有低开销、低功耗、支持组播的特点,适用于资源受限的嵌入式设备和低功耗网络。
核心架构图
CoAP协议栈架构
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
客户端 服务器
┌─────────────────────┐ ┌─────────────────────┐
│ 应用层 (App) │ │ 应用层 (App) │
├─────────────────────┤ ├─────────────────────┤
│ CoAP 请求/响应 │ │ CoAP 请求/响应 │
│ Request/Response │ │ Request/Response │
├─────────────────────┤ ├─────────────────────┤
│ CoAP 消息层 │ │ CoAP 消息层 │
│ - CON (可靠) │ │ - ACK (确认) │
│ - NON (不可靠) │ │ - RST (重置) │
├─────────────────────┤ ├─────────────────────┤
│ UDP (用户数据报) │ │ UDP │
├─────────────────────┤ ├─────────────────────┤
│ IP (网络层) │ │ IP │
└─────────────────────┘ └─────────────────────┘
│ │
└──────── UDP 5683端口 ────────┘
CoAP 请求方法(类似HTTP):
┌─────────┬──────────────────────────────┐
│ GET │ 获取资源 │
│ POST │ 创建资源 │
│ PUT │ 更新资源 │
│ DELETE │ 删除资源 │
└─────────┴──────────────────────────────┘
CoAP 响应码(类似HTTP状态码):
┌─────────┬──────────────────────────────┐
│ 2.01 │ Created(已创建) │
│ 2.02 │ Deleted(已删除) │
│ 2.03 │ Valid(有效) │
│ 2.04 │ Changed(已修改) │
│ 2.05 │ Content(内容) │
│ 4.00 │ Bad Request(错误请求) │
│ 4.04 │ Not Found(未找到) │
│ 5.00 │ Internal Server Error(服务器错误)│
└─────────┴──────────────────────────────┘
关键特性:
- 报文大小:4字节头部 + 负载(典型<100字节)
- 传输方式:UDP(无连接,低开销)
- 可靠性:可选的可靠传输(CON消息 + ACK确认)
- 支持特性:资源发现、观察者模式、块传输、组播
- 资源消耗:<10KB RAM,<100KB Flash
一、CoAP基础概念
1.1 什么是CoAP?
CoAP(Constrained Application Protocol,受限应用协议)是IETF RFC 7252定义的应用层协议,专为物联网受限设备和网络设计。它提供了类似HTTP的RESTful架构,但报文开销比HTTP小得多,适合低功耗、低带宽的IoT场景。
核心特性:
- 轻量级:头部仅4字节(HTTP头部通常>100字节),总报文<100字节
- 基于UDP:无连接开销,适合低功耗设备
- RESTful风格:使用GET/POST/PUT/DELETE方法,易于理解和使用
- 可靠传输:可选的确认机制(CON消息),支持重传
- 资源发现:自动发现设备提供的资源(类似UPnP)
- 观察者模式:订阅资源变化通知(类似MQTT的Pub/Sub)
- 组播支持:一对多通信,适合智能照明等场景
1.2 CoAP vs HTTP
| 对比维度 | CoAP | HTTP/1.1 | 适用场景 |
|---|---|---|---|
| 传输协议 | UDP | TCP | CoAP:低功耗 HTTP:可靠性优先 |
| 头部大小 | 4字节(固定) | 100-500字节 | CoAP:窄带网络 HTTP:宽带网络 |
| 连接开销 | 无(UDP无连接) | 三次握手 + 四次挥手 | CoAP:频繁短连接 HTTP:长连接 |
| 消息类型 | 4种(CON/NON/ACK/RST) | 请求/响应 | CoAP:灵活可靠性 HTTP:始终可靠 |
| 默认端口 | 5683(UDP) 5684(DTLS加密) |
80(TCP) 443(TLS) |
- |
| 资源发现 | 内置(/.well-known/core) | 需自定义 | CoAP:即插即用 HTTP:手动配置 |
| 观察者模式 | 内置(Observe选项) | 需WebSocket/SSE | CoAP:原生支持 HTTP:需额外协议 |
| 组播 | 支持(UDP组播) | 不支持 | CoAP:一对多 HTTP:一对一 |
| 功耗 | 极低(<1mA) | 较高(>10mA) | CoAP:电池供电 HTTP:常供电 |
选型建议:
- 选CoAP:受限设备(<128KB RAM)、窄带网络(NB-IoT)、低功耗要求、频繁短请求
- 选HTTP:资源充足、需与Web生态集成、已有HTTP基础设施
1.3 CoAP的应用场景
典型应用领域:
-
智能照明系统
- 开关控制:GET /light/status → {“on”: true}
- 调光:PUT /light/brightness → 80
- 组播控制:发送到224.0.1.187(CoAP组播地址)→ 所有灯同时响应
-
环境监测传感器
- 温湿度上报:Observe /sensor/temp → 每分钟推送更新
- 资源发现:GET /.well-known/core → 列出所有可用传感器
- 低功耗:NON消息(无需ACK)→ 功耗降低50%
-
智能农业灌溉
- 土壤湿度:GET /soil/moisture → 32%
- 启动灌溉:POST /irrigation/start → 2.01 Created
- NB-IoT网络:窄带传输,CoAP报文<50字节
-
工业设备监控
- 设备状态:Observe /machine/status → 实时推送异常
- 参数配置:PUT /machine/config → 更新PLC参数
- 块传输:传输大文件(日志、固件)
-
智能电表抄表
- 电量读取:GET /meter/power → 1234.5 kWh
- 组播抄表:一次请求 → 批量设备响应
- DTLS加密:防止数据篡改
二、CoAP协议原理
2.1 CoAP消息模型
CoAP定义了4种消息类型,提供灵活的可靠性机制:
CoAP消息类型
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. CON(Confirmable)可确认消息
客户端 ──┬──> CON [MID=0x1234] GET /temp
│
└──< ACK [MID=0x1234] 2.05 Content "25.5°C"
服务器
特点:需要ACK确认,超时自动重传(指数退避)
适用:重要数据,需保证可靠性
2. NON(Non-confirmable)不可确认消息
客户端 ────> NON [MID=0x5678] GET /status
(无ACK,单向发送)
服务器
特点:不需要确认,发完即忘
适用:频繁更新、可容忍丢失(传感器数据)
3. ACK(Acknowledgement)确认消息
客户端 <──── ACK [MID=0x1234] 2.05 Content "data"
服务器
特点:响应CON消息,携带响应数据(Piggybacked响应)
适用:快速响应(服务器立即有数据返回)
4. RST(Reset)重置消息
客户端 ──> CON [MID=0xABCD] GET /unknown
<── RST [MID=0xABCD](资源不存在)
服务器
特点:拒绝请求,无响应数据
适用:错误处理、资源不存在、不支持的方法
2.2 CoAP请求/响应模型
CoAP采用RESTful风格的请求/响应模型:
CoAP请求/响应交互
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
场景1:同步响应(Piggybacked Response)
┌────────────────────────────────────────────────┐
│ 客户端 服务器 │
│ │ │ │
│ │ CON [MID=0x1234] │ │
│ │ GET /temperature │ │
│ ├─────────────────────────────────>│ │
│ │ │ │
│ │ ACK [MID=0x1234] │ │
│ │ 2.05 Content "25.5°C" │ │
│ │<─────────────────────────────────┤ │
│ │ │ │
│ 延迟:<100ms(单次RTT) │
└────────────────────────────────────────────────┘
场景2:异步响应(Separate Response)
┌────────────────────────────────────────────────┐
│ 客户端 服务器 │
│ │ │ │
│ │ CON [MID=0x1234] │ │
│ │ GET /process │ │
│ ├─────────────────────────────────>│ │
│ │ │ 处理中...│
│ │ ACK [MID=0x1234](空确认) │ │
│ │<─────────────────────────────────┤ │
│ │ │ │
│ │ CON [MID=0x5678] │ 处理完成 │
│ │ 2.05 Content "result" │ │
│ │<─────────────────────────────────┤ │
│ │ │ │
│ │ ACK [MID=0x5678] │ │
│ ├─────────────────────────────────>│ │
│ │ │ │
│ 适用:耗时操作(数据库查询、传感器读取) │
└────────────────────────────────────────────────┘
场景3:不可靠传输(NON消息)
┌────────────────────────────────────────────────┐
│ 客户端 服务器 │
│ │ │ │
│ │ NON [MID=0xAAAA] │ │
│ │ GET /status │ │
│ ├─────────────────────────────────>│ │
│ │ │ │
│ │ NON [MID=0xBBBB] │ │
│ │ 2.05 Content "online" │ │
│ │<─────────────────────────────────┤ │
│ │ │ │
│ 无确认开销,功耗最低 │
└────────────────────────────────────────────────┘
2.3 CoAP观察者模式(Observe)
观察者模式允许客户端订阅资源变化,服务器主动推送更新:
CoAP Observe机制
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
客户端 服务器
│ │
│ CON [MID=0x1234] │
│ GET /temperature │
│ Observe: 0(注册观察者) │
├──────────────────────────────────────>│
│ │
│ ACK [MID=0x1234] │
│ 2.05 Content "25.5°C" │
│ Observe: 12(序列号) │
│<──────────────────────────────────────┤
│ │
│ ... 温度变化 ... │
│ │
│ CON [MID=0x5678] │ 温度升高
│ 2.05 Content "26.3°C" │
│ Observe: 13 │
│<──────────────────────────────────────┤
│ │
│ ACK [MID=0x5678] │
├──────────────────────────────────────>│
│ │
│ ... 温度变化 ... │
│ │
│ NON [MID=0x9ABC] │ 温度稳定
│ 2.05 Content "26.1°C" │ (使用NON)
│ Observe: 14 │
│<──────────────────────────────────────┤
│ │
│ RST [MID=0x9ABC](取消订阅) │
├──────────────────────────────────────>│
│ │
Observe序列号机制:
- 序列号递增表示新数据
- 乱序时,比较序列号决定是否更新
- 客户端发送RST取消订阅
2.4 CoAP资源发现
CoAP内置资源发现机制,无需手动配置:
CoAP资源发现流程
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
客户端 服务器
│ │
│ CON [MID=0x0001] │
│ GET /.well-known/core │
├──────────────────────────────────────>│
│ │
│ ACK [MID=0x0001] │
│ 2.05 Content │
│ Content-Format: application/link-format│
│ </temperature>;rt="temp-c";if="sensor",│
│ </humidity>;rt="humidity";if="sensor",│
│ </light/switch>;rt="switch";if="actuator"│
│<──────────────────────────────────────┤
│ │
资源链接格式(RFC 6690):
</path>;参数1="值1";参数2="值2",...
常用参数:
- rt(Resource Type):资源类型,如"temp-c"(摄氏温度)
- if(Interface):接口类型,如"sensor"、"actuator"
- ct(Content Type):内容格式,如0(text/plain)、50(JSON)
- sz(Size):资源大小估计
- obs(Observable):标记是否支持Observe
示例解析:
</temperature>;rt="temp-c";if="sensor";obs
→ 路径:/temperature
→ 类型:温度传感器(摄氏度)
→ 接口:传感器
→ 支持:Observe订阅
三、CoAP报文结构详解
3.1 CoAP报文格式
CoAP报文由固定头部(4字节) + 可变选项 + 负载组成:
CoAP报文结构(RFC 7252)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
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) ... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
字段说明:
┌──────────┬────────┬──────────────────────────────────┐
│ 字段 │ 长度 │ 说明 │
├──────────┼────────┼──────────────────────────────────┤
│ Ver │ 2 bit │ 版本号(固定为01,表示CoAP 1.0) │
│ T │ 2 bit │ 消息类型(0=CON, 1=NON, 2=ACK, 3=RST)│
│ TKL │ 4 bit │ Token长度(0-8字节) │
│ Code │ 8 bit │ 方法/响应码(如0.01=GET, 2.05=Content)│
│ Message ID│ 16 bit│ 消息ID(用于匹配请求/响应) │
│ Token │ 0-8 B │ 令牌(用于匹配请求/响应) │
│ Options │ 可变 │ 选项列表(Uri-Path、Content-Format等)│
│ 0xFF │ 1 B │ 负载标记(如果有负载) │
│ Payload │ 可变 │ 实际数据(JSON、CBOR、文本等) │
└──────────┴────────┴──────────────────────────────────┘
3.2 Code字段详解
Code字段(8位)编码格式:c.dd(类.详细)
CoAP Code编码规则
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
格式:c.dd(3位类码 + 5位详细码)
示例:2.05 → 类码=2(成功), 详细码=05 → 65(十进制)
请求方法(0.xx):
┌────────┬──────────┬────────┐
│ Code │ 十进制 │ 方法 │
├────────┼──────────┼────────┤
│ 0.01 │ 1 │ GET │
│ 0.02 │ 2 │ POST │
│ 0.03 │ 3 │ PUT │
│ 0.04 │ 4 │ DELETE │
└────────┴──────────┴────────┘
响应码(类似HTTP状态码):
┌────────┬──────────┬────────────────────┐
│ Code │ 十进制 │ 说明 │
├────────┼──────────┼────────────────────┤
│ 2.01 │ 65 │ Created(已创建) │
│ 2.02 │ 66 │ Deleted(已删除) │
│ 2.03 │ 67 │ Valid(有效) │
│ 2.04 │ 68 │ Changed(已修改) │
│ 2.05 │ 69 │ Content(内容) │
│ 4.00 │ 128 │ Bad Request(错误请求)│
│ 4.04 │ 132 │ Not Found(未找到) │
│ 4.05 │ 133 │ Method Not Allowed │
│ 5.00 │ 160 │ Internal Server Error│
│ 5.03 │ 163 │ Service Unavailable│
└────────┴──────────┴────────────────────┘
3.3 Options选项详解
CoAP选项使用TLV(Type-Length-Value)编码:
常用CoAP选项
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
┌─────────┬────────┬──────────────────────────────┐
│ 选项号 │ 名称 │ 说明 │
├─────────┼────────┼──────────────────────────────┤
│ 1 │ If-Match│ 条件匹配(ETag) │
│ 3 │ Uri-Host│ 目标主机 │
│ 4 │ ETag │ 实体标签(缓存验证) │
│ 5 │ If-None-Match│ 不存在时才创建 │
│ 6 │ Observe│ 观察者注册/取消 │
│ 7 │ Uri-Port│ 目标端口 │
│ 8 │ Location-Path│ 资源位置路径 │
│ 11 │ Uri-Path│ 请求路径(可重复) │
│ 12 │ Content-Format│ 内容格式 │
│ 14 │ Max-Age│ 缓存有效期(秒) │
│ 15 │ Uri-Query│ 查询参数(可重复) │
│ 17 │ Accept │ 可接受的内容格式 │
│ 23 │ Block2 │ 块传输(响应) │
│ 27 │ Block1 │ 块传输(请求) │
│ 28 │ Size2 │ 总大小(响应) │
│ 35 │ Proxy-Uri│ 代理URI │
│ 60 │ Size1 │ 总大小(请求) │
└─────────┴────────┴──────────────────────────────┘
Content-Format常用值:
┌──────┬─────────────────────────────┐
│ 值 │ 内容格式 │
├──────┼─────────────────────────────┤
│ 0 │ text/plain; charset=utf-8 │
│ 40 │ application/link-format │
│ 41 │ application/xml │
│ 42 │ application/octet-stream │
│ 47 │ application/exi │
│ 50 │ application/json │
│ 60 │ application/cbor(推荐) │
└──────┴─────────────────────────────┘
选项编码示例:
Uri-Path: /temperature
→ Option 11, Value="temperature"(12字节)
Content-Format: application/json
→ Option 12, Value=50(1字节)
3.4 报文示例解析
示例1:GET请求
十六进制报文:
44 01 12 34 B3 74 65 6D 70
解析:
┌────────────┬──────────┬────────────────────────────┐
│ 字段 │ 十六进制 │ 解析 │
├────────────┼──────────┼────────────────────────────┤
│ Ver+T+TKL │ 44 │ Ver=01, T=01(CON), TKL=4 │
│ Code │ 01 │ 0.01 → GET │
│ Message ID │ 12 34 │ 0x1234 = 4660 │
│ Token │ (无) │ TKL=4但未包含(简化示例) │
│ Option │ B3 │ Delta=11(Uri-Path),Len=3 │
│ Option Value│74 65 6D │ "tem" │
│ Option(续)│70 │ "p" → 完整路径"/temp" │
└────────────┴──────────┴────────────────────────────┘
完整请求:
CON GET /temp
示例2:响应(带负载)
十六进制报文:
64 45 12 34 C0 FF 32 35 2E 35
解析:
┌────────────┬──────────┬────────────────────────────┐
│ 字段 │ 十六进制 │ 解析 │
├────────────┼──────────┼────────────────────────────┤
│ Ver+T+TKL │ 64 │ Ver=01, T=10(ACK), TKL=4 │
│ Code │ 45 │ 2.05 → Content │
│ Message ID │ 12 34 │ 0x1234(匹配请求) │
│ Option │ C0 │ Delta=12(Content-Format),Len=0│
│ │ │ 隐含值=0(text/plain) │
│ Payload标记│ FF │ 0xFF(负载开始) │
│ Payload │ 32...35 │ "25.5"(ASCII) │
└────────────┴──────────┴────────────────────────────┘
完整响应:
ACK 2.05 Content
Content-Format: text/plain
Payload: "25.5"
四、CoAP开发实战
4.1 服务器端实现(Python)
使用aiocoap库实现CoAP服务器:
from datetime import datetime
class TemperatureResource(resource.Resource):
"""温度传感器资源"""
def __init__(self):
super().__init__()
self.temperature = 25.5 # 初始温度
self.observers = [] # 观察者列表
async def render_get(self, request):
"""处理GET请求"""
payload = f"{self.temperature:.1f}".encode('utf-8')
# 检查是否为Observe请求
if request.opt.observe is not None:
self.observers.append(request)
return aiocoap.Message(
code=aiocoap.Code.CONTENT, # 2.05
payload=payload,
content_format=0 # text/plain
)
async def render_put(self, request):
"""处理PUT请求(更新温度)"""
try:
new_temp = float(request.payload.decode('utf-8'))
self.temperature = new_temp
# 通知所有观察者
await self.notify_observers()
return aiocoap.Message(code=aiocoap.Code.CHANGED) # 2.04
except ValueError:
return aiocoap.Message(code=aiocoap.Code.BAD_REQUEST) # 4.00
async def notify_observers(self):
"""通知观察者温度变化"""
for observer in self.observers:
try:
payload = f"{self.temperature:.1f}".encode('utf-8')
response = aiocoap.Message(
code=aiocoap.Code.CONTENT,
payload=payload
)
observer.observation.trigger(response)
except Exception as e:
print(f"通知观察者失败: {e}")
self.observers.remove(observer)
class LightSwitchResource(resource.Resource):
"""灯光开关资源"""
def __init__(self):
super().__init__()
self.state = False # 灯光状态
async def render_get(self, request):
"""获取灯光状态"""
payload = b'{"on": true}' if self.state else b'{"on": false}'
return aiocoap.Message(
code=aiocoap.Code.CONTENT,
payload=payload,
content_format=50 # application/json
)
async def render_post(self, request):
"""切换灯光状态"""
self.state = not self.state
action = "打开" if self.state else "关闭"
print(f"{datetime.now()} - 灯光已{action}")
payload = b'{"on": true}' if self.state else b'{"on": false}'
return aiocoap.Message(
code=aiocoap.Code.CHANGED,
payload=payload,
content_format=50
)
async def main():
"""启动CoAP服务器"""
root = resource.Site()
# 注册资源
root.add_resource(['temperature'], TemperatureResource())
root.add_resource(['light', 'switch'], LightSwitchResource())
# 启动服务器
await aiocoap.Context.create_server_context(root, bind=('::', 5683))
print("CoAP服务器已启动,监听端口 5683")
print("可用资源:")
print(" GET coap://localhost/temperature")
print(" GET coap://localhost/temperature?obs (Observe)")
print(" PUT coap://localhost/temperature")
print(" GET coap://localhost/light/switch")
print(" POST coap://localhost/light/switch")
# 保持运行
await asyncio.get_running_loop().create_future()
if __name__ == '__main__':
asyncio.run(main())
4.2 客户端实现(Python)
from aiocoap import *
class CoapClient:
"""CoAP客户端"""
def __init__(self):
self.protocol = None
async def connect(self):
"""初始化CoAP协议"""
self.protocol = await Context.create_client_context()
async def get(self, uri: str):
"""GET请求"""
request = Message(code=Code.GET, uri=uri)
try:
response = await self.protocol.request(request).response
print(f"[GET] {uri}")
print(f"响应码: {response.code}")
print(f"负载: {response.payload.decode('utf-8')}\n")
return response
except Exception as e:
print(f"GET请求失败: {e}")
async def put(self, uri: str, payload: str):
"""PUT请求"""
request = Message(
code=Code.PUT,
uri=uri,
payload=payload.encode('utf-8')
)
try:
response = await self.protocol.request(request).response
print(f"[PUT] {uri}")
print(f"负载: {payload}")
print(f"响应码: {response.code}\n")
return response
except Exception as e:
print(f"PUT请求失败: {e}")
async def post(self, uri: str, payload: str = ""):
"""POST请求"""
request = Message(
code=Code.POST,
uri=uri,
payload=payload.encode('utf-8') if payload else b''
)
try:
response = await self.protocol.request(request).response
print(f"[POST] {uri}")
print(f"响应码: {response.code}")
print(f"负载: {response.payload.decode('utf-8')}\n")
return response
except Exception as e:
print(f"POST请求失败: {e}")
async def observe(self, uri: str, callback, duration: int = 60):
"""Observe订阅(观察者模式)"""
request = Message(code=Code.GET, uri=uri, observe=0)
print(f"[OBSERVE] {uri}")
print(f"订阅{duration}秒...\n")
observation_is_over = asyncio.Future()
async def handle_observation(response):
"""处理观察通知"""
if response.code.is_successful():
callback(response.payload.decode('utf-8'))
else:
print(f"Observe错误: {response.code}")
observation_is_over.set_result(None)
try:
request_handle = self.protocol.request(request)
request_handle.observation.register_callback(handle_observation)
# 订阅指定时长
await asyncio.sleep(duration)
request_handle.observation.cancel()
except Exception as e:
print(f"Observe失败: {e}")
async def demo():
"""客户端演示"""
client = CoapClient()
await client.connect()
# 1. GET请求
await client.get('coap://localhost/temperature')
# 2. PUT请求(更新温度)
await client.put('coap://localhost/temperature', '26.8')
# 3. POST请求(切换灯光)
await client.post('coap://localhost/light/switch')
# 4. Observe订阅
def on_temperature_change(value):
print(f"[通知] 温度更新: {value}°C")
await client.observe('coap://localhost/temperature', on_temperature_change, duration=30)
if __name__ == '__main__':
asyncio.run(demo())
4.3 嵌入式设备实现(C语言 + libcoap)
#include <coap2/coap.h>
#include <stdio.h>
#include <string.h>
// 全局变量
static float temperature = 25.5f;
static coap_resource_t *temp_resource = NULL;
/**
* 温度资源GET处理器
*/
static void temp_get_handler(
coap_context_t *ctx,
coap_resource_t *resource,
coap_session_t *session,
coap_pdu_t *request,
coap_binary_t *token,
coap_string_t *query,
coap_pdu_t *response
) {
// 设置响应码
response->code = COAP_RESPONSE_CODE(205); // 2.05 Content
// 生成负载
char payload[16];
snprintf(payload, sizeof(payload), "%.1f", temperature);
// 添加负载到响应
coap_add_data(response, strlen(payload), (unsigned char *)payload);
// 设置Content-Format选项
coap_add_option(response, COAP_OPTION_CONTENT_FORMAT,
coap_encode_var_safe(buf, sizeof(buf), COAP_MEDIATYPE_TEXT_PLAIN),
buf);
}
/**
* 温度资源PUT处理器
*/
static void temp_put_handler(
coap_context_t *ctx,
coap_resource_t *resource,
coap_session_t *session,
coap_pdu_t *request,
coap_binary_t *token,
coap_string_t *query,
coap_pdu_t *response
) {
size_t size;
unsigned char *data;
// 获取请求负载
coap_get_data(request, &size, &data);
if (size > 0) {
// 解析新温度值
char temp_str[16];
memcpy(temp_str, data, size < sizeof(temp_str) ? size : sizeof(temp_str) - 1);
temp_str[size] = '\0';
float new_temp = atof(temp_str);
if (new_temp >= -50.0f && new_temp <= 100.0f) {
temperature = new_temp;
printf("温度已更新: %.1f°C\n", temperature);
// 通知观察者
coap_resource_notify_observers(resource, NULL);
response->code = COAP_RESPONSE_CODE(204); // 2.04 Changed
} else {
response->code = COAP_RESPONSE_CODE(400); // 4.00 Bad Request
}
} else {
response->code = COAP_RESPONSE_CODE(400);
}
}
/**
* 启动CoAP服务器
*/
int main(void) {
coap_context_t *ctx = NULL;
coap_address_t serv_addr;
coap_endpoint_t *endpoint = NULL;
// 初始化CoAP上下文
coap_startup();
// 设置服务器地址
coap_address_init(&serv_addr);
serv_addr.addr.sin.sin_family = AF_INET;
serv_addr.addr.sin.sin_port = htons(5683);
serv_addr.addr.sin.sin_addr.s_addr = INADDR_ANY;
// 创建CoAP上下文
ctx = coap_new_context(NULL);
if (!ctx) {
fprintf(stderr, "创建CoAP上下文失败\n");
return -1;
}
// 创建UDP端点
endpoint = coap_new_endpoint(ctx, &serv_addr, COAP_PROTO_UDP);
if (!endpoint) {
fprintf(stderr, "创建UDP端点失败\n");
coap_free_context(ctx);
return -1;
}
// 创建温度资源
temp_resource = coap_resource_init(
coap_make_str_const("temperature"),
COAP_RESOURCE_FLAGS_NOTIFY_CON // 支持Observe
);
// 注册处理器
coap_register_handler(temp_resource, COAP_REQUEST_GET, temp_get_handler);
coap_register_handler(temp_resource, COAP_REQUEST_PUT, temp_put_handler);
// 添加资源到上下文
coap_add_resource(ctx, temp_resource);
printf("CoAP服务器已启动,监听端口 5683\n");
printf("资源: /temperature (GET, PUT, Observe)\n");
// 主循环
while (1) {
int result = coap_run_once(ctx, 1000); // 1秒超时
if (result < 0) {
break;
}
}
// 清理资源
coap_free_context(ctx);
coap_cleanup();
return 0;
}
五、行业应用案例分析
案例1:智能路灯组播控制系统
项目背景:
某智慧城市项目需要同时控制区域内数百盏路灯的开关、调光,传统HTTP方案需逐个发送指令,效率低下。
技术方案:
智能路灯CoAP组播架构
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
中心控制器(CoAP Client)
│
│ NON POST coap://[FF02::FD]/light/control
│ Payload: {"action": "on", "brightness": 80}
│
└─────> 组播地址 FF02::FD(所有CoAP节点)
│
├──> 路灯1 → 收到,立即执行
├──> 路灯2 → 收到,立即执行
├──> 路灯3 → 收到,立即执行
│... (同时响应)
└──> 路灯N → 收到,立即执行
优势:
- 单次发送:1条指令控制所有设备
- 零延迟:所有路灯同时接收
- 低带宽:仅1个报文(vs HTTP需N个请求)
- 容错性:部分路灯故障不影响其他设备
实现代码(Python客户端):
from aiocoap import *
async def multicast_control():
"""组播控制所有路灯"""
protocol = await Context.create_client_context()
# IPv6组播地址(所有CoAP节点)
multicast_uri = 'coap://[FF02::FD]/light/control'
# 控制指令
command = {
"action": "on",
"brightness": 80
}
request = Message(
code=Code.POST,
uri=multicast_uri,
payload=json.dumps(command).encode('utf-8'),
mtype=NON # 使用NON消息(无需ACK)
)
print(f"发送组播指令: {command}")
await protocol.request(request).response
print("指令已发送到所有路灯")
asyncio.run(multicast_control())
实施效果:
| 指标 | CoAP组播方案 | HTTP轮询方案 | 提升 |
|---|---|---|---|
| 控制延迟 | <50ms | 5-10s(N×50ms) | 99%↓ |
| 网络流量 | 1个报文 | N个请求+响应 | 99%↓ |
| 同步精度 | ±10ms | ±5s | 500倍 |
| 服务器负载 | 单次发送 | N次循环 | N倍↓ |
案例2:NB-IoT智能水表远程抄表
项目背景:
某水务公司的智能水表采用NB-IoT网络,需要低功耗、低带宽的通信协议实现远程抄表。
技术方案:
NB-IoT智能水表CoAP架构
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
智能水表(CoAP Server + NB-IoT模组)
│
│ 休眠状态(功耗<5μA)
│
├─ 定时唤醒(每24小时)
│ └─> NON POST coap://server/meter/data
│ Payload: {"id": "123456", "usage": 152.3}
│
├─ 远程查询(按需)
│ ← GET coap://meter/123456/reading
│ → 2.05 Content {"usage": 152.3, "battery": 85%}
│
└─ 异常告警(实时)
└─> CON POST coap://server/alarm
Payload: {"id": "123456", "type": "leak"}
← ACK 2.01 Created
CoAP报文大小分析:
- GET请求:15字节(头部4 + Token4 + Uri-Path7)
- 响应:35字节(头部4 + 负载31)
- HTTP请求:>150字节(头部+Keep-Alive)
功耗对比:
- CoAP/UDP:发送15字节 → 耗时<50ms → 功耗<10mAh
- HTTP/TCP:三次握手+发送+四次挥手 → 耗时>500ms → 功耗>50mAh
实现代码(嵌入式设备):
// NB-IoT模组 + CoAP上报
void meter_report_usage(float usage) {
coap_context_t *ctx;
coap_session_t *session;
coap_pdu_t *pdu;
coap_address_t dst;
// 唤醒NB-IoT模组
nbiot_wakeup();
// 解析服务器地址
coap_address_init(&dst);
dst.addr.sin.sin_family = AF_INET;
dst.addr.sin.sin_port = htons(5683);
inet_pton(AF_INET, "10.20.30.40", &dst.addr.sin.sin_addr);
// 创建CoAP会话
ctx = coap_new_context(NULL);
session = coap_new_client_session(ctx, NULL, &dst, COAP_PROTO_UDP);
// 构造NON POST请求
pdu = coap_pdu_init(COAP_MESSAGE_NON, COAP_REQUEST_POST, coap_new_message_id(session), coap_session_max_pdu_size(session));
// 添加Uri-Path
coap_add_option(pdu, COAP_OPTION_URI_PATH, 5, (unsigned char *)"meter");
coap_add_option(pdu, COAP_OPTION_URI_PATH, 4, (unsigned char *)"data");
// 添加负载(JSON)
char payload[64];
snprintf(payload, sizeof(payload), "{\"id\":\"123456\",\"usage\":%.1f}", usage);
coap_add_data(pdu, strlen(payload), (unsigned char *)payload);
// 发送(无需等待ACK)
coap_send(session, pdu);
printf("水表数据已上报: %.1f m³\n", usage);
// 清理资源
coap_session_release(session);
coap_free_context(ctx);
// NB-IoT模组进入休眠
nbiot_sleep();
}
实施效果:
| 指标 | CoAP方案 | HTTP方案 | 提升 |
|---|---|---|---|
| 报文大小 | 50字节 | 200字节 | 75%↓ |
| 上报耗时 | <50ms | >500ms | 90%↓ |
| 单次功耗 | <10mAh | >50mAh | 80%↓ |
| 电池续航 | 10年 | 2年 | 5倍 |
案例3:工业传感器数据采集
项目背景:
某工厂部署数百个温湿度传感器,需要实时监控并在异常时告警。
技术方案:
- 观察者模式:服务器订阅所有传感器,变化时自动推送
- 块传输:历史数据查询使用块传输(Block2),支持大数据量
- DTLS加密:敏感工艺数据使用DTLS保护
实现代码(Python服务器):
from aiocoap import *
class SensorMonitor:
"""传感器监控系统"""
def __init__(self):
self.sensors = {} # 传感器数据缓存
self.protocol = None
async def connect(self):
"""初始化CoAP协议"""
self.protocol = await Context.create_client_context()
async def discover_sensors(self):
"""资源发现:自动发现所有传感器"""
request = Message(code=Code.GET, uri='coap://224.0.1.187/.well-known/core')
try:
response = await self.protocol.request(request).response
resources = response.payload.decode('utf-8')
# 解析资源链接格式
for line in resources.split(','):
if 'rt="temp-sensor"' in line:
path = line.split(';')[0].strip('<>/')
sensor_id = path.split('/')[-1]
self.sensors[sensor_id] = {"type": "temperature", "path": path}
print(f"发现温度传感器: {sensor_id}")
except Exception as e:
print(f"资源发现失败: {e}")
async def subscribe_sensor(self, sensor_id: str):
"""订阅传感器数据(Observe)"""
sensor = self.sensors.get(sensor_id)
if not sensor:
print(f"传感器不存在: {sensor_id}")
return
uri = f"coap://10.0.1.{sensor_id}/{sensor['path']}"
request = Message(code=Code.GET, uri=uri, observe=0)
def on_notification(response):
"""处理传感器通知"""
if response.code.is_successful():
value = float(response.payload.decode('utf-8'))
sensor['value'] = value
sensor['timestamp'] = datetime.now()
print(f"[{sensor_id}] 温度: {value}°C")
# 异常检测
if value > 80.0:
self.send_alarm(sensor_id, value)
try:
request_handle = self.protocol.request(request)
request_handle.observation.register_callback(on_notification)
print(f"已订阅传感器: {sensor_id}")
except Exception as e:
print(f"订阅失败: {e}")
def send_alarm(self, sensor_id: str, value: float):
"""发送告警"""
print(f"⚠️ 告警: 传感器{sensor_id}温度过高({value}°C)!")
# 集成告警系统...
async def main():
monitor = SensorMonitor()
await monitor.connect()
# 1. 自动发现传感器
await monitor.discover_sensors()
# 2. 订阅所有传感器
for sensor_id in monitor.sensors.keys():
await monitor.subscribe_sensor(sensor_id)
# 保持运行
await asyncio.get_running_loop().create_future()
asyncio.run(main())
实施效果:
| 指标 | 数据 | 说明 |
|---|---|---|
| 传感器数量 | 500个 | 自动发现+订阅 |
| 上报延迟 | <100ms | Observe实时推送 |
| 带宽占用 | <1Mbps | 500×2KB/s |
| 告警响应 | <1s | 异常立即通知 |
案例4:智能农业土壤监测
项目背景:
某农业科技公司的土壤传感器(温度、湿度、N/P/K)需要低功耗长期运行。
技术方案:
- 低功耗模式:使用NON消息,无ACK开销
- 批量上报:每小时批量上传数据,减少唤醒次数
- 自适应采样:根据变化幅度调整上报频率
实现代码(嵌入式C):
typedef struct {
float temperature;
float humidity;
float nitrogen;
float phosphorus;
float potassium;
} SoilData;
void soil_sensor_report(SoilData *data) {
coap_context_t *ctx;
coap_session_t *session;
coap_pdu_t *pdu;
// 创建NON消息(不等待ACK,降低功耗)
pdu = coap_pdu_init(
COAP_MESSAGE_NON,
COAP_REQUEST_POST,
coap_new_message_id(session),
coap_session_max_pdu_size(session)
);
// CBOR编码(比JSON更紧凑)
uint8_t cbor_payload[64];
size_t len = encode_cbor_soil_data(data, cbor_payload, sizeof(cbor_payload));
// 添加Content-Format: application/cbor
uint8_t buf[4];
coap_add_option(pdu, COAP_OPTION_CONTENT_FORMAT,
coap_encode_var_safe(buf, sizeof(buf), 60), // CBOR=60
buf);
// 添加负载
coap_add_data(pdu, len, cbor_payload);
// 发送(无阻塞)
coap_send(session, pdu);
printf("土壤数据已上报 (CBOR %zu字节)\n", len);
}
// CBOR编码(RFC 7049)
size_t encode_cbor_soil_data(SoilData *data, uint8_t *buffer, size_t size) {
// CBOR Map (5 key-value pairs)
buffer[0] = 0xA5; // Map(5)
size_t offset = 1;
// "temp": 25.5
buffer[offset++] = 0x64; // Text(4)
memcpy(&buffer[offset], "temp", 4);
offset += 4;
buffer[offset++] = 0xF9; // Float16
*(uint16_t*)&buffer[offset] = float_to_half(data->temperature);
offset += 2;
// ... 其他字段类似编码
return offset;
}
CBOR vs JSON大小对比:
JSON格式(95字节):
{
"temp": 25.5,
"humidity": 68.2,
"N": 120,
"P": 45,
"K": 180
}
CBOR格式(45字节):
A5 # Map(5)
64 74656D70 # "temp"
F9 4CCC # Float16: 25.5
68 68756D6964697479 # "humidity"
F9 5110 # Float16: 68.2
61 4E # "N"
1878 # UInt: 120
61 50 # "P"
182D # UInt: 45
61 4B # "K"
18B4 # UInt: 180
节省空间:53%
实施效果:
| 指标 | CBOR | JSON | 提升 |
|---|---|---|---|
| 报文大小 | 45字节 | 95字节 | 53%↓ |
| 编码耗时 | <1ms | 3ms | 67%↓ |
| 传输功耗 | 6mAh | 12mAh | 50%↓ |
| 电池续航 | 5年 | 2.5年 | 2倍 |
案例5:智能楼宇暖通空调控制
项目背景:
某写字楼的暖通空调系统需要根据各区域温度自动调节,要求响应快、功耗低。
技术方案:
- 分区控制:每层楼独立组播组
- 自适应调节:根据温度变化动态调整制冷/制热
- 块传输:历史数据查询使用Block2分块传输
实现代码:
from aiocoap import *
class HVACController:
"""暖通空调控制器"""
async def set_zone_temperature(self, floor: int, target_temp: float):
"""设置楼层目标温度(组播)"""
protocol = await Context.create_client_context()
# 楼层组播地址(自定义)
multicast_uri = f'coap://[FF02::{floor}]/hvac/target'
request = Message(
code=Code.PUT,
uri=multicast_uri,
payload=str(target_temp).encode('utf-8'),
mtype=NON
)
await protocol.request(request).response
print(f"{floor}楼目标温度已设置为 {target_temp}°C")
async def query_history(self, zone_id: str, start_time: str, end_time: str):
"""查询历史数据(块传输)"""
protocol = await Context.create_client_context()
uri = f'coap://server/hvac/{zone_id}/history?start={start_time}&end={end_time}'
request = Message(code=Code.GET, uri=uri)
# Block2选项会自动处理
response = await protocol.request(request).response
# 可能包含多个块,自动重组
history_data = response.payload.decode('utf-8')
print(f"历史数据({len(history_data)}字节):{history_data[:100]}...")
asyncio.run(HVACController().set_zone_temperature(5, 24.0))
实施效果:
| 指标 | 数据 | 说明 |
|---|---|---|
| 分区数量 | 20层 × 4区 = 80区 | 独立控制 |
| 响应延迟 | <200ms | 温度设置→空调启动 |
| 节能效果 | 节省30%电费 | 精细化控制 |
| 历史查询 | 支持100MB数据 | Block2分块传输 |
六、性能优化实战
6.1 减少报文大小
优化策略:
# 优化前:JSON + text/plain(95字节)
payload_json = json.dumps({
"temperature": 25.5,
"humidity": 68.2,
"pressure": 1013.25
})
# 优化后:CBOR(45字节,节省53%)
payload_cbor = cbor2.dumps({
"temp": 25.5,
"hum": 68.2,
"press": 1013.25
})
# 更激进:自定义二进制格式(12字节,节省87%)
def encode_custom(temp, hum, press):
# Float16 + Float16 + UInt16
return struct.pack('!HHH',
float_to_half(temp),
float_to_half(hum),
int(press))
payload_binary = encode_custom(25.5, 68.2, 1013.25)
效果对比:
| 格式 | 大小 | 可读性 | 适用场景 |
|---|---|---|---|
| JSON | 95字节 | 高 | 调试、跨平台 |
| CBOR | 45字节 | 中 | 生产环境(推荐) |
| 自定义二进制 | 12字节 | 低 | 极限优化(NB-IoT) |
6.2 降低传输功耗
策略1:使用NON消息
# 高功耗:CON消息(需ACK)
request_con = Message(code=Code.POST, uri='coap://server/data', mtype=CON)
# 发送 → 等待ACK → 重传(超时) → 功耗高
# 低功耗:NON消息(无ACK)
request_non = Message(code=Code.POST, uri='coap://server/data', mtype=NON)
# 发送 → 完成 → 功耗降低50%
策略2:批量上报
// 优化前:每次测量立即上报(功耗高)
void sensor_loop_high_power() {
while (1) {
float value = read_sensor();
coap_send_value(value); // 立即发送(每分钟1次)
sleep(60);
}
}
// 优化后:批量上报(功耗降低80%)
void sensor_loop_low_power() {
float buffer[60];
int count = 0;
while (1) {
buffer[count++] = read_sensor();
sleep(60);
if (count >= 60) { // 每小时上报1次
coap_send_batch(buffer, count);
count = 0;
}
}
}
功耗对比:
| 模式 | 唤醒频率 | 单次功耗 | 总功耗 | 续航 |
|---|---|---|---|---|
| 实时上报 | 60次/小时 | 10mAh | 600mAh/h | 3天 |
| 批量上报 | 1次/小时 | 10mAh | 10mAh/h | 180天 |
6.3 可靠性与性能平衡
策略:混合使用CON和NON
async def smart_send(data, importance: str):
"""智能选择消息类型"""
if importance == "critical":
# 关键数据:使用CON保证可靠性
mtype = CON
elif importance == "normal":
# 普通数据:使用NON降低功耗
mtype = NON
else:
# 频繁数据:使用NON,偶尔CON校验
mtype = CON if random.random() < 0.1 else NON # 10%使用CON
request = Message(code=Code.POST, uri='coap://server/data', payload=data, mtype=mtype)
await protocol.request(request).response
七、故障排查方案
问题1:连接超时
现象:客户端发送CON请求后,长时间未收到ACK。
排查步骤:
from aiocoap import *
async def debug_timeout():
protocol = await Context.create_client_context()
request = Message(code=Code.GET, uri='coap://server/resource')
try:
# 设置超时
response = await asyncio.wait_for(
protocol.request(request).response,
timeout=10.0
)
print(f"成功: {response.code}")
except asyncio.TimeoutError:
print("超时!可能原因:")
print("1. 服务器未运行(检查端口5683)")
print("2. 防火墙阻止UDP(检查iptables)")
print("3. 网络不通(ping测试)")
print("4. 服务器负载过高(检查资源)")
常见原因与解决:
| 原因 | 检查命令 | 解决方案 |
|---|---|---|
| 服务器未运行 | netstat -uln | grep 5683 |
启动CoAP服务器 |
| 防火墙阻止 | iptables -L -n | grep 5683 |
开放UDP 5683端口 |
| 网络不通 | ping <server_ip> |
检查路由配置 |
| MTU问题 | ping -s 1472 <server_ip> |
调整MTU大小 |
问题2:Observe通知丢失
现象:订阅资源后,部分通知未收到。
排查与解决:
async def debug_observe():
protocol = await Context.create_client_context()
request = Message(code=Code.GET, uri='coap://server/temp', observe=0)
notification_count = 0
last_sequence = -1
async def on_notification(response):
nonlocal notification_count, last_sequence
# 检查Observe序列号
observe_value = response.opt.observe
if observe_value is not None:
if observe_value <= last_sequence:
print(f"⚠️ 乱序通知: {observe_value} (上次: {last_sequence})")
last_sequence = observe_value
notification_count += 1
print(f"通知 #{notification_count}, Observe序列: {observe_value}")
request_handle = protocol.request(request)
request_handle.observation.register_callback(on_notification)
await asyncio.sleep(60)
print(f"共收到 {notification_count} 次通知")
优化方案:
- 使用CON通知(服务器端):
# 重要通知使用CON(需ACK确认)
coap_resource_set_mode(resource, COAP_RESOURCE_FLAGS_NOTIFY_CON)
- 客户端缓存机制:
class ObserveCache:
def __init__(self):
self.cache = {}
def update(self, observe_seq, value):
if observe_seq > self.cache.get('seq', -1):
self.cache = {'seq': observe_seq, 'value': value}
return True # 新数据
return False # 旧数据,忽略
问题3:组播无响应
现象:发送组播请求后,没有设备响应。
排查步骤:
# 1. 检查组播路由
ip route show
# 2. 检查组播组成员
netstat -g
# 3. 手动加入组播组
ip maddress add 224.0.1.187 dev eth0
# 4. 抓包验证
tcpdump -i eth0 -n udp port 5683
常见原因:
| 原因 | 解决方案 |
|---|---|
| 未加入组播组 | 服务器端调用setsockopt(IP_ADD_MEMBERSHIP) |
| 组播TTL过小 | 设置IP_MULTICAST_TTL为合适值 |
| 网络不支持组播 | 使用unicast轮询替代 |
八、CoAP技术对比
8.1 CoAP vs MQTT
| 对比维度 | CoAP | MQTT | 适用场景 |
|---|---|---|---|
| 传输协议 | UDP | TCP | CoAP:低功耗 MQTT:可靠性优先 |
| 通信模式 | 请求/响应 观察者模式 |
发布/订阅 | CoAP:点对点 MQTT:多对多 |
| 报文开销 | 4字节头部 | 2字节头部 | CoAP:小报文 MQTT:大量消息 |
| QoS | CON/NON | QoS 0/1/2 | 相当 |
| 资源模型 | RESTful(URI) | 主题(Topic) | CoAP:Web风格 MQTT:消息队列 |
| 资源发现 | 内置 | 需自定义 | CoAP:即插即用 MQTT:手动配置 |
| 组播 | 支持 | 不支持 | CoAP:一对多 MQTT:需代理 |
| 代理/中间件 | 可选 | 必须(Broker) | CoAP:去中心化 MQTT:集中式 |
选型建议:
- 选CoAP:点对点通信、需组播、资源受限、类HTTP接口
- 选MQTT:多对多通信、需持久化、复杂订阅规则、已有Broker
8.2 CoAP vs HTTP
| 对比维度 | CoAP | HTTP/1.1 | HTTP/2 |
|---|---|---|---|
| 头部大小 | 4字节 | 100-500字节 | 动态压缩 |
| 传输协议 | UDP | TCP | TCP |
| 连接开销 | 无 | 三次握手 | 多路复用 |
| 二进制协议 | 是 | 否(文本) | 是 |
| 资源消耗 | <10KB RAM | >100KB RAM | >200KB RAM |
| 适用设备 | 8位MCU | 32位CPU | 高性能CPU |
迁移建议:
HTTP → CoAP迁移映射
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
HTTP CoAP
┌───────────────────┐ ┌───────────────────┐
│ GET /api/temp │ → │ GET /temp │
│ Host: server.com │ │ Uri-Host: server │
│ Accept: text/json │ │ Accept: 50 (JSON) │
└───────────────────┘ └───────────────────┘
HTTP状态码 → CoAP响应码
200 OK → 2.05 Content
201 Created → 2.01 Created
204 No Content → 2.04 Changed
400 Bad Request → 4.00 Bad Request
404 Not Found → 4.04 Not Found
500 Server Error → 5.00 Internal Server Error
九、总结
3句话记住CoAP
- CoAP是专为IoT受限设备设计的轻量级协议,基于UDP实现类似HTTP的RESTful风格,头部仅4字节,报文<100字节。
- 核心优势是低开销、低功耗、支持组播,适用于NB-IoT等窄带网络,电池续航可达数年。
- 内置资源发现和观察者模式,支持可靠/不可靠传输切换,灵活平衡性能与功耗。
核心要点
技术选型建议
| 场景 | 是否选CoAP | 原因 |
|---|---|---|
| NB-IoT/LoRa窄带网络 | ✅ 推荐 | 报文小、功耗低 |
| 电池供电设备 | ✅ 推荐 | 超低功耗(<1mA) |
| 智能照明组播控制 | ✅ 推荐 | 原生支持UDP组播 |
| 高带宽视频传输 | ❌ 不推荐 | UDP不可靠,选TCP |
| 需MQTT Broker生态 | ❌ 不推荐 | 选MQTT |
| Web浏览器访问 | ❌ 不推荐 | 浏览器不支持UDP |
最佳实践总结
-
报文优化:
- 优先使用CBOR(比JSON节省50%)
- 极限场景使用自定义二进制格式
- 利用Content-Format避免冗余描述
-
功耗优化:
- 频繁数据用NON消息(无ACK)
- 关键数据用CON消息(保证可靠)
- 批量上报减少唤醒次数
-
可靠性保证:
- 使用Observe序列号检测丢包
- 重要操作使用CON消息
- 实现客户端缓存机制
-
性能提升:
- 组播减少网络流量(N倍提升)
- 块传输支持大文件传输
- 资源发现实现即插即用
关键注意事项
- UDP特性:CoAP基于UDP,需考虑丢包、乱序问题
- NAT穿透:某些NAT设备可能阻止UDP,需配置端口转发
- 安全性:生产环境使用DTLS加密(端口5684)
- 代理支持:HTTP-CoAP代理实现Web集成
- 调试工具:使用Wireshark、coap-client等工具调试
扩展阅读
更多推荐

所有评论(0)