本文介绍如何使用LinkSDK通过国密算法接入MQTT云网关。
背景信息
国密算法是国家密码管理局制定、认定和颁布的一系列国产密码算法,又称商用密码(能够实现商用密码算法的加密、解密和认证等功能的技术),保障金融,医疗等领域的信息传输安全。物联网设备可以使用国密算法接入物联网平台的MQTT云网关,通过MQTT云网关接入阿里云物联网平台。
前提条件
已购买尊享型实例。
已安装国密开源库Tongsuo。
已新建MQTT云网关并完成国密证书部署。
已获取设备认证信息(Username、Password、Clientid)。
操作步骤
如下功能时序图,以设备的应用程序demos/device_gmssl_demo.c为例,介绍国密接入的使用流程。
步骤1:设备初始化
添加头文件
#include "aiot_device_api.h"
创建设备句柄
void *device_client = aiot_device_create(product_key, device_name); if (device_client == NULL) { printf("device_client failed\n"); return -1; }
步骤2:配置事件回调函数
编写消息回调函数。
该回调函数会在设备接收到任意消息时执行,用于消息的监听。该回调函数原型为aiot_device_msg_result_callback_t。
/* 设备的消息回调示例,默认只做打印处理 */ void demo_msg_callback(void *device, const aiot_msg_t *message, void *userdata) { printf("[message] <<, topic: %s\n", message->topic); printf("[message] <<, payload: %.*s\n", message->payload_lenth, message->payload); }
编写状态回调函数。
该回调函数会在设备连接状态变化时执行,用于监听设备的状态。该回调函数原型为aiot_device_status_callback_t。
void demo_status_callback(void *device, const aiot_device_status_t *status, void *userdata) { switch (status->type) { /* SDK因为用户调用了aiot_mqtt_connect()接口, 与mqtt服务器建立连接已成功 */ case AIOT_DEVICE_STATUS_CONNECT: { printf("AIOT_DEVICE_STATUS_CONNECT\n"); /* TODO: 处理SDK建连成功, 不可以在这里调用耗时较长的阻塞函数 */ } break; /* SDK因为网络状况被动断连后, 自动发起重连已成功 */ case AIOT_DEVICE_STATUS_RECONNECT: { printf("AIOT_DEVICE_STATUS_CONNECT\n"); /* TODO: 处理SDK重连成功, 不可以在这里调用耗时较长的阻塞函数 */ } break; /* SDK因为网络的状况而被动断开了连接, network是底层读写失败, heartbeat是没有按预期得到服务端心跳应答 */ case AIOT_DEVICE_STATUS_DISCONNECT: { printf("AIOT_DEVICE_STATUS_CONNECT: %d\n", status->error_code); /* TODO: 处理SDK被动断连, 不可以在这里调用耗时较长的阻塞函数 */ } break; } }
编写消息发送结果回调函数。
该函数会在设备主动发送QoS 1消息,或执行订阅、取消订阅、云端有返回时执行。该回调函数的原型为aiot_device_msg_result_callback_t。
/* 主动发送的消息,或者订阅/取消订阅消息有结果后会回调该消息 */ void demo_result_callback(void *device, const aiot_device_msg_result_t *result, void *userdata) { switch(result->type) { case AIOT_DEVICE_SEND_MESSAGE_RESULT: { printf("send message result, message id: %d, code -0x%04X\n", result->message_id, -result->code); } break; case AIOT_DEVICE_REGISTER_TOPIC_RESULT:{ printf("register, message id: %d, code code -0x%04X\n", result->message_id, -result->code); } break; default: break; } }
设置事件回调函数。
上述定义了回调函数,执行设置动作才能生效,设置事件回调函数接口说明,请参见aiot_device_set_event_callback,该接口可设置三种事件的监听,如果不需要监听事件,可将对应参数设置为NULL。
/* 设置设备消息回调及状态变化回调 */ aiot_device_set_event_callback(device_client, demo_msg_callback, demo_status_callback, demo_result_callback, NULL);
步骤3:配置连接参数
配置连接参数,一般使用默认设置,更多连接参数信息,请参考aiot_linkconfig_api.h。
设置云网关接入域名和端口:aiot_linkconfig_host(config, host, port);
设置TLS采用国密算法:aiot_linkconfig_tls_gm(config, 1);
设置国密根证书:aiot_linkconfig_root_cert(config, demo_sm2_cert, strlen(demo_sm2_cert));
设置MQTT认证参数:aiot_linkconfig_mqtt_auth(config, username, password, cilentid);
/* 连接配置参数初始化 */
aiot_linkconfig_t* config = aiot_linkconfig_init(MQTT_PROTOCOL);
/* 设置连接参数:服务器的host、port */
aiot_linkconfig_host(config, host, port);
/* 设置连接参数:TLS采用国密 */
aiot_linkconfig_tls_gm(config, 1);
/* 设置连接参数:根证书 */
aiot_linkconfig_root_cert(config, demo_sm2_cert, strlen(demo_sm2_cert));
/* 设置连接参数:MQTT认证参数*/
aiot_linkconfig_mqtt_auth(config, username, password, cilentid);
/* 将连接参数上下文传递给设备 */
aiot_device_set_linkconfig(device_client, config);
步骤4:设备建连
如下示例的连接为阻塞式的接口,SDK也支持异步建连,异步建连接口使用具体操作,请参考aiot_device_connect_async。
/* 设备建连 */
res = aiot_device_connect(device_client);
if (res < STATE_SUCCESS) {
/* 尝试建立连接失败, 销毁MQTT实例, 回收资源 */
aiot_linkconfig_deinit(&config);
aiot_device_delete(&device_client);
printf("aiot_device_connect failed: -0x%04X\n\r\n", -res);
return -1;
}
设备建连成功后,如果网络出现断连,SDK会自动进行设备重连,无需再调用aiot_device_connect
接口。
步骤5:订阅自定义消息
定义消息的回调函数。
回调函数原型为aiot_device_msg_result_callback_t。
/* 自定义topic下行消息处理示例, 示例只做打印处理 */ void demo_user_topic_callback(void *device, const aiot_msg_t *message, void *userdata) { printf("[thing_message] <<, topic: %s\n", message->topic); printf("[thing_message] <<, payload: %.*s\n", message->payload_lenth, message->payload); }
向物联网平台发起消息订阅,并注册消息回调。
注册自定义消息回调的接口aiot_device_register_topic_filter,该接口有两个功能:
(可选)向云平台发起消息订阅。
向SDK注册接收到消息的回调。
/* 订阅消息,设置该消息回调函数 */ char sub_topic[128] = { 0 }; snprintf(sub_topic, sizeof(sub_topic), "/%s/%s/user/get", product_key, device_name); int32_t msg_id = aiot_device_register_topic_filter(device_client, sub_topic, demo_user_topic_callback, 1, NULL); if(msg_id >= 0){ printf("aiot_device_register_topic_filter topic %s msg_id %d\r\n", sub_topic, msg_id); }
如果需要取消已订阅的Topic,可通过接口aiot_device_deregister_topic_filter取消订阅。
步骤6:发送自定义消息
创建消息。
如下示例使用Topic和Payload创建消息。更多消息配置,请参考aiot_message_api.h。
char pub_topic[128] = { 0 }; snprintf(pub_topic, sizeof(pub_topic), "/%s/%s/user/update", product_key, device_name); char *pub_payload = "{\"id\":\"1\",\"version\":\"1.0\",\"params\":{\"LightSwitch\":0}}"; aiot_msg_t *pub_message = aiot_msg_create_raw(pub_topic, (uint8_t *)pub_payload, strlen(pub_payload));
发送消息。
aiot_device_send_message(device_client, pub_message);
删除消息。
aiot_msg_delete(pub_message);
步骤7:接收自定义消息
设备订阅消息后,用户可通过云端SDK给设备发消息;调试阶段,也可通过OpenApi给设备发消息。通过OpenApi给设备发自定义消息步骤:
登录控制台,打开Pub接口链接。
输入必选参数:ProductKey、TopicFullName、MessageContent,若为企业实例,还需输入IotInstanceId。
发起调用:单机发起调用,若执行成功,即可看到设备端打印接收的消息。
步骤8:设备断开连接
设备和物联网平台断开连接。
res = aiot_device_disconnect(device_client);
if (res < STATE_SUCCESS) {
printf("aiot_mqtt_disconnect failed: -0x%04X\n", -res);
return -1;
}
步骤9:删除设备句柄。
程序退出时,SDK执行资源回收。
/* 销毁设备实例, 一般不会运行到这里 */
aiot_device_delete(&device_client);
aiot_linkconfig_deinit(&config);
编译运行
Makefile编译
编译: make -j GMSSL=ON 运行: ./output/bin/device_gmssl_demo
Cmake编译
编译: mkdir build && cd build cmake .. -DGMSSL=ON make -j 运行: ./output/bin/device_gmssl_demo