十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

RIOT unicoap 实战教程:用 C 语言编写支持 UDP 与 DTLS 的 CoAP 服务器

RIOT unicoap 实战教程:用 C 语言编写支持 UDP 与 DTLS 的 CoAP 服务器 RIOT unicoap 实战教程用 C 语言编写支持 UDP 与 DTLS 的 CoAP 服务器【免费下载链接】RIOTRIOT - The friendly OS for IoT项目地址: https://gitcode.com/GitHub_Trending/riot/RIOT导读本教程基于 RIOT 操作系统The friendly OS for IoT的unicoapUnified CoAP Suite框架带你从零编写一个完整的 CoAP 服务器应用先是注册一个返回Hello, World!的静态资源再实现一个从 URI 查询参数读取用户姓名、返回个性化问候的动态资源并最终通过 DTLSPSK 凭据提供加密访问。全流程使用 RIOT 的native板卡在 Linux 主机上完成验证无需任何无线硬件。读完本文你将掌握UNICOAP_RESOURCE资源声明、请求处理器编写、矢量iolist载荷、选项设置、DTLS 凭据装载以及借助 aiocoap 客户端脚本进行端到端测试的完整技能链。本文对应的完整可运行示例位于仓库 examples/networking/coap/unicoap_server服务端框架文档见 sys/net/application_layer/unicoap/docs/server.doc.mdunicoap的公共 API 头文件位于 sys/include/net/unicoap/server.h。1. 环境准备与工程骨架unicoap是 RIOT 中面向受限应用协议Constrained Application ProtocolCoAP的统一、模块化框架。相比 HTTPCoAP 专为带宽受限、内存有限的物联网节点设计支持资源发现、消息分片与端到端消息保护参见 sys/net/application_layer/unicoap/docs/doc.md。unicoap采用分层、模块化设计每种传输如 UDP、DTLS通过独立的 driver 模块接入后续也旨在逐步取代 RIOT 中更早期的gcoap、nanocoap等实现。1.1 编写 Makefile 并引入依赖在应用目录下新建Makefile首先引入unicoap及其服务器子模块。由于我们要同时支持 CoAP over UDP 与 CoAP over DTLSDTLS 底层同样依赖 UDP需要把对应的两个传输驱动一并加入USEMODULE unicoap USEMODULE unicoap_server USEMODULE unicoap_driver_udp USEMODULE unicoap_driver_dtls其中unicoap框架核心unicoap_serverCoAP 服务器子模块负责资源注册与请求的异步处理unicoap_driver_udp/unicoap_driver_dtls传输驱动分别监听默认的 5683 / 5684 端口端口号可在 Kconfig 中通过UNICOAP_UDP_PORT、UNICOAP_DTLS_PORT调整见 sys/net/application_layer/unicoap/Kconfig。按需引入其中任意一个即可也可以两者都引入。RIOT 允许切换网络后端network backend本教程选择 GNRC# Include packages that pull up and auto-init the link layer. # NOTE: 6LoWPAN will be included if IEEE802.15.4 devices are present USEMODULE netdev_default # Automatically initialize GNRC upon startup USEMODULE auto_init_gnrc_netif # Specify the mandatory networking modules USEMODULE gnrc_ipv6_default # Additional networking modules that can be dropped if not needed USEMODULE gnrc_icmpv6_echo最后在main.c中引入框架头文件#include net/unicoap.h工程实际使用的 Makefile 参见 examples/networking/coap/unicoap_server/Makefile。该文件默认BOARD ? native并通过LWIP_IPV4/LWIP_IPV6开关在 GNRC 与 lwIP 两套网络栈之间切换便于移植到不同板卡。2. 定义第一个 CoAP 资源CoAP 中的资源resource与 HTTP 中的端点endpoint行为类似。unicoap提供了两种注册方式一种是利用跨文件静态数组在编译期声明资源推荐另一种是在运行时手动创建监听器并注册资源。本教程使用前者。2.1 静态声明资源要使用编译期声明需要在Makefile中引入资源声明模块USEMODULE unicoap_server_resource_declarations随后用UNICOAP_RESOURCE宏定义一个资源。宏需要一个标识符参数例如hello该标识符仅要求是唯一的 C 变量名本身不参与路由纯粹是为了支撑跨文件数组的实现详见 sys/net/application_layer/unicoap/docs/resources-xfa.doc.mdUNICOAP_RESOURCE(hello) { // ... };2.2 分配路径path每个资源必须被赋予一个路径它是 CoAP URI 中主机/域名与端口之后的部分。将unicoap_resource_t.path属性设为UNICOAP_PATH_ROOT根路径/或使用UNICOAP_PATH构造自定义路径UNICOAP_RESOURCE(hello) { .path UNICOAP_PATH_ROOT, };例如路径/gelato/flavours/menu应写成UNICOAP_PATH(gelato, flavours, menu)注意单个路径组件内严禁包含斜杠即绝不能写UNICOAP_PATH(gelato/flavours, menu)。从 sys/include/net/unicoap/server.h 的源码可见UNICOAP_PATH实际构造了一个unicoap_pathspec_t把各组件以字符串字面量数组的形式保存并在编译期通过_UNICOAP_TRY_CHECK_PATH_COMPONENTS对组件做检查UNICOAP_PATH_ROOT则直接以NULL组件列表表示根路径后续匹配时unicoap_path_is_root()会据此判定见同一头文件的unicoap_path_is_root与unicoap_path_component_count内联函数。2.3 声明允许的 CoAP 方法接着用UNICOAP_METHODS宏列出该资源允许的 CoAP 方法类似 HTTP 方法。methods属性不能留空。如果客户端后续发送的方法不在允许集合内unicoap会以Method Not Allowed的 CoAP 响应拒绝请求UNICOAP_RESOURCE(hello) { .path UNICOAP_PATH_ROOT, .methods UNICOAP_METHODS(UNICOAP_METHOD_GET, UNICOAP_METHOD_FETCH), };2.4 按需限制传输协议可选地可以用UNICOAP_PROTOCOLS将资源限制到一组传输协议这在加密是强制要求的场景下尤为有用。本教程中的问候资源仍允许明文 UDP 访问UNICOAP_RESOURCE(hello) { // ... .protocols UNICOAP_PROTOCOLS(UNICOAP_PROTO_DTLS, UNICOAP_PROTO_UDP), // ... };需要说明的是unicoap在请求与资源匹配时会先检查当前传输是否在该资源的协议集合内若不在则视为该资源不存在。而protocols属性未设置时默认含义是允许所有协议如需显式表达可使用UNICOAP_PROTOCOLS_ALLOW_ALL或UNICOAP_PROTOCOLS_ALLOW_NONE见 sys/net/application_layer/unicoap/docs/server.doc.md 的 Request-resource matching 一节。2.5 请求可靠传输由于我们基于 CoAP over UDPDTLS 亦然其底层仍是 UDP可以指示unicoap发送**可确认confirmableCON**响应——unicoap会持续重传响应直到客户端确认收到为止。为此传入UNICOAP_RESOURCE_FLAG_RELIABLE标志该标志对非 UDP/DTLS 传输没有效果UNICOAP_RESOURCE(hello) { .path UNICOAP_PATH_ROOT, .methods UNICOAP_METHODS(UNICOAP_METHOD_GET, UNICOAP_METHOD_FETCH), .flags UNICOAP_RESOURCE_FLAG_RELIABLE, };2.6 绑定请求处理器最后指定一个处理发往/的请求的函数handle_hello_requestUNICOAP_RESOURCE(hello) { .path UNICOAP_PATH_ROOT, .methods UNICOAP_METHODS(UNICOAP_METHOD_GET, UNICOAP_METHOD_FETCH), .flags UNICOAP_RESOURCE_FLAG_RELIABLE, .handler handle_hello_request };请求处理器必须遵循unicoap_request_handler_t的函数签名共四个参数请求消息message、携带客户端地址等信息的辅助对象aux、响应上下文ctx以及可选的附加参数arg对应unicoap_resource_t.handler_arg详见 sys/include/net/unicoap/server.hstatic int handle_hello_request(unicoap_message_t* message, const unicoap_aux_t* aux, unicoap_request_context_t* ctx, void* arg) { // ... }2.7 处理器实现记录请求并应答处理器首先打印日志。借助unicoap_string_from_method取得 CoAP 方法即 CoAP 消息码的字符串表示用unicoap_message_payload_get_size获取载荷字节数。这将输出类似GET /, 0 bytes的日志printf( app: %s /, % PRIuSIZE bytes\n, unicoap_string_from_method(message-method), unicoap_message_payload_get_size(message) );对于以\0结尾的静态字符串unicoap提供了便捷初始化器unicoap_response_init_string。注意这里可以直接复用message参数的内存来构造响应。随后调用unicoap_send_response发送响应unicoap_response_init_string(message, UNICOAP_STATUS_CONTENT, Hello, World!); return unicoap_send_response(message, ctx);关于返回值有一项重要的约定返回值始终应表示成功0或错误负整数。以下两种行为均被视为致命错误调用send_response之后却返回状态码而不返回其结果以及既未调用send_response、又未返回状态码详见 server.doc.md 的 Responding 一节。完整的首个资源及其处理器在示例代码 examples/networking/coap/unicoap_server/main.c 中均有对应实现你可以直接对照学习。3. 打造动态 CoAP 资源个性化问候静态资源过于简单让我们开发一个/greeting资源它接受名为name的查询参数。当客户端请求coap://...host.../greeting?nameRIOTeer时服务器响应Hello, RIOTeer! Welcome to our itsy bitsy tiny CoAP server!。3.1 定义资源UNICOAP_RESOURCE(greeting) { .path UNICOAP_PATH(greeting), .flags UNICOAP_RESOURCE_FLAG_RELIABLE, .methods UNICOAP_METHODS(UNICOAP_METHOD_GET), .handler handle_greeting_request, };在 CoAP 消息的层面URIcoap://.../greeting?nameRIOTeer会被编码为两个选项Uri-Path: greeting与Uri-Query: nameRIOTeer。3.2 读取查询参数并校验在handle_greeting_request中我们通过unicoap_options_get_first_uri_query_by_name_string提取名为name的查询参数。若访问者未提供姓名直接返回Bad Request响应。处理器可以直接返回一个 CoAP 状态码作为快捷方式——此时unicoap会自动发送一个不带载荷的响应static int handle_greeting_request(unicoap_message_t* message, const unicoap_aux_t* aux, unicoap_request_context_t* ctx, void* arg) { (void)aux; (void)arg; ssize_t res 0; const char* name NULL; if ((res unicoap_options_get_first_uri_query_by_name_string(message-options, name, name)) 0) { printf(error: could not get name query: % PRIdSIZE (%s)\n, res, strerror(-res)); return UNICOAP_STATUS_BAD_REQUEST; } // ... }接着校验输入。示例代码设定了 30 个 UTF-8 字符的长度上限并要求做 UTF-8 有效性检查包括是否存在提前出现的\0终止符。当校验失败时这次我们借助unicoap_send_response与专门的错误消息把查询值无效明确告知客户端// Validate any input. Here, we apply a 30 UTF-8 character limit. You should perform UTF-8 // validation (is_valid_name). This also includes checking if theres a premature null terminator. if (res 30 || !is_valid_name(name, res)) { unicoap_response_init_string(message, UNICOAP_STATUS_BAD_REQUEST, invalid name query); return unicoap_send_response(message, ctx); }示例中的is_valid_name实现会遍历字符串长度内的每个字节若发现任何字节为 0 则判定非法参见 main.c。3.3 设置 Content-Format 选项规范的响应应设置Content-Format选项这里取text/plain。由于设置选项可能失败例如缓冲区容量不足应当最先执行。选项通过UNICOAP_OPTIONS_ALLOC在栈上分配再用unicoap_options_set_content_format设置格式UNICOAP_OPTIONS_ALLOC(options, 2); // Set Content-Format option to text/plain if (unicoap_options_set_content_format(options, UNICOAP_FORMAT_TEXT) 0) { return UNICOAP_STATUS_INTERNAL_SERVER_ERROR; } message-options options;关于缓冲容量分配时指定的容量应当是上界估计。如果你确切知道 CoAP 选项在内存中的表示方式也可以把容量设为精确的字节数——本例中只用到Content-Format这一个选项因此设为恰好 2 字节。请注意这个精确值也意味着无法再追加其他选项。相关可调参数见 sys/net/application_layer/unicoap/KconfigUNICOAP_OPTIONS_BUFFER_DEFAULT_CAPACITY是UNICOAP_OPTIONS_ALLOC未显式指定时的默认缓冲容量默认 32 字节UNICOAP_OPTIONS_MAX控制单条消息中允许的最大选项数默认 16UNICOAP_OPTIONS_FULL_SUPPORT则决定是否支持任意顺序地增删改选项关闭后乱序修改选项会触发运行时错误。3.4 用矢量iolist拼接动态载荷理论上可以把Hello,、姓名、后缀! Welcome ...三段写入一个大缓冲区再发送但当需要构造大缓冲区时下面的技术更可取不创建缓冲区而是创建一个矢量——即一系列载荷chunk片段的链表。诀窍在于网络后端会自行把这些 chunk 拷贝进发送缓冲区从而省去应用层的额外拷贝操作。首先是首、尾两个静态 chunk#define PREFIX Hello, iolist_t list { .iol_base PREFIX, .iol_len static_strlen(PREFIX), }; #define SUFFIX ! Welcome to our itsy bitsy tiny CoAP server! iolist_t suffix { .iol_base SUFFIX, .iol_len static_strlen(SUFFIX) };static_strlen是示例代码中定义的辅助宏#define static_strlen(string) sizeof(string) - 1用于在编译期取得字符串字面量的长度参见 main.c。接着加入动态的中间部分——即来自查询参数的姓名iolist_t name_chunk { .iol_next suffix, .iol_base (void*)name, .iol_len res }; list.iol_next name_chunk;通过.iol_next suffix把后缀链到中间块之后再用list.iol_next name_chunk把这条两段链追加到首个块之后矢量组装完成。最后设置状态与载荷并发送。unicoap支持多种略有差异的响应方式以减少样板代码可参阅 server.doc.md 的 Responding 一节获取扩展讲解unicoap_message_payload_set_chunks(message, list); unicoap_response_set_status(message, UNICOAP_STATUS_CONTENT); return unicoap_send_response(message, ctx);4. 为服务器开启 DTLSDTLSDatagram Transport Layer Security为 UDP 数据报提供传输层安全。我们在前文已经引入了unicoap_driver_dtls驱动但还需要包含额外头文件并向unicoap装载一个 DTLS 凭据#if IS_USED(MODULE_UNICOAP_DRIVER_DTLS) # include net/sock/dtls/creds.h # include net/credman.h # include net/dsm.h # include unicoap_example_dtls.h # define EXAMPLE_DTLS_CREDENTIAL_TAG 42 static const uint8_t psk_id_0[] PSK_DEFAULT_IDENTITY; static const uint8_t psk_key_0[] PSK_DEFAULT_KEY; static const credman_credential_t credential { .type CREDMAN_TYPE_PSK, .tag EXAMPLE_DTLS_CREDENTIAL_TAG, .params { .psk { .key { .s psk_key_0, .len sizeof(psk_key_0) - 1, }, .id { .s psk_id_0, .len sizeof(psk_id_0) - 1, }, } }, }; #endif要点说明这里使用的是PSK预共享密钥凭据身份标识为Client_identity、密钥为secretPSK二者的默认值定义在示例头文件 unicoap_example_dtls.h 中该文件同时提供了CONFIG_DTLS_ECC下的 ECDSA 密钥对示例供需要非对称加密的场景参考credman是 RIOT 中统一管理 DTLS 凭据的模块见sys下的net/credman标签tag42连同凭据类型需要保持唯一之后通过该标签把凭据绑定到 DTLS socket。在main函数中把凭据加入系统再通过unicoap_transport_dtls_get_socket取得 DTLS socket 并装载凭据int main(void) { # if IS_USED(MODULE_UNICOAP_DRIVER_DTLS) int res credman_add(credential); if (res 0 res ! CREDMAN_EXIST) { /* ignore duplicate credentials */ printf(app: cannot add credential to system: %d\n, res); return 1; } sock_dtls_t* dtls_socket unicoap_transport_dtls_get_socket(); assert(dtls_socket); if ((res sock_dtls_add_credential(dtls_socket, EXAMPLE_DTLS_CREDENTIAL_TAG)) 0) { printf(app: cannot add credential to DTLS sock: %d\n, res); return 1; } # endif }credman_add返回CREDMAN_EXIST表示该凭据已存在此处予以忽略重复凭据不做处理。完成之后你的服务器就可以通过加密的 DTLS 连接访问了。关于 DTLS 运行参数可在 sys/net/application_layer/unicoap/Kconfig 中调整UNICOAP_DTLS_PORT默认 5684、UNICOAP_DTLS_HANDSHAKE_TIMEOUT_MS握手超时默认 3000 ms设为 0 表示无限等待、UNICOAP_DTLS_MINIMUM_AVAILABLE_SESSION_SLOTS及对应的会话整理超时UNICOAP_DTLS_MINIMUM_AVAILABLE_SESSION_SLOTS_TIMEOUT_MS。4.1 关于 unicoap 初始化与运行线程示例的main中还展示了若干可选的运行时行为有助于理解框架默认情况下unicoap_init()会在main()之前由auto_init_unicoap自动调用它属于 RIOT 的DEFAULT_MODULE。可通过DISABLE_MODULE auto_init_unicoap关闭该默认行为此时需要自行调用unicoap_init()通过unicoap_transport_udp_get_socket()可取得底层 UDP socketsock API进而用unicoap_print_endpoint打印监听端点unicoap_transport_udp_add_socket()允许额外添加第二个监听 socket示例中监听 UDP 5682 端口若在 Kconfig 中把UNICOAP_CREATE_THREAD设为 0unicoap_init()不再创建专用线程而需要你在自己的线程如 main 线程里调用unicoap_loop_run()驱动处理循环CONFIG_UNICOAP_CREATE_THREADy时该函数不可用。示例的 app.config 默认开启了CONFIG_UNICOAP_CREATE_THREADy。5. 在 native 板卡上端到端测试示例代码自带一个基于 aiocoap 的client.py测试脚本。我们将使用 RIOT 的native板卡——客户端与服务器都运行在 Linux 主机上不涉及任何无线网络无需天线。5.1 编译并运行服务器cd RIOT/examples/networking/coap/unicoap_server BOARDnative make -j flash term运行后应看到类似如下的输出RIOT/dist/tools/pyterm/pyterm -ps RIOT/examples/networking/coap/unicoap_server/bin/native64/unicoap_server.elf --process-args tap0 Welcome to pyterm! Type /exit to exit. # RIOT native interrupts/signals initialized. # RIOT native64 board initialized. # RIOT native hardware initialization complete. # coap: registered 2 XFA resources # coap.transport.udp: zero_copy_guarantees1 creating UDP sock, port5683 if0 familyinet6 # coap.transport.dtls: creating DTLS sock, port5684 if0 familyinet6 # main(): This is RIOT! (Version: 2025.04-devel-634-RED-unicoap-02-server-minimal) # app: listening at UDP sock_tl_ep port5683 netif0 ipv6:: # app: listening at DTLS sock_tl_ep port5684 netif0 ipv6:: # app: using credential: typePSK idClient_identity keysecretPSK # app: IPv6 address: fe80::c0:ff:ee输出解读coap: registered 2 XFA resources框架通过跨文件数组XFA注册了 2 个静态资源hello与greeting服务器同时在 UDP 5683 与 DTLS 5684 端口监听 IPv6 全地址示例中 PSK 凭据的 identity/key 为Client_identity/secretPSK。提示如果tap0尚未启用可能需要先手动创建并拉起 tap 接口sudo ip tuntap add tap0 mode tap user ${USER} sudo ip link set tap0 up关于调试日志unicoap会根据CONFIG_UNICOAP_DEBUG_LOGGING与CONFIG_UNICOAP_ASSIST输出调试日志。前者是包含追踪日志的完整调试版本启用时也会自动包含辅助诊断后者是更轻量的版本仅记录 API 误用、缺失模块与修复建议。示例代码在 app.config 中将两者默认置为n关闭如需排查问题可按需打开。5.2 发送 CoAP 请求在第二个终端会话中运行python3 client.py -m GET -u coap://[fe80::c0:ff:ee%tap0]/greeting?nameRIOTer应看到脚本输出的若干调试日志最终是response: 2.05 Content bHello, RIOTer! Welcome to our itsy bitsy tiny CoAP server!恭喜你的服务器已经可以正常工作了5.3 抓包验证 CoAP 消息交互如果想亲眼看到实际传输的 CoAP 消息可开启第三个终端执行tcpdump -i tap0 -w coap-greeting.pcap然后按上文方式运行客户端脚本最后用CTRLC终止 tcpdump。之后可用 Wireshark 打开coap-greeting.pcap检查 CoAP 消息应能看到一个NON不可确认请求、随后一个CON可确认响应以及客户端回应的ACK消息——这正是UNICOAP_RESOURCE_FLAG_RELIABLE生效的体现unicoap发送 CON 响应并等待确认。5.4 客户端脚本能力速览client.py 是一个基于 aiocoap 的多功能客户端其命令行接口为client.py -m GET|PUT|POST|DELETE|PATCH|iPATCH|FETCH -u URI [--type NON|CON] [--observe] [-p PAYLOAD]常用参数-m/--methodCoAP 方法必填-u/--uri请求 URI必填例如coap://[fe80::c0:ff:ee%tap0]/greeting?nameRIOTer-mt/--type消息类型NON默认或CON--observe/--observe-cancel注册或取消资源观察Observe-p/--payload请求载荷-to/--timeout请求超时秒数默认 4 秒。脚本内置了 DTLS 客户端凭据PSKsecretPSK/ identityClient_identity因此在服务器开启 DTLS 后把 URI 协议换成coaps://即可测试加密路径。由于 CoAP over DTLS 走默认端口 5684测试 DTLS 时请相应调整 URI 中的端口。6. 深入理解请求-资源匹配与三种响应方式作为补充理解服务器的匹配与响应机制有助于写出更健壮的处理器。6.1 匹配顺序unicoap按以下顺序判定请求是否命中某个资源先遍历所有监听器listener再遍历该监听器内注册的所有资源。监听器的顺序遵循注册顺序但有两个特例第一个监听器是处理/.well-known/coreCoRE 资源发现的内置监听器第二个可能是承载 XFA 静态资源的监听器。一旦找到匹配资源后续资源不再检查。在此过程中不包含当前传输协议的资源直接跳过视为不存在然后比对路径若设置了UNICOAP_RESOURCE_FLAG_MATCH_SUBTREE标志注册在/foo/bar的资源也会匹配/foo/bar/zoo等子路径最后检查请求方法是否在允许集合内若方法不匹配且其他资源也都不匹配则返回Method Not Allowed若没有找到任何匹配资源则返回Not Found。若内置行为不符合你的场景可以为每个监听器自定义匹配算法设置unicoap_listener_t.request_matcher属性默认情况下属性未设置使用内置匹配器详见 server.doc.md 的 Request-resource matching 一节。运行时动态注册资源的完整代码模式构造资源数组 → 创建监听器 →unicoap_listener_register可选unicoap_listener_deregister同样收录在该文档中。6.2 三种响应技术unicoap提供多种响应方式server.doc.md 的 Responding 一节直接返回状态码无需响应载荷与选项时直接return UNICOAP_STATUS_XXX;。注意这要求在此之前已完成全部请求处理若涉及敏感操作、担心时序侧信道建议改用其他方式调用unicoap_send_response先用状态码可选地加上载荷与选项初始化响应消息再传入处理器参数中的ctx发送。可以复用message参数的内存但绝不能写载荷缓冲区或修改选项需要选项时请用UNICOAP_OPTIONS_ALLOC分配。send_response可能失败你需要自行处理错误可重试或改发Internal Server Error等。这也是大多数应用的首选方式延迟响应Deferred response该技术目前尚未在 RIOT 中实现文档标注 not available yet。另外处理器内的所有处理都运行在服务器处理循环中会阻塞该循环——若处理开销较大值得考虑将工作迁移到其他线程。7. 小结本文完整走通了基于 RIOTunicoap编写 CoAP 服务器的全流程通过USEMODULE组合引入框架、服务器、资源声明与 UDP/DTLS 驱动构建最小工程骨架用UNICOAP_RESOURCEUNICOAP_PATH/UNICOAP_PATH_ROOTUNICOAP_METHODS声明路径与方法用UNICOAP_PROTOCOLS限制传输、UNICOAP_RESOURCE_FLAG_RELIABLE启用 CON 可靠响应实现静态问候资源并进阶到读取name查询参数、校验输入、设置Content-Format、用 iolist 矢量载荷零拷贝拼接动态响应的动态资源通过credman PSK 凭据装载为服务器开启 DTLS 加密访问使用BOARDnative make -j flash term在 Linux 上运行并用 aiocoap 客户端与 tcpdump 完成端到端验证与抓包分析。unicoap仍处于快速演进中doc.md 明确标注 work in progress部分文档先行、功能尚未完全落地因此在实际项目中引入时建议结合当前仓库的 sys/net/application_layer/unicoap 源码与 sys/include/net/unicoap 公共头文件核对最新 API并留意 Kconfig 中CONFIG_UNICOAP_DEBUG_LOGGING、CONFIG_UNICOAP_ASSIST等调试开关以降低排障成本。【免费下载链接】RIOTRIOT - The friendly OS for IoT项目地址: https://gitcode.com/GitHub_Trending/riot/RIOT创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表