返回首页

CoAP 协议学习笔记

1. CoAP 概述

1.1 什么是 CoAP

Constrained Application Protocol (CoAP) 是专为受限设备设计的 Web 传输协议,适用于物联网场景。

1.2 特点

1.3 与 HTTP 对比

特性CoAPHTTP
传输UDPTCP
头部4 字节+几十字节
方法GET/POST/PUT/DELETEGET/POST/PUT/DELETE
资源发现支持可选

2. CoAP 消息格式

2.1 消息类型

2.2 消息结构

 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) ...
+-+-+-+-+-+-+-+-+++++++++++++++++++++++++++++++++-+-+-+-+-+-+-+-+
|                    Payload (if any) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+++++++++++++++++-+-+-+-+-+-+-+-+

2.3 CoAP 代码

3. Zephyr CoAP 实现

3.1 Kconfig 配置

CONFIG_COAP=y
CONFIG_COAP_NETWORKING=y
CONFIG_COAP_MESSAGE_MAX_SIZE=1152

3.2 CoAP 服务端

#include <zephyr/net/coap.h>
#include <zephyr/net/coap_link_format.h>

// CoAP 资源定义
static struct coap_resource resources[] = {
    {
        .path = "sensor/temperature",
        .get = get_temperature,
        .post = post_temperature,
    },
    {
        .path = "led",
        .put = put_led,
    },
    {}
};

// GET 处理器
static int get_temperature(struct coap_resource *resource,
                          struct coap_packet *request,
                          struct coap_packet *response,
                          uint8_t *data,
                          uint16_t data_len,
                          struct sockaddr *addr,
                          socklen_t addr_len) {
    char payload[32];
    int len = snprintf(payload, sizeof(payload), 
                      "{\"temp\":%d}", temperature);
    
    coap_packet_append_payload(response, payload, len);
    coap_header_set_code(response, COAP_RESPONSE_CODE_CONTENT);
    
    return 0;
}

3.3 CoAP 客户端

// 发送 CoAP GET 请求
int coap_get(const char *uri) {
    struct coap_packet request;
    uint8_t request_token[8];
    uint8_t request_data[128];
    
    // 构建请求
    coap_packet_init(&request, request_data, sizeof(request_data),
                    1, COAP_TYPE_CON,
                    8, coap_next_token(request_token, 8),
                    coap_next_id(), COAP_METHOD_GET, NULL);
    
    // 添加 URI 路径
    coap_packet_append_option(&request, COAP_OPTION_URI_PATH, 
                             "sensor", 6);
    
    // 发送请求
    return send_request(&request, addr, addr_len);
}

4. CoAP Observe(观察模式)

4.1 Observe 概述

4.2 Observe 实现

// 观察者列表
static sys_slist_t observers;

// 注册观察者
static int observe_register(struct coap_resource *resource,
                           struct coap_packet *request,
                           struct coap_observer *observer) {
    sys_slist_append(&observers, &observer->node);
    return 0;
}

// 通知观察者
static void notify_observers(struct coap_resource *resource,
                             uint8_t *data, size_t len) {
    sys_snode_t *node;
    struct coap_observer *observer;
    
    SYS_SLIST_FOR_EACH_NODE(&observers, node) {
        observer = CONTAINER_OF(node, struct coap_observer, node);
        
        // 发送通知(具体实现取决于当前 socket/context 组织方式)
        send_notification(observer, data, len);
    }
}

5. CoAP 与 BLE

5.1 BLE GATT CoAP

这部分更适合作为“概念迁移”理解,而不是 Zephyr/NCS 当前的标准 CoAP 用法。

5.2 6LoWPAN CoAP

BLE / IEEE 802.15.4 → IPv6 → UDP → CoAP

6. CoAP 安全

6.1 DTLS 加密

CoAP 的安全版本通常称为 CoAPS,本质上是在 UDP socket 之上叠加 DTLS。

6.2 Pre-Shared Key

// PSK 预共享密钥
static uint8_t psk_id[] = "client1";
static uint8_t psk_key[] = {0x01, 0x02, 0x03, /*...*/};

7. CoAP 代理

7.1 CoAP-HTTP 代理

CoAP Client → CoAP Proxy → HTTP Server

7.2 代理实现

CoAP-HTTP 代理属于网关层设计,通常包含三步:

  1. 解析 CoAP 方法、URI、payload
  2. 映射到 HTTP 方法、路径和消息体
  3. 将 HTTP 响应重新编码为 CoAP 响应

这里不建议把某个 http_request 结构体和 http_client_req() 直接写成通用模板,因为实际工程里 HTTP 层封装差异很大。

8. 实用示例

8.1 温度传感器

// 资源:/temperature
static int get_temperature(struct coap_resource *resource,
                          struct coap_packet *request,
                          struct coap_packet *response,
                          uint8_t *data, uint16_t data_len,
                          struct sockaddr *addr, socklen_t addr_len) {
    int temp = read_temperature_sensor();
    
    snprintf(data, data_len, "%d", temp);
    coap_packet_append_payload(response, data, strlen(data));
    
    return 0;
}

8.2 LED 控制

// 资源:/led
static int put_led(struct coap_resource *resource,
                  struct coap_packet *request,
                  struct coap_packet *response,
                  uint8_t *data, uint16_t data_len,
                  struct sockaddr *addr, socklen_t addr_len) {
    // 解析 JSON: {"on": true}
    bool led_on = parse_led_state(data, data_len);
    
    set_led(led_on);
    
    coap_header_set_code(response, COAP_RESPONSE_CODE_CHANGED);
    return 0;
}

9. 测试工具

9.1 coap-client

# GET 请求
coap-client -m get coap://192.168.1.100/sensor/temperature

# PUT 请求
coap-client -m put -e '{"on":true}' coap://192.168.1.100/led

9.2 Copper

10. 最佳实践

  1. 使用 CON 消息:关键控制命令
  2. 使用 NON 消息:周期传感器数据
  3. 启用 Observe:状态监控场景
  4. 使用 DTLS:敏感数据传输
  5. 合理设置超时:网络条件自适应

参考资料