分类: 其他平台
2020-09-16 09:37:27
阿里云MQTT设备的一型一密免白名单动态注册手册:
阿里云C-SDK下载手册:
https://help.aliyun.com/document_detail/96623.html?spm=a2c4g.11186623.2.16.3d122cf0tXDTVE
下面使用C-SDK的4.X版本进行设备的一型一密免白名单动态注册
首先是操作流程:
1. 在阿里云的产品端打开动态注册功能
2. 下载C-SDK 4.X版本至Ubunutu然后解压,下载参考上面的阿里云C-SDK下载手册
3. 打开demos/dynregmq_basic_demo.c文件进行编辑
4. 编辑main函数中的host字符串变量为自己产品的host路径$(ProductKey).iot-as-mqtt.cn-shanghai.aliyuncs.com
5. 编辑main函数中的product_key字符串为自己的$(ProductKey)
6. 编辑main函数中的product_secret字符串为自己的$(ProductSecret)
7. 编辑main函数中的device_name字符串为自己想要注册的设备名称,可以使用设备的MAC或者ID号作为设备之间的个体区分,这里为默认的dynreg_device_01
8. 编辑main函数中的nowhitelist变量,设置为1用于选择使用免白名单功能
9. 执行make进行编译
10. 执行./output/dynregmq-basic-demo即可运行
显示结果如下:
[1600074451.922][LK-0313] MQTT user calls aiot_mqtt_connect api, connect
establish mbedtls connection with server(host='x.iot-as-mqtt.cn-shanghai.aliyuncs.com', port=[443])
success to establish tcp, fd=3
success to establish mbedtls connection, fd = 3(cost 44958 bytes in total, max used 47870 bytes)
[1600074452.288][LK-0313] MQTT connect success in 360 ms
[1600074452.288][LK-0309] pub: /ext/regnwl
xxxx
clientid: x|authType=connwl,securemode=-2,_ss=1,ext=3,_v=sdk-c-4.0.0|
username: x&x
password: x
执行完成后阿里云上的设备端则会生成dynreg_device_01这个设备,状态为未激活
设备的动态注册MQTT协议流程很简单:
1) 设备通过TLS加密发送CONNECT包(clientId部分携带动态注册信息,格式为$(DeviceName).$(ProductKey)|random=672753970,authType=regnwl,securemode=2,signmethod=hmacsha256|)给服务端建立会话
2) 设备接收CONNACK包确认会话的建立
3) 设备等待接收服务端发来的PUBLISH包(主题为"/ext/regnwl",负载部分携带clientId,productKey,deviceName,deviceToken)
下面是代码流程解析:
1.main
1) 通过aiot_sysdep_set_portfile函数设置SDK的底层接口操作句柄,这里设置为g_aiot_sysdep_portfile(包含网络,内存管理,互斥锁管理等接口)
2) 创建SDK的安全凭据, 用于建立TLS连接
3) 通过aiot_dynregmq_init函数创建1个dynregmq_handle_t实例并初始化默认参数
4) 配置会话的服务器地址,端口,productKey,productSecret,deviceName等信息至刚申请的dynregmq_handle_t结构体中,其中recv_handler配置为demo_dynregmq_recv_handler函数
5) 通过aiot_dynregmq_send_request函数发送CONNECT包进行动态设备的注册
6) 通过aiot_dynregmq_recv函数接收动态注册的PUBLISH回包
7) 通过aiot_dynregmq_deinit函数销毁申请的dynregmq_handle_t实例
2. aiot_dynregmq_send_request
1) 通过aiot_mqtt_init函数创建core_mqtt_handle_t实例并进行内部变量的初始化
2) 根据是否使用白名单选择clientId中authType的认证类型,这里使用免白名单模式则为connwl
3) 生成clientid, username和password用于之后的CONNECT包连接
4) 将之前创建的dynregmq_handle_t实例中配置的服务器地址,端口等信息拷贝至刚创建的core_mqtt_handle_t实例中,其中recv_handler配置为_dynregmq_recv_handler,userdata配置为刚之前创建的dynregmq_handle_t实例
5) 通过aiot_mqtt_connect函数进行会话的连接
3.aiot_mqtt_connect
1) 通过_core_mqtt_connect函数进行会话的连接
2) 如会话连接成功(接收到CONNACK包),则通过_core_mqtt_connect_event_notify函数进行CONNECT成功的事件通知,过程为先调用系统的event_handler回调函数进行处理,然后遍历process_data_list队列,将事件使用其handler回调函数进行处理
4._core_mqtt_connect
1) 通过core_sysdep_network_init函数句柄创建一个core_network_handle_t实例用于网络通信
2) 设置core_network_handle_t实例的socket类型为TCP_CLIENT,以及根据之前申请的core_mqtt_handle_t实例设置HOST地址,目标端口,连接超时时间,证书等配置
3) 通过core_sysdep_network_establish函数句柄进行网络的连接
4) 通过_core_mqtt_conn_pkt函数进行CONNECT包的构建
5) 通过_core_mqtt_write函数发送CONNECT包的内容
6) 通过_core_mqtt_read函数读取网络数据,这里读取的数据长度为CONNACK包的长度
7) 通过_core_mqtt_connack_handle函数检测收到的数据是否为CONNACK包
5. _core_mqtt_conn_pkt
1) 设置负载的长度为clientid,username,password 3个字符串的长度加上每个字符串2字节的长度标识(UTF-8格式)
2) 剩余长度为可变包头加负载的长度
3) 计算connect包的大小,其中剩余长度固定估算使用4字节
4) 设置固定包头的header字节为CONNECT包的配置
5) 通过_core_mqtt_remain_len_encode函数计算剩余长度并对剩余长度字段赋值
6) 设置MQTT协议版本信息
7) 设置MQTT协议版本号
8) 设置CONNECT Flag标识
9) 设置KeepAlive字段
10) 设置负载的clientId内容,2字节长度加字符串长度
11) 设置负载的username内容,2字节长度加字符串长度
12) 设置负载的password内容,2字节长度加字符串长度
6._core_mqtt_connack_handle
1) 检查header,剩余长度和flag的值是否与CONNACK包匹配,匹配则确认为CONNACK包,获取返回码
2) 根据CONNACK包的返回码返回CONNECT的结果
7.aiot_dynregmq_recv
1) 检测是否超过超时时间,超过则返回超时异常
2) 未超时则通过aiot_mqtt_recv函数接收MQTT包
8.aiot_mqtt_recv
1) 检查会话是否需要重新连接,如果需要则通过_core_mqtt_reconnect函数进行重连接
2) 通过_core_mqtt_read函数读取一个字节用于包的header类型解析
3) 通过_core_mqtt_read_remainlen函数读取剩余字节字段的数据
4) 根据剩余字节的信息通过_core_mqtt_read_remainbytes函数读取剩余字节
5) 获取header字节的packet type字段
6) 根据packet type类型,调用对应的处理,如果设备注册成功,则服务器返回PUBLISH包
7) 通过_core_mqtt_pub_handler函数处理接收到的PUBLISH包
9._core_mqtt_pub_handler
1) 设置包的类型为AIOT_MQTTRECV_PUB
2) 获取主题字符串
3) 检查QoS是否为QoS1,是则获取packetId
4) 获得负载数据
5) 如果QoS为QoS1则通过_core_mqtt_puback_send函数发送PUBACK包
6) 遍历sub_list订阅主题队列,匹配是否有主题和当前的PUBLISH包主题对应上,如果匹配上,则遍历主题节点的handle_list队列,将其节点复制一份挂载到handler_list_copy队列上
7) 遍历handler_list_copy队列,调用订阅主题的handler回调函数进行处理
8) 如果没有与订阅主题匹配上,则调用系统的默认回调函数recv_handler进行处理,这里为之前注册的_dynregmq_recv_handler
10._dynregmq_recv_handler
1) 判断传入的aiot_mqtt_recv_t包是否为AIOT_MQTTRECV_PUB类型
2) 是则判断其主题名称是否匹配我们使用的是免白名单注册字符串"/ext/regnwl"
3) 如果匹配则通过core_json_value函数对包的负载内容进行clientId和deviceToken的提取
4) 设置动态注册标志flag_completed为1标识成功完成设备的动态注册
5) 判断dynregmq_handle_t的recv_handler动态注册处理函数是否存在,之前配置了这个函数为demo_dynregmq_recv_handler
6) 存在则生成设备用于连接阿里云服务器的clientid,username和password存放于aiot_dynregmq_recv_t结构体中,并设置其type类型为AIOT_DYNREGMQRECV_DEVICEINFO_NWL表示免白名单动态注册,然后调用recv_handler,也就是demo_dynregmq_recv_handler
11.demo_dynregmq_recv_handler
1) 判断传入的aiot_dynregmq_recv_t结构体指针其type类型,这里为AIOT_DYNREGMQRECV_DEVICEINFO_NWL类型
2) 将传入的aiot_dynregmq_recv_t结构体的clientid,username,password拷贝至全局结构体变量demo_devinfo_nwl中进行长期保存