1. CoAP协议基础:从诞生到核心设计
各位同学好,我是你们这趟CoAP安全之旅的向导。今天咱们先不急着谈加密、握手那些硬核东西,先把地基打牢——CoAP协议到底是个什么玩意儿?它为什么会出现?跟HTTP比有什么不同?
我个人习惯是,学任何协议之前,先搞清楚它要解决什么问题。CoAP的诞生,说白了就是一句话:物联网设备太“穷”了。
1.1 诞生背景:HTTP太重了
你想想看,一个温度传感器,可能只有几十KB的RAM,几MHz的主频,跑个完整的HTTP协议栈?那简直是让小学生扛大鼎。HTTP的报文头动不动几百字节,TCP三次握手、TLS握手,一套流程下来,设备电池可能就掉了一格电。
我记得2015年做智能照明项目时,用HTTP控制灯泡开关,一个简单的“开灯”指令,HTTP请求+响应要来回好几趟,延迟高不说,功耗还大。后来换成CoAP,同样的功能,报文只有几十字节,响应时间从秒级降到百毫秒级。嗯,这就是CoAP诞生的直接动力——为受限节点和低功耗网络而生。
CoAP由IETF的CoRE工作组制定,标准是RFC 7252。它专门针对那些内存小、CPU弱、可能还用电池供电的设备,以及丢包率高的无线网络(比如6LoWPAN)。
核心定位: CoAP = HTTP的“瘦身版” + UDP的“轻量级” + M2M的“专用优化”。
1.2 CoAP vs HTTP:一张表看懂差异
很多新手问我:“CoAP是不是就是HTTP over UDP?” 其实没那么简单。我整理了一张对比表,你看完就明白了。
| 对比维度 | HTTP | CoAP |
|---|---|---|
| 传输层 | TCP(可靠、有序) | UDP(不可靠,但轻量) |
| 报文开销 | 头部通常几百字节 | 头部仅4字节,总报文可压缩到几十字节 |
| 请求/响应模型 | 同步阻塞(通常) | 异步非阻塞(基于消息ID匹配) |
| 资源发现 | 无标准机制(靠文档) | 内置 /.well-known/core 资源发现 |
| 观察模式 | WebSocket/长轮询 | 原生 Observe 选项(订阅推送) |
| 安全层 | TLS(TCP之上) | DTLS(UDP之上) |
| 典型场景 | Web浏览器、API服务 | 传感器、执行器、智能家居节点 |
看到没?CoAP把HTTP的“重装备”全卸了,只保留核心功能。但它不是简单粗暴地砍功能,而是做了精巧的适配。
我的经验: 如果你在做一个网关设备,既要对接云端HTTP,又要对接本地CoAP设备,建议在网关上做协议转换。我曾经在树莓派上用 libcoap 做代理,效果不错。
1.3 RESTful设计思想:资源就是一切
CoAP继承了HTTP的RESTful风格。什么意思呢?就是把物理世界里的“东西”抽象成资源,每个资源有一个URI,通过统一的接口来操作。
举个例子:一个灯泡,在CoAP世界里就是一个资源:
coap://192.168.1.100:5683/light/bulb1
对它做操作,就是标准的CRUD(增删改查),映射到CoAP方法上:
- GET — 读取资源状态(比如查询灯泡是开还是关)
- PUT — 更新资源(比如把灯泡亮度调到80%)
- POST — 创建子资源(比如新增一个定时任务)
- DELETE — 删除资源(比如移除这个灯泡)
这种设计的好处是:接口统一,语义清晰。不管你是温度传感器、门锁还是电机,操作方式都一样。我在做智能楼宇项目时,把空调、窗帘、灯光都抽象成资源,客户端代码复用率极高。
注意: CoAP的PUT和POST在语义上比HTTP更严格。PUT要求幂等(多次执行结果相同),POST不要求。我曾经踩过坑——用PUT去触发一个非幂等的动作(比如“增加温度1度”),结果重传时温度被加了两次。正确的做法是用POST。
1.4 消息类型:CON、NON、ACK、RST
这是CoAP最核心的机制之一。因为跑在UDP上,没有TCP的自动重传和确认,所以CoAP自己实现了四种消息类型来保证可靠性。我画了一张流程图,帮你理清它们的关系。
好,咱们逐个拆解:
1.4.1 CON — 可靠消息(Confirmable)
CON消息要求接收方必须回复ACK。如果发送方超时没收到ACK,就会重传。重传策略是二进制指数退避——第一次等2秒,第二次4秒,第三次8秒,直到达到最大重传次数(默认4次)。
典型场景: 控制指令(开锁、关阀门),必须确保对方收到。
1.4.2 NON — 不可靠消息(Non-confirmable)
NON消息不需要ACK,发出去就不管了。适合那些丢了也无所谓的场景,比如周期性的温度上报——丢了这一帧,下一帧马上就来。
典型场景: 传感器数据上报、心跳包。
1.4.3 ACK — 确认消息(Acknowledgement)
ACK是对CON的回应。注意:ACK必须携带和CON相同的Message ID,这样发送方才能把ACK和对应的CON匹配上。
这里有个细节:ACK可以“捎带”响应数据。比如服务端收到一个CON GET请求,可以直接在ACK报文里把响应数据带回去,省一次交互。这叫piggybacked response,我特别喜欢这个设计,省流量又省电。
1.4.4 RST — 复位消息(Reset)
RST表示接收方无法处理这条消息,或者明确拒绝。比如服务端不认识这个请求,或者客户端收到了一个它没发过的CON,都可以回RST。
我曾经在调试时遇到过:设备A给设备B发CON,但B已经重启了,状态丢了。B收到CON后,发现Message ID对不上,直接回RST。A收到RST后就知道B不认这个会话,需要重新建立。
避坑指南: 我曾经在项目中把NON和CON用反了。控制指令用了NON,结果丢包了设备没反应,排查了半天。后来统一规则:所有“写操作”用CON,“读操作”或“上报”用NON。这个原则一直沿用至今。
1.5 小结:CoAP的哲学
CoAP的设计哲学,我总结为三句话:
- 够用就好 — 不追求HTTP的全功能,只做物联网场景需要的
- UDP之上建可靠 — 用CON/ACK/RST机制,在不可靠的UDP上实现可控的可靠性
- 资源即一切 — RESTful风格,让设备接口像网页一样清晰
嗯,这一章的内容就到这里。你可能会问:“这些基础跟安全有什么关系?” 关系大了去了——CoAP的安全层DTLS就是建立在UDP之上的,而消息类型直接影响了重传攻击、重放攻击的防御策略。这些我们后面章节会一一展开。