CoAP 设备接入
名尚智联实现了 RFC 7252 精简子集 的 CoAP Server(端口 5683),用于低功耗、UDP 受限的物联网设备接入。不依赖 Californium,协议栈完全自研。
一、协议支持
| 能力 | 支持 |
|---|---|
| CON / NON / ACK / RST | ✅ |
| GET / POST / PUT / DELETE | ✅(仅用 POST) |
| Confirmable Ping | ✅(空 CON → 空 ACK) |
| 选项 delta/len 扩展编码(13/14) | ✅ |
| URI-Path / Content-Format / Max-Age | ✅ |
| Blockwise Transfer | ❌(暂不支持) |
| DTLS / OSCORE | ❌(暂不支持) |
二、接入流程
1. POST /dev/{serial}
Authorization: Bearer {authCode}
Content-Format: application/json
Payload: {"props":{"temperature":25.6,"humidity":62.3}}
2. 服务端认证
→ serial + authCode 查 ms_device 表
→ 支持主库 + tenant_* 动态库全局检索
3. 返回
2.04 Changed → 成功
4.00 Bad Request → payload 解析失败
4.01 Unauthorized → 认证失败
4.04 Not Found → 路径非法
4.05 Method Not Allowed → 非 POST三、命令行测试(coap-cli)
bash
# 安装
npm install -g coap-cli
# 上报属性
coap post coap://your-server:5683/dev/ESP32-001 \
-H "Authorization: Bearer <authCode>" \
-H "Content-Format: application/json" \
-d '{"props":{"temperature":25.6,"humidity":62.3}}'
# → 2.04 Changed四、Python 测试脚本
python
import socket
import struct
import json
def build_coap_post(uri_path: str, auth_token: str, payload: bytes) -> bytes:
"""构建 CoAP CON POST 请求(4 字节首部 + Token + 选项 + payload)"""
version = 1
msg_type = 0 # CON
code = 0x02 # 2.01 Created
mid = struct.unpack('>H', __import__('os').urandom(2))[0]
token = b'\x00' # 可选,空 Token
token_len = len(token)
# 选项:URI-Path + Content-Format + Authorization
# delta/len 编码参考 RFC 7252 第 3 节
options = bytearray()
# URI-Path = 11,Content-Format = 12,Authorization = 2048
uri_path = uri_path.encode()
cf = struct.pack('>H', 50) # application/json
auth = auth_token.encode()
# 简化版:只用 URI-Path
# 实际生产需完整选项编码,此处示意
...
header = bytearray([
(version << 6) | (msg_type << 4) | token_len,
code,
mid >> 8, mid & 0xFF
])
return bytes(header + token + options + b'\xff' + payload)
def parse_coap_response(data: bytes):
"""解析 CoAP 响应"""
first_byte = data[0]
ver = (first_byte >> 6) & 0x03
typ = (first_byte >> 4) & 0x03
tkl = first_byte & 0x0f
code = data[1]
mid = (data[2] << 8) | data[3]
# code 拆解
cls = code >> 5
detail = code & 0x1f
return {"ver": ver, "type": typ, "code": f"{cls}.{detail:02d}", "mid": mid}
# 发送
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.settimeout(5)
msg = build_coap_post("/dev/ESP32-001", "<authCode>",
json.dumps({"props": {"temperature": 25.6}}).encode())
sock.sendto(msg, ("your-server.com", 5683))
try:
resp, _ = sock.recvfrom(4096)
print(parse_coap_response(resp))
except socket.timeout:
print("超时未收到 ACK")五、响应码对照
| CoAP Code | 十进制 | 含义 |
|---|---|---|
2.04 | 0x44 | 成功(Changed) |
4.00 | 0x80 | Payload 格式错误 |
4.01 | 0x81 | 认证失败(serial / authCode 不匹配) |
4.04 | 0x84 | 路径非法(非 /dev/{serial}) |
4.05 | 0x85 | 方法不允许(非 POST) |
0.00 | 0x00 | 空 CON(Ping,正常用于保活) |
六、设备状态维护
- 首次连接:设备通过认证后自动在服务端创建会话(TTL 10 分钟)
- 会话复用:同 socket 的后续请求自动命中会话,无需重复认证
- 离线检测:超过
reader-idle-seconds(默认 300)未收到数据则标记离线
七、服务端配置
yaml
ms:
coap:
enabled: true
port: 5683
netty:
udp:
enabled: true # CoAP 复用 UDP Netty 通道
port: 11001 # 全局 UDP 端口(CoAP 用独立 5683)💡 CoAP 端口 5683 独立于全局 UDP 端口 11001,由
ms.netty.udp.enabled门控 +ms.coap.enabled独立开关控制。