7.2.4 CoAP 协议
2026/7/18大约 5 分钟常用组件组件网络CoAPUDPNB-IoT
7.2.4 CoAP 协议
📚 本节导读
学习时长:约 25 分钟
难度级别:⭐⭐⭐⭐☆
前置知识:UDP 协议基础、RESTful API 概念、第 7.2.1 节 Socket 基础
🎯 学习目标
- 理解 CoAP 协议的设计理念和适用场景
- 掌握 CoAP 与 MQTT 的差异对比
- 掌握 libcoap 客户端的使用(GET / PUT / POST / DELETE)
- 掌握 libcoap 服务端的使用(资源注册、请求处理)
- 了解 CoAP 资源发现机制
一、概述
CoAP(Constrained Application Protocol,受限应用协议)是 IETF 专为受限节点和受限网络设计的 Web 传输协议(RFC 7252)。它基于 UDP,采用 RESTful 风格,报文头极小(最小仅 4 字节),非常适合 NB-IoT、LoRa 等低功耗广域网场景。
OneOS 集成了两种 CoAP 实现:
| 实现 | 路径 | 特点 |
|---|---|---|
| libcoap v4.3.0 | components/net/protocols/coap/libcoap-v4.3.0/ | 完整 CoAP 实现,支持 RFC 7252,资源发现,块传输,DTLS 安全 |
| freecoap | components/net/protocols/coap/freecoap/ | 精简 CoAP 实现,代码量小,适合极低资源场景 |
二、CoAP vs MQTT 对比
| 特性 | CoAP | MQTT |
|---|---|---|
| 传输层 | UDP | TCP |
| 通信模型 | 请求/响应(类 HTTP) | 发布/订阅 |
| 报文头大小 | 最小 4 字节 | 最小 2 字节 |
| QoS | 可确认(CON)/ 不可确认(NON) | QoS 0/1/2 |
| 资源发现 | 内置(/.well-known/core) | 无内置 |
| 块传输 | 支持(RFC 7959) | 不支持 |
| 安全性 | DTLS | TLS |
| 适用场景 | NB-IoT、资源受限 RESTful API | 通用物联网消息推送 |
三、libcoap 核心数据结构
/* CoAP 上下文:管理所有 CoAP 会话和资源 */
typedef struct coap_context_t coap_context_t;
/* CoAP 会话:表示一个客户端-服务器之间的通信会话 */
typedef struct coap_session_t coap_session_t;
/* CoAP PDU(协议数据单元):表示一个 CoAP 报文 */
typedef struct coap_pdu_t coap_pdu_t;
/* CoAP 资源:服务端可访问的资源 */
typedef struct coap_resource_t {
coap_str_const_t *uri_path; /* 资源 URI 路径 */
int flags; /* 资源标志 */
coap_method_handler_t handler; /* 资源处理方法 */
void *user_data; /* 用户自定义数据 */
} coap_resource_t;四、libcoap 核心 API 参考
/* 创建 CoAP 上下文 */
coap_context_t *coap_new_context(const coap_address_t *listen_addr);
/* 创建客户端会话 */
coap_session_t *coap_new_client_session(coap_context_t *ctx,
const coap_address_t *local_if, const coap_address_t *server,
coap_proto_t proto);
/* 创建新的 PDU */
coap_pdu_t *coap_new_pdu(coap_session_t *session,
coap_pdu_type_t type, coap_pdu_code_t code, coap_mid_t mid);
/* 发送 PDU */
int coap_send(coap_session_t *session, coap_pdu_t *pdu);
/* 添加选项到 PDU */
int coap_add_option(coap_pdu_t *pdu, uint16_t type,
uint16_t len, const uint8_t *data);
/* 添加数据到 PDU */
int coap_add_data(coap_pdu_t *pdu, size_t len, const uint8_t *data);
/* 初始化资源 */
coap_resource_t *coap_resource_init(coap_str_const_t *uri_path, int flags);
/* 注册资源处理方法 */
void coap_register_handler(coap_resource_t *resource,
coap_request_t method,
coap_method_handler_t handler);
/* 将资源添加到上下文 */
void coap_add_resource(coap_context_t *context, coap_resource_t *resource);
/* 处理一次 CoAP IO 事件 */
int coap_run_once(coap_context_t *ctx, unsigned int timeout_ms);五、CoAP 请求方法与响应码
| 方法 | 编码 | 描述 |
|---|---|---|
| GET | 0.01 | 获取资源表示 |
| POST | 0.02 | 创建新资源 |
| PUT | 0.03 | 更新或创建资源 |
| DELETE | 0.04 | 删除资源 |
| 响应码 | 描述 |
|---|---|
| 2.01 Created | 资源创建成功 |
| 2.04 Changed | 资源修改成功 |
| 2.05 Content | 响应包含内容 |
| 4.00 Bad Request | 请求格式错误 |
| 4.04 Not Found | 资源不存在 |
| 5.00 Internal Server Error | 服务器内部错误 |
六、CoAP 客户端示例
#include <coap3/coap.h>
#include <oneos/os_kernel.h>
void coap_get_example(void)
{
coap_context_t *ctx = NULL;
coap_session_t *session = NULL;
coap_address_t dst_addr;
coap_pdu_t *pdu = NULL;
coap_mid_t mid;
/* 1. 设置目标服务器地址 */
coap_address_init(&dst_addr);
dst_addr.addr.sin.sin_family = AF_INET;
dst_addr.addr.sin.sin_port = htons(5683); /* 标准 CoAP 端口 */
inet_pton(AF_INET, "127.0.0.1", &dst_addr.addr.sin.sin_addr);
/* 2. 创建 CoAP 上下文 */
ctx = coap_new_context(NULL);
if (!ctx) {
os_kprintf("[CoAP] create context failed\r\n");
return;
}
/* 3. 创建客户端会话 */
session = coap_new_client_session(ctx, NULL, &dst_addr, COAP_PROTO_UDP);
if (!session) {
os_kprintf("[CoAP] create session failed\r\n");
coap_free_context(ctx);
return;
}
/* 4. 构建 GET 请求 PDU */
mid = coap_new_message_id(session);
pdu = coap_new_pdu(session, COAP_MESSAGE_CON, COAP_REQUEST_CODE_GET, mid);
if (!pdu) {
os_kprintf("[CoAP] create PDU failed\r\n");
coap_session_release(session);
coap_free_context(ctx);
return;
}
/* 添加 URI-Path 选项:访问 /hello 资源 */
coap_add_option(pdu, COAP_OPTION_URI_PATH,
5, (const uint8_t *)"hello");
/* 5. 发送请求 */
os_kprintf("[CoAP] sending request to /hello\r\n");
int ret = coap_send(session, pdu);
if (ret == COAP_INVALID_MID) {
os_kprintf("[CoAP] send failed\r\n");
}
/* 6. 等待响应 */
coap_run_once(ctx, 5000);
/* 7. 清理资源 */
coap_session_release(session);
coap_free_context(ctx);
os_kprintf("[CoAP] done\r\n");
}七、CoAP 服务端示例
#include <coap3/coap.h>
#include <oneos/os_kernel.h>
/* GET /hello 处理函数 */
static void hnd_get_hello(coap_resource_t *resource,
coap_session_t *session,
const coap_pdu_t *request,
const coap_string_t *query,
coap_pdu_t *response)
{
coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT);
const char *msg = "Hello from OneOS CoAP Server!\r\n";
coap_add_data(response, strlen(msg), (const uint8_t *)msg);
}
/* CoAP 服务端主函数 */
void coap_server_example(void)
{
coap_context_t *ctx = NULL;
coap_address_t addr;
coap_resource_t *resource;
/* 1. 设置监听地址 */
coap_address_init(&addr);
addr.addr.sin.sin_family = AF_INET;
addr.addr.sin.sin_port = htons(5683);
addr.addr.sin.sin_addr.s_addr = INADDR_ANY;
/* 2. 创建 CoAP 上下文 */
ctx = coap_new_context(&addr);
if (!ctx) {
os_kprintf("[CoAP Server] create context failed\r\n");
return;
}
os_kprintf("[CoAP Server] listening on port 5683\r\n");
/* 3. 注册 /hello 资源(GET) */
resource = coap_resource_init(coap_make_str_const("hello"), 0);
coap_register_handler(resource, COAP_REQUEST_GET, hnd_get_hello);
coap_add_resource(ctx, resource);
os_kprintf("[CoAP Server] resources registered: GET /hello\r\n");
/* 4. 事件循环 */
while (1) {
coap_run_once(ctx, 1000);
}
}八、CoAP 资源发现
CoAP 内置资源发现机制,客户端可以通过 GET /.well-known/core 获取服务端所有可用资源:
/* 访问 /.well-known/core 资源发现端点 */
coap_add_option(pdu, COAP_OPTION_URI_PATH,
15, (const uint8_t *)".well-known");
coap_add_option(pdu, COAP_OPTION_URI_PATH,
4, (const uint8_t *)"core");📝 本节小结
本节介绍了 OneOS 中 CoAP 协议的使用。CoAP 基于 UDP 协议,采用 RESTful 风格,报文头极小,内置资源发现机制,非常适合 NB-IoT 等低功耗广域网场景。
核心要点回顾:
- CoAP 基于 UDP,采用请求/响应模型,与 HTTP 语义相似
- libcoap 提供完整的 CoAP 客户端和服务端实现
- 支持 CON(可确认)和 NON(不可确认)两种消息类型
- 内置资源发现(
/.well-known/core),无需额外配置 - 支持 DTLS 安全传输