7.5.1 CMS Connect 组件
7.5.1 CMS Connect 组件
📚 本节导读
学习时长:约 30 分钟
难度级别:⭐⭐⭐⭐☆
前置知识:MQTT 协议基础、TCP/UDP 网络编程、第 7.2 节网络协议栈
🎯 学习目标
- 理解 CMS Connect 组件的架构设计与定位
- 掌握 CMS Connect 组件的线上下载与 Menuconfig 配置
- 掌握 MQTT 接入 API(
cms_mqtt_*)及数据结构 - 掌握 TCP/UDP 接入 API(
cms_tcp_*/cms_udp_*) - 能够使用 CMS Connect 组件实现设备接入 CMS 中台
一、CMS Connect 组件概述
CMS Connect 组件承接端侧设备与 CMS(中国移动)中台的连接任务,向开发者提供统一的接入协议,并负责端侧设备与中台之间的消息分发。组件源码位于 components/cms/(核心为预编译库 source/library/*.lib + 移植层 port/)。
一句话核心:cms_mqtt_init(scode, ¶m) 创建实例 → cms_mqtt_connect(handle, &connect_param) 建连 → cms_mqtt_subscribe/cms_mqtt_publish 订阅/收发消息 → cms_mqtt_disconnect/cms_mqtt_deinit 释放;TCP/UDP 同理(cms_tcp_*/cms_udp_*)。
当前版本主要功能:
- 支持 MQTT 协议接入(QoS 0/1,不支持 QoS 2)
- 支持 TCP 协议接入
- 支持 UDP 协议接入
- 支持设备与服务间心跳保持
1.1 线上管理组件(需远程下载)
CMS Connect 属于"线上管理组件",官方源码不随 OneOS 内核/组件默认打包。使用前需先通过 OneOS Studio 的组件远程下载/管理功能(或对应的 pkg/pkgs 包管理指令)从 OneOS 线上仓库把 CMS 组件下载到本地工程。下载完成后,组件出现在工程 components/cms/ 目录,即可正常 Menuconfig 配置与编译。
关键理解:若在 Menuconfig 的
Components → CMS下找不到 CMS Connect 配置项,说明组件尚未下载;按官方文档完成下载后再进入配置。
1.2 架构设计
┌──────────────────────────────────────────────┐
│ 应用协议层 │
│ MQTT │ CoAP │ LwM2M │ TCP │ UDP │
├──────────────────────────────────────────────┤
│ 协议封装层 │
│ CMS 协议封装 │ 协议验证 │ 消息分发 │
├──────────────────────────────────────────────┤
│ 连接管理层 │
│ Socket 管理 │ 心跳保持 │
├──────────────────────────────────────────────┤
│ 依赖:CMS ID(链路鉴权)│ OneOS 网络能力 │
└──────────────────────────────────────────────┘各层职责:
- 应用协议层:提供主流物联网应用协议接入(MQTT/CoAP/LwM2M/TCP/UDP),且持续丰富中;
- 协议封装层:提供 CMS 协议封装、验证及消息分发功能;
- 连接管理层:提供 socket 管理、心跳保持等功能;
- 依赖:链路认证依赖 CMS ID 组件提供互信及鉴权能力;基础网络依赖 OneOS 网络能力。
二、Menuconfig 配置
使用 CMS Connect 组件可通过 Menuconfig 图形化工具配置,路径为:
(Top) → Components → CMS → CMS Connect
OneOS Configuration
[*] Using CMS connect component
Application Protocol (MQTT) --->
(172.16.21.83) CMS Server Address (NEW)
(6588) CMS Server Port (NEW)
(1460) Size of send buffer (NEW)
(1460) Size of receive buffer (NEW)
-*- Enable/disable CMS heartbeat (NEW)
(300) CMS heartbeat interval(s) (NEW)配置项说明:
| 配置项 | 说明 |
|---|---|
Using CMS connect component | CMS Connect 组件总开关 |
Application Protocol | 选择接入的应用协议(MQTT/CoAP/TCP/UDP 等) |
CMS Server Address | CMS 中台服务网络地址,支持 IP 与域名两种方式 |
CMS Server Port | CMS 中台服务端口 |
Size of send buffer | 最大发送缓存深度(按实际配置) |
Size of receive buffer | 接收缓存深度 |
Enable/disable CMS heartbeat | 是否使能心跳保持 |
CMS heartbeat interval(s) | 心跳保持间隔时间(秒) |
关键理解:应用协议(MQTT/TCP/UDP)选择后,对应头文件(
cms_con_mqtt.h/cms_con_tcp.h/cms_con_udp.h)才生效,使用示例中通过CMS_CONNECT_MQTT/CMS_CONNECT_TCP/CMS_CONNECT_UDP宏条件编译(见第五节示例)。
三、MQTT 协议接入
3.1 数据结构
完整源码(source/include/cms_con_mqtt.h):
typedef enum
{
mqtt_qos0,
mqtt_qos1,
mqtt_sub_fail = 0x80
} cms_mqtt_qos; //不支持QoS2
typedef struct
{
cms_mqtt_qos qos;
void *payload;
size_t payloadlen;
} cms_mqtt_message;
typedef void (*cms_mqtt_message_handler)(void *handle, const char *topic_name, void *payload, size_t payload_len);
typedef struct
{
uint16_t timeout_ms; /* 消息超时时间 */
size_t sendbuf_size; /* mqtt实例发送区缓存大小 */
size_t recvbuf_size; /* mqtt实例接收区缓存大小 */
} cms_mqtt_param;
typedef struct
{
char *client_id; /* 客户端ID */
uint16_t keep_alive_s; /* 保活时间,单位秒,0为不保活 */
char *username; /* 用户名 */
char *password; /* 密码 */
} cms_mqtt_connect_param;结构体详解:
| 结构体 | 成员 | 说明 |
|---|---|---|
cms_mqtt_qos | mqtt_qos0/mqtt_qos1/mqtt_sub_fail(0x80) | QoS 等级,仅支持 QoS0/QoS1,不支持 QoS2 |
cms_mqtt_message | qos/payload/payloadlen | 发布的消息体(QoS、内容、长度) |
cms_mqtt_message_handler | — | 消息回调函数类型 (handle, topic_name, payload, payload_len) |
cms_mqtt_param | timeout_ms/sendbuf_size/recvbuf_size | 初始化参数:消息超时、发送/接收缓存大小 |
cms_mqtt_connect_param | client_id/keep_alive_s/username/password | 连接参数:客户端 ID、保活秒数、用户名、密码 |
3.2 接口总览
| 接口 | 说明 |
|---|---|
cms_mqtt_init | 初始化 mqtt 实例 |
cms_mqtt_deinit | 去初始化 mqtt 实例 |
cms_mqtt_connect | 连接到服务器 |
cms_mqtt_disconnect | 断开连接 |
cms_mqtt_subscribe | 消息订阅 |
cms_mqtt_unsubscribe | 消息去订阅 |
cms_mqtt_publish | 消息发布 |
cms_mqtt_ping_request | 发布 ping 消息 |
cms_mqtt_get_state | 查询 mqtt 连接当前状态 |
3.3 核心 API 详解
(1)cms_mqtt_init —— 初始化 mqtt 实例
void *cms_mqtt_init(uint16_t scode, const cms_mqtt_param *param);| 参数 | 说明 |
|---|---|
scode | 服务编码,由用户向 CMS 服务申请 |
param | 初始化参数结构体指针(超时时间、发送/接收缓存大小) |
| 返回 | NULL 初始化失败;非 NULL 为 mqtt 实例操作指针 |
分步详解:
第一步:准备参数(无条件执行)
cms_mqtt_param param = {5000, 1000, 1000}; /* timeout_ms=5000, sendbuf=1000, recvbuf=1000 */第二步:调用初始化接口(无条件执行)
void *handle = cms_mqtt_init(scode, ¶m);scode 需向 CMS 服务申请;handle 为后续所有操作的操作指针(NULL 表示失败)。
(2)cms_mqtt_connect —— 连接到服务器
int cms_mqtt_connect(void *handle, cms_mqtt_connect_param *param);| 参数 | 说明 |
|---|---|
handle | mqtt 实例操作指针 |
param | 连接参数(client_id、保活周期、用户名、密码) |
| 返回 | 0 成功;非 0 失败(返回错误码) |
分步详解:
第一步:判断已连接(条件 cms_mqtt_get_state == cms_con_state_connect)
if (cms_mqtt_get_state(handle) == cms_con_state_connect)
return;已连接则跳过,避免重复连接。
第二步:填写连接参数并连接(无条件执行)
cms_mqtt_connect_param param;
memset(¶m, 0, sizeof(cms_mqtt_connect_param));
param.client_id = "12345678";
param.username = "oneos";
param.password = "123456";
param.keep_alive_s = 300;
int ret = cms_mqtt_connect(handle, ¶m);client_id/username/password 由 CMS 平台分配;keep_alive_s=0 表示不保活。
(3)cms_mqtt_subscribe —— 消息订阅
int cms_mqtt_subscribe(void *handle,
const char *topic_filter,
cms_mqtt_qos qos,
cms_mqtt_message_handler message_handler);| 参数 | 说明 |
|---|---|
handle | mqtt 实例操作指针 |
topic_filter | 消息主题 |
qos | QoS 等级(mqtt_qos0/mqtt_qos1) |
message_handler | 消息处理回调函数 |
| 返回 | 0 订阅成功;非 0 失败 |
(4)cms_mqtt_publish —— 消息发布
int cms_mqtt_publish(void *handle, const char *topic, cms_mqtt_message *message);| 参数 | 说明 |
|---|---|
handle | mqtt 实例操作指针 |
topic | 消息主题 |
message | 消息结构体指针(QoS、消息内容、消息长度) |
| 返回 | 0 发布成功;非 0 失败 |
(5)cms_mqtt_ping_request —— 心跳
int cms_mqtt_ping_request(void *handle);设备端向服务端发送心跳请求。返回 0 成功、非 0 失败。
(6)cms_mqtt_get_state —— 查询连接状态
int cms_mqtt_get_state(void *handle);返回 0 表示未连接、1 表示已连接(对应 cms_con_state_disconnect/cms_con_state_connect)。
(7)cms_mqtt_disconnect / cms_mqtt_deinit —— 断开/释放
void cms_mqtt_disconnect(void *handle);
void cms_mqtt_deinit(void *handle);disconnect 断开服务连接;deinit 去初始化并释放资源。
3.4 完整 MQTT 使用示例(官方 Atest 示例精简)
#include "oneos_config.h"
#if defined(CMS_CONNECT_MQTT)
#include "cms_con_mqtt.h"
#include <os_kernel.h>
#include <oneos_config.h>
#include <string.h>
static void *mqtt_handle = NULL;
static int scode = 300;
static char topic_str[] = "svr/300/req";
static void message_handler(void *handle, const char *topic_name, void *payload, size_t payload_len)
{
os_kprintf("topic:%s\r\n", topic_name);
os_kprintf("payload:%s\r\n", (char *)payload);
}
void cms_mqtt_example(void)
{
cms_mqtt_param param = {5000, 1000, 1000}; /* timeout_ms=5000, sendbuf=1000, recvbuf=1000 */
cms_mqtt_connect_param connect_param;
cms_mqtt_message message;
int ret;
/* 1. 初始化 mqtt 实例 */
mqtt_handle = cms_mqtt_init(scode, ¶m);
if (mqtt_handle == NULL) {
os_kprintf("mqtt init failed\r\n");
return;
}
/* 2. 连接 CMS 中台 */
memset(&connect_param, 0, sizeof(cms_mqtt_connect_param));
connect_param.client_id = "12345678";
connect_param.username = "oneos";
connect_param.password = "123456";
connect_param.keep_alive_s = 300;
ret = cms_mqtt_connect(mqtt_handle, &connect_param);
if (ret != 0) {
os_kprintf("mqtt connect failed: %d\r\n", ret);
cms_mqtt_deinit(mqtt_handle);
return;
}
/* 3. 订阅主题 */
ret = cms_mqtt_subscribe(mqtt_handle, topic_str, mqtt_qos1, message_handler);
if (ret != 0) {
os_kprintf("mqtt subscribe failed: %d\r\n", ret);
}
/* 4. 发布消息 */
message.qos = mqtt_qos1;
message.payload = (void *)"hello cms";
message.payloadlen = strlen("hello cms");
ret = cms_mqtt_publish(mqtt_handle, topic_str, &message);
if (ret != 0) {
os_kprintf("mqtt publish failed: %d\r\n", ret);
}
/* 5. 心跳 */
cms_mqtt_ping_request(mqtt_handle);
/* 6. 断开并释放 */
if (cms_mqtt_get_state(mqtt_handle) == 1)
cms_mqtt_disconnect(mqtt_handle);
cms_mqtt_deinit(mqtt_handle);
}
#endif分步详解:
- 初始化:
cms_mqtt_init(scode, ¶m),scode向 CMS 申请;handle==NULL判失败; - 连接:填
cms_mqtt_connect_param(client_id/username/password/keep_alive_s)后cms_mqtt_connect; - 订阅:
cms_mqtt_subscribe(handle, topic, qos, handler),消息到达回调message_handler; - 发布:填
cms_mqtt_message(qos/payload/payloadlen)后cms_mqtt_publish; - 心跳:
cms_mqtt_ping_request维持连接(若keep_alive_s>0,库也会自动心跳); - 释放:
cms_mqtt_get_state判连接后cms_mqtt_disconnect+cms_mqtt_deinit。
四、TCP / UDP 协议接入
4.1 TCP 接入接口
相关接口(source/include/cms_con_tcp.h):
| 接口 | 说明 |
|---|---|
cms_tcp_init(scode, recvbuf_size) | 初始化 tcp 连接实例 |
cms_tcp_deinit(handle) | 去初始化 tcp 连接实例 |
cms_tcp_connect(handle) | 连接到 CMS 中台 |
cms_tcp_disconnect(handle) | 断开连接 |
cms_tcp_send(handle, buf, len) | 发送数据 |
cms_tcp_recv(handle, buf, max_length, timeout_ms) | 接收数据 |
cms_tcp_get_state(handle) | 查询连接状态 |
核心 API 原型:
void *cms_tcp_init(uint16_t scode, uint32_t recvbuf_size);
void cms_tcp_deinit(void *handle);
int cms_tcp_connect(void *handle);
void cms_tcp_disconnect(void *handle);
int cms_tcp_send(void *handle, const uint8_t *buf, size_t len);
int cms_tcp_recv(void *handle, uint8_t *buf, size_t max_length, int timeout_ms);
int cms_tcp_get_state(void *handle);关键理解:
cms_tcp_send返回< 0失败、其他为已发送长度;cms_tcp_recv的timeout_ms为超时(0不等待),返回< 0失败、其他为接收长度;cms_tcp_get_state返回0未连接、1已连接;- TCP 收包通常需独立接收任务循环调用
cms_tcp_recv(官方示例用os_task_create建tcp_recv_test任务)。
4.2 UDP 接入接口
相关接口(source/include/cms_con_udp.h):
| 接口 | 说明 |
|---|---|
cms_udp_init(scode, recvbuf_size) | 初始化 udp 连接实例 |
cms_udp_deinit(handle) | 去初始化 udp 连接实例 |
cms_udp_send(handle, buf, len) | 发送数据 |
cms_udp_recv(handle, buf, max_length, timeout_ms) | 接收数据 |
cms_udp_get_state(handle) | 查询当前状态 |
核心 API 原型:
void *cms_udp_init(uint16_t scode, uint32_t recvbuf_size);
void cms_udp_deinit(void *handle);
int cms_udp_send(void *handle, const uint8_t *buf, size_t len);
int cms_udp_recv(void *handle, uint8_t *buf, size_t max_length, int timeout_ms);
int cms_udp_get_state(void *handle);关键理解:UDP 与 TCP 的 send/recv 用法一致;cms_udp_get_state 当心跳超时或认证失败时,显示设备未连接到 CMS 中台(返回 0)。
4.3 TCP/UDP 典型用法
#include "oneos_config.h"
#if defined(CMS_CONNECT_TCP)
#include "cms_con_tcp.h"
#include <os_kernel.h>
#include <os_task.h>
static void *tcp_handle = NULL;
static int scode = 301;
void tcp_recv_process(void *param)
{
uint8_t buf[1024];
int len;
while (cms_tcp_get_state(tcp_handle) == 1) { /* 已连接 */
len = cms_tcp_recv(tcp_handle, buf, sizeof(buf), 100); /* 阻塞 100ms 接收 */
if (len > 0) {
os_kprintf("recv %d bytes\r\n", len);
}
}
}
void cms_tcp_example(void)
{
os_task_t *recv_task;
int ret;
/* 1. 初始化 tcp 实例 */
tcp_handle = cms_tcp_init(scode, 1024);
if (tcp_handle == NULL) {
os_kprintf("tcp init failed\r\n");
return;
}
/* 2. 连接 CMS 中台 */
ret = cms_tcp_connect(tcp_handle);
if (ret != 0) {
os_kprintf("tcp connect failed: %d\r\n", ret);
cms_tcp_deinit(tcp_handle);
return;
}
/* 3. 创建接收任务 */
recv_task = os_task_create("tcp_recv", tcp_recv_process, NULL, 1024, 22);
os_task_startup(recv_task);
/* 4. 发送数据 */
ret = cms_tcp_send(tcp_handle, (uint8_t *)"hello cms tcp", strlen("hello cms tcp"));
os_kprintf("tcp send ret=%d\r\n", ret);
os_task_tsleep(5000);
/* 5. 断开并释放 */
if (cms_tcp_get_state(tcp_handle) == 1)
cms_tcp_disconnect(tcp_handle);
cms_tcp_deinit(tcp_handle);
}
#endif📝 本节小结
核心要点回顾
- 定位:CMS Connect 承接端侧设备与 CMS 中台的连接任务,提供统一接入协议与消息分发;支持 MQTT/TCP/UDP,核心为预编译库。
- 线上管理组件:CMS Connect 不随内核默认打包,使用前需通过 OneOS Studio 组件管理远程下载到本地(工程
components/cms/),之后才可 Menuconfig 配置。 - 配置:Menuconfig
Components → CMS → CMS Connect,选应用协议(MQTT/TCP/UDP)、CMS Server Address/Port、send/recv buffer、心跳间隔。 - MQTT 接入:
cms_mqtt_init(scode, ¶m)→cms_mqtt_connect(handle, &connect_param)→cms_mqtt_subscribe/cms_mqtt_publish→cms_mqtt_ping_request→cms_mqtt_disconnect/cms_mqtt_deinit。 - 真实 API:
cms_mqtt_*/cms_tcp_*/cms_udp_*;头文件cms_con_mqtt.h/cms_con_tcp.h/cms_con_udp.h,随应用协议宏(CMS_CONNECT_MQTT/TCP/UDP)条件编译。 - QoS 限制:MQTT 仅支持 QoS0/QoS1,不支持 QoS2(
cms_mqtt_qos无 qos2 值)。 - 连接状态:
cms_mqtt/tcp/udp_get_state返回0未连接、1已连接(对应cms_con_state_disconnect/connect);UDP 在心跳超时或认证失败时显示未连接。 - 依赖:链路认证依赖 CMS ID 组件;基础网络依赖 OneOS 网络能力。
关键易错点速查
| 易错点 | 正确做法 |
|---|---|
用 cms_connect_init/cms_connect_start/cms_property_report/cms_connect_yield 等 API | 这些 API 不存在 → 用真实的 cms_mqtt_init/cms_mqtt_connect/cms_mqtt_publish 等 |
用 cms_device_info_t/cms_connect_config_t 结构体 | 不存在 → 用 cms_mqtt_param/cms_mqtt_connect_param |
找不到 Components → CMS 配置项 | CMS 组件未下载 → 先按官方文档远程下载到本地 |
| 忘记在 Menuconfig 选应用协议 | 头文件不生效 → 在 Application Protocol 选 MQTT/TCP/UDP |
| MQTT 用 QoS2 | 不支持 → 只用 mqtt_qos0/mqtt_qos1 |
cms_mqtt_publish 直接传裸字符串 | 第三参是 cms_mqtt_message* → 先填 message.qos/payload/payloadlen 再传 &message |
cms_mqtt_connect 返回判 NULL | 它返回 int(0 成功)→ 判 ret != 0;NULL 只在 cms_mqtt_init |
scode 随便填 | scode 是服务编码,需向 CMS 服务申请 → 正确申请 |
| TCP 收包无接收任务 | cms_tcp_recv 阻塞式 → 建独立任务循环调用 |
忘记 cms_mqtt_deinit | 资源泄漏 → 结束后 cms_mqtt_disconnect + cms_mqtt_deinit |
下一步
- 7.5.2 CMS ID 组件:CMS Connect 已完成设备连接,下一节学习 CMS ID 组件,掌握设备身份认证(互信与鉴权),实现"连接 + 认证"完整的 CMS 上云链路。