CoAP 客户端

概述

CoAP 客户端库允许应用发送 CoAP 请求并解析 CoAP 响应。该库可通过 CONFIG_COAP_CLIENT Kconfig 选项启用。应用通过请求中提供给 API 的回调获知响应。CoAP 客户端通过 socket 处理通信。由于 CoAP 客户端不会创建其使用的 socket,应用需要负责创建 socket。支持普通 UDP 或 DTLS socket。

基于 TCP 的 CoAP

还支持 RFC 8323 中规定的基于可靠传输(TCP/TLS)的 CoAP。可通过 CONFIG_COAP_CLIENT_TCP 启用。TCP 客户端内部管理连接建立、CSM(Capabilities and Settings Message)交换以及信令(Ping/Pong、Release、Abort)。与 UDP 客户端不同,应用不创建 socket,而是使用服务器地址调用 coap_client_tcp_connect()。用法示例见 CoAP TCP Client。

用法示例

以下示例展示了如何初始化 CoAP 客户端并发送请求:

static struct coap_client client;
struct coap_client_request req = { 0 };

coap_client_init(&client, NULL);

req.method = COAP_METHOD_GET;
req.confirmable = true;
strcpy(req.path, "test");
req.fmt = COAP_CONTENT_FORMAT_TEXT_PLAIN;
req.cb = response_cb;
req.payload = NULL;
req.len = 0;

/* Sock is a file descriptor referencing a socket, address is the net_sockaddr struct for the
 * destination address of the request or NULL if the socket is already connected.
 */
ret = coap_client_req(&client, sock, &address, &req, -1);

发送任何请求之前,需要先初始化 CoAP 客户端。初始化后,应用可以发送 CoAP 请求并等待响应。目前,单个 CoAP 客户端一次只能发送一个请求。系统中可以有多个 CoAP 客户端。

所提供的回调将在以下情况下被调用:

  • 请求有响应

  • 请求因某种原因失败

回调包含一个标志 last_block,用于指示响应中是否还有更多数据,并表示当前响应属于块传输的一部分。当 last_block 设为 true 时,响应已经完成,客户端从回调返回后即可处理下一个请求。

如果服务器对请求作出响应,库会通过请求结构中注册的响应回调将响应提供给应用。由于响应可能采用块传输,且客户端会针对每个块调用一次回调,因此应用应能够处理所有块,以便处理完整的响应。

在块传输期间,客户端会按照 RFC 7959 的要求比较已接收块的 ETag 选项。如果在传输过程中资源表示发生变化,传输将被中止,并以 result_code 设置为 -EBADMSG 调用回调。这比 RFC 的最低要求更严格:RFC 仅要求比较服务器提供的 ETag,而此实现还会在块之间 ETag 选项出现或消失时中止传输,因为无法验证这种带标签和未带标签块的混合情况。应用应丢弃已接收的部分数据,并可以重试请求。

以下是一个非常简单的响应处理函数示例:

void response_cb(const struct coap_client_response_data *data, void *user_data)
{
    if (data->result_code >= 0) {
            LOG_INF("CoAP response from server %d", data->result_code);
            if (data->last_block) {
                    LOG_INF("Last packet received");
            }
    } else {
            LOG_ERR("Error in sending request %d", data->result_code);
    }
}

应用也可以向请求中添加 CoAP 选项。以下示例展示了应用向初始请求添加 Block2 选项,以便针对预期大到需要块传输的资源,向服务器建议最大块大小(参见 RFC 7959 图 3:带早期协商的块式 GET)。

static struct coap_client client;
struct coap_client_request req = { 0 };

coap_client_init(&client, NULL);

req.method = COAP_METHOD_GET;
req.confirmable = true;
strcpy(req.path, "test");
req.fmt = COAP_CONTENT_FORMAT_TEXT_PLAIN;
req.cb = response_cb;
req.options[0] = coap_client_option_initial_block2();
req.num_options = 1;
req.payload = NULL;
req.len = 0;

ret = coap_client_req(&client, sock, &address, &req, -1);

应用还可以选择为 CoAP 上传注册载荷回调,而不是提供载荷指针。在这种情况下,CoAP 客户端库会在准备 PUT/POST 请求时调用该回调,以便应用可以分块提供载荷,而无需提供包含完整载荷的单个连续缓冲区。提供 Lorem Ipsum 字符串内容的示例回调如下所示:

static int lorem_ipsum_cb(size_t offset, const uint8_t **payload, size_t *len,
                          bool *last_block, void *user_data)
{
    size_t data_left;

    if (offset > LOREM_IPSUM_STRLEN) {
        return -EINVAL;
    }

    *payload = LOREM_IPSUM + offset;

    data_left = LOREM_IPSUM_STRLEN - offset;
    if (data_left <= *len) {
        *len = data_left;
        *last_block = true;
    } else {
        *last_block = false;
    }

    return 0;
}

然后可以为 PUT/POST 请求注册该回调,而不是提供载荷指针:

struct coap_client_request req = { 0 };

req.method = COAP_METHOD_PUT;
req.confirmable = true;
strcpy(req.path, "lorem-ipsum");
req.fmt = COAP_CONTENT_FORMAT_TEXT_PLAIN;
req.cb = response_cb;
req.payload_cb = lorem_ipsum_cb,

ret = coap_client_req(&client, sock, &address, &req, -1);

API 参考

CoAP client API
CoAP TCP client API