全部产品
弹性计算 会员服务 网络 安全 移动云 数加·大数据分析及展现 数加·大数据应用 管理与监控 云通信 阿里云办公 培训与认证 更多
存储与CDN 数据库 域名与网站(万网) 应用服务 数加·人工智能 数加·大数据基础服务 互联网中间件 视频服务 开发者工具 解决方案 物联网 智能硬件
阿里云物联网套件

MQTT-TCP连接通信

更新时间:2018-04-02 21:19:41

本篇文档主要讲解基于TCP的MQTT连接,并且提供了两种设备认证模式。

  1. MQTT客户端域名直连(资源受限设备推荐)
  2. 先HTTPS发送授权后再连接MQTT(一些特殊增值服务,比如设备级别的引流)

需要注意的事项

在进行MQTT CONNECT协议设置的时候,

  1. Connect指令中的KeepAlive有效范围[60秒,300秒],否则会拒绝连接。
  2. 如果同一个设备多个连接可能会导致客户端互相上下线,MQTT默认开源SDK会自动重连,您可以通过日志服务看到设备行为。

一 使用MQTT域名连接模式

您可以使用我们的DEMO进行快速移植,如果不用我们DEMO,完全使用开源MQTT包自主接入(若使用第三方代码, 阿里云不提供技术支持),可以参考以下流程:

  1. 如果使用TLS,需要 下载根证书
  2. 使用MQTT客户端连接服务器,如果您自主接入可以使用开源MQTT客户端参考,如果您对MQTT不了解,可以参考 http://mqtt.org 相关文档。(若使用第三方代码, 阿里云不提供技术支持)

  3. MQTT 连接使用说明

    1. 连接域名:
    2. 华东2节点:${productKey}.iot-as-mqtt.cn-shanghai.aliyuncs.com:1883
    3. 美西节点:${productKey}.iot-as-mqtt.us-west-1.aliyuncs.com:1883
    4. 新加坡节点:${productKey}.iot-as-mqtt.ap-southeast-1.aliyuncs.com:1883
    5. ${productKey}请替换为您的产品keymqttConnect报文参数如下:
    6. mqttClientId: clientId+"|securemode=3,signmethod=hmacsha1,timestamp=132323232|"
    7. mqttUsername: deviceName+"&"+productKey
    8. mqttPassword: sign_hmac(deviceSecret,content)sign签名需要把以下参数按字典序排序后,再根据signmethod加签。
    9. content=提交给服务器的参数(productKey,deviceName,timestamp,clientId), 按照字母顺序排序, 然后将参数值依次拼接
    10. 其中clientId是客户端自表示id,建议macsn64字符内;timestamp当前时间毫秒值,仅做混淆,可以不传递;mqttClientId格式中||内为扩展参数,signmethod代表签名算法类型,securemode代表目前安全模式,可选值有2 TLS直连模式)、3TCP直连模式),参考以下示例:
  4. 举例:

    1. 如果clientId = 12345deviceName = device productKey = pk timestamp = 789signmethod=hmacsha1deviceSecret=secret,那么使用tcp方式提交给mqtt参数分别如下:
    2. mqttclientId=12345|securemode=3,signmethod=hmacsha1,timestamp=789|
    3. username=device&pk
    4. password=hmacsha1("secret","clientId12345deviceNamedeviceproductKeypktimestamp789").toHexString(); //最后是二进制转16制字符串,大小写不敏感。 这个例子结果为 FAFD82A3D602B37FB0FA8B7892F24A477F851A14
    5. 注意上面3个参数分别是mqtt Connect登录报文的mqttClientId,mqttUsername,mqttPasswrod
  5. 如果您使用我们的DEMO,请将make.settings 里面FEATURE_MQTT_DIRECT的设置修改成y
    1. FEATURE_MQTT_DIRECT = y
    在域名直连的情况才可以支持TCP的MQTT连接,请将make.settings 里面FEATURE_MQTT_DIRECT的设置修改成y,同时将FEATURE_MQTT_DIRECT_NOTLS位置成y。
    1. FEATURE_MQTT_DIRECT = y
    2. FEATURE_MQTT_DIRECT_NOTLS = y
  6. 在SDK中,直接调用IOT_MQTT_Construct就可以完成与云端建立连接的过程。
    1. pclient = IOT_MQTT_Construct(&mqtt_params);
    2. if (NULL == pclient) {
    3. EXAMPLE_TRACE("MQTT construct failed");
    4. rc = -1;
    5. goto do_exit;
    6. }
    1. /**
    2. * @brief Construct the MQTT client
    3. * This function initialize the data structures, establish MQTT connection.
    4. *
    5. * @param [in] pInitParams: specify the MQTT client parameter.
    6. *
    7. * @retval NULL : Construct failed.
    8. * @retval NOT_NULL : The handle of MQTT client.
    9. * @see None.
    10. */
    11. void *IOT_MQTT_Construct(iotx_mqtt_param_t *pInitParams);

二 使用HTTPS认证再连接模式

设备认证流程:

  • 设备认证走https,认证域名为https://iot-auth.cn-shanghai.aliyuncs.com/auth/devicename
  • 认证请求参数信息:

    productKey必选productKey,从iot套件控制台获取
    deviceName必选deviceName,从iot套件控制台获取
    sign必选签名,hmacmd5(deviceSecret,content), content = 将所有提交给服务器的参数(version,sign,resources,signmethod除外), 按照字母顺序排序, 然后将参数值依次拼接,无拼接符号
    signmethod 可选 算法类型,hmacmd5或hmacsha1
    clientId 必选 客户端自表示Id,64字符内
    timestamp 可选 时间戳,目前时间戳并不做窗口校验,只起到加盐的作用
    resources 可选 希望获取的资源描述,如mqtt。 多个资源名称用逗号隔开.

    • 响应参数

      iotId必选服务器颁发的一个连接标记,用于赋值给MQTT connect报文中的username
      iotToken必选token有效期为7天,赋值给MQTT connect报文中的password
      [resources]可选资源信息,扩展信息比如mqtt服务器地址、CA证书信息等
    • x-www-form-urlencoded请求举例

    1. POST /auth/devicename HTTP/1.1
    2. Host: iot-auth.cn-shanghai.aliyuncs.com
    3. Content-Type: application/x-www-form-urlencoded
    4. Content-Length: 123
    5. productKey=123&sign=123&timestamp=123&version=default&clientId=123&resouces=mqtt&deviceName=test
    6. sign = hmac_md5(deviceSecret, clientId123deviceNametestproductKey123timestamp123)
    • 请求响应
    1. HTTP/1.1 200 OK
    2. Server: Tengine
    3. Date: Wed, 29 Mar 2017 13:08:36 GMT
    4. Content-Type: application/json;charset=utf-8
    5. Connection: close
    6. {
    7. "code" : 200,
    8. "data" : {
    9. "iotId" : "42Ze0mk3556498a1AlTP",
    10. "iotToken" : "0d7fdeb9dc1f4344a2cc0d45edcb0bcb",
    11. "resources" : {
    12. "mqtt" : {
    13. "host" : "xxx.iot-as-mqtt.cn-shanghai.aliyuncs.com",
    14. "port" : 1883
    15. }
    16. }
    17. },
    18. "message" : "success"
    19. }

    连接MQTT:

    • 下载IoT根证书 root.crt, 建议使用TLS1.2。
    • 指定阿里云MQTT服务器地址(认证返回的MQTT地址和端口)进行通信。
    • 采用TLS建立连接,客户端验证服务器通过CA证书完成,而服务器验证客户端通过MQTT协议体内connect报文中的username=iotId,password=iotToken,clientId=自定义设备标记(建议MAC或SN);如果iotId或iotToken非法,mqtt connect失败,收到的connect ack为3。

    错误码

    1. 401: request auth error,在这个场景里面通常在签名匹配不通过时返回
    2. 460: param error,参数异常
    3. 500: unknow error,一些未知异常
    4. 5001: meta device not found,指定的设备不存在
    5. 6200: auth type mismatch,未授权认证类型错误
    • 如果您使用我们的DEMO,请将make.settings 里面FEATURE_MQTT_DIRECT的设置修改成n 再调用IOT_MQTT_Construct就可以完成认证再连接的整个过程。
      1. FEATURE_MQTT_DIRECT = n
    • HTTPS认证的过程具体实现在:\src\system\iotkit-system\src\guider.c 中 iotx_guider_authenticate。

    三、SDK API介绍

    1. IOT_MQTT_Construct 与云端建立MQTT连接

    • mqtt-example 程序发送一次消息后会自动退出,可以尝试以下任意一种方式实现长期在线。
    • 1.执行mqtt-example时使用命令行./mqtt-example loop,设备会保持长期在线;
    • 2.修改demo代码,example 的代码在最后会调用IOT_MQTT_Destroy,设备最后会变成离线状态。如果希望设备一直处于上线状态,请去掉IOT_MQTT_Unregister 和IOT_MQTT_Destroy的部分,然后使用while 保持长连接状态。
      1. while(1)
      2. {
      3. IOT_MQTT_Yield(pclient, 200);
      4. HAL_SleepMs(100);
      5. }
      1. /**
      2. * @brief Construct the MQTT client
      3. * This function initialize the data structures, establish MQTT connection.
      4. *
      5. * @param [in] pInitParams: specify the MQTT client parameter.
      6. *
      7. * @retval NULL : Construct failed.
      8. * @retval NOT_NULL : The handle of MQTT client.
      9. * @see None.
      10. */
      11. void *IOT_MQTT_Construct(iotx_mqtt_param_t *pInitParams);

      IOT_MQTT_Subscribe 同云端订阅某个topic。

    • 请保留topic_filter的memory,这样callback topic_handle_func才能准确送达。

      1. /* Subscribe the specific topic */
      2. rc = IOT_MQTT_Subscribe(pclient, TOPIC_DATA, IOTX_MQTT_QOS1, _demo_message_arrive, NULL);
      3. if (rc < 0) {
      4. IOT_MQTT_Destroy(&pclient);
      5. EXAMPLE_TRACE("IOT_MQTT_Subscribe() failed, rc = %d", rc);
      6. rc = -1;
      7. goto do_exit;
      8. }
      1. /**
      2. * @brief Subscribe MQTT topic.
      3. *
      4. * @param [in] handle: specify the MQTT client.
      5. * @param [in] topic_filter: specify the topic filter.
      6. * @param [in] qos: specify the MQTT Requested QoS.
      7. * @param [in] topic_handle_func: specify the topic handle callback-function.
      8. * @param [in] pcontext: specify context. When call 'topic_handle_func', it will be passed back.
      9. *
      10. * @retval -1 : Subscribe failed.
      11. * @retval >=0 : Subscribe successful.
      12. The value is a unique ID of this request.
      13. The ID will be passed back when callback 'iotx_mqtt_param_t:handle_event'.
      14. * @see None.
      15. */
      16. int IOT_MQTT_Subscribe(void *handle,
      17. const char *topic_filter,
      18. iotx_mqtt_qos_t qos,
      19. iotx_mqtt_event_handle_func_fpt topic_handle_func,
      20. void *pcontext);

      3. IOT_MQTT_Publish 发布信息到云端

      1. /* Initialize topic information */
      2. memset(&topic_msg, 0x0, sizeof(iotx_mqtt_topic_info_t));
      3. strcpy(msg_pub, "message: hello! start!");
      4. topic_msg.qos = IOTX_MQTT_QOS1;
      5. topic_msg.retain = 0;
      6. topic_msg.dup = 0;
      7. topic_msg.payload = (void *)msg_pub;
      8. topic_msg.payload_len = strlen(msg_pub);
      9. rc = IOT_MQTT_Publish(pclient, TOPIC_DATA, &topic_msg);
      10. EXAMPLE_TRACE("rc = IOT_MQTT_Publish() = %d", rc);
      1. /**
      2. * @brief Publish message to specific topic.
      3. *
      4. * @param [in] handle: specify the MQTT client.
      5. * @param [in] topic_name: specify the topic name.
      6. * @param [in] topic_msg: specify the topic message.
      7. *
      8. * @retval -1 : Publish failed.
      9. * @retval 0 : Publish successful, where QoS is 0.
      10. * @retval >0 : Publish successful, where QoS is >= 0.
      11. The value is a unique ID of this request.
      12. The ID will be passed back when callback 'iotx_mqtt_param_t:handle_event'.
      13. * @see None.
      14. */
      15. int IOT_MQTT_Publish(void *handle, const char *topic_name, iotx_mqtt_topic_info_pt topic_msg);

      4. IOT_MQTT_Unsubscribe 取消云端订阅

      1. IOT_MQTT_Unsubscribe(pclient, TOPIC_DATA);
      1. /**
      2. * @brief Unsubscribe MQTT topic.
      3. *
      4. * @param [in] handle: specify the MQTT client.
      5. * @param [in] topic_filter: specify the topic filter.
      6. *
      7. * @retval -1 : Unsubscribe failed.
      8. * @retval >=0 : Unsubscribe successful.
      9. The value is a unique ID of this request.
      10. The ID will be passed back when callback 'iotx_mqtt_param_t:handle_event'.
      11. * @see None.
      12. */
      13. int IOT_MQTT_Unsubscribe(void *handle, const char *topic_filter);

      5. IOT_MQTT_Yield 数据接收函数

    • 请在任何需要接收数据的地方调用这个API。如果系统允许,请起一个单独的线程,执行该接口。
      1. /* handle the MQTT packet received from TCP or SSL connection */
      2. IOT_MQTT_Yield(pclient, 200);
      1. /**
      2. * @brief Handle MQTT packet from remote server and process timeout request
      3. * which include the MQTT subscribe, unsubscribe, publish(QOS >= 1), reconnect, etc..
      4. *
      5. * @param [in] handle: specify the MQTT client.
      6. * @param [in] timeout_ms: specify the timeout in millisecond in this loop.
      7. *
      8. * @return status.
      9. * @see None.
      10. */
      11. int IOT_MQTT_Yield(void *handle, int timeout_ms);

      6.IOT_MQTT_Destroy 销毁 MQTT连接,释放内存

      1. IOT_MQTT_Destroy(&pclient);
      1. /**
      2. * @brief Deconstruct the MQTT client
      3. * This function disconnect MQTT connection and release the related resource.
      4. *
      5. * @param [in] phandle: pointer of handle, specify the MQTT client.
      6. *
      7. * @retval 0 : Deconstruct success.
      8. * @retval -1 : Deconstruct failed.
      9. * @see None.
      10. */
      11. int IOT_MQTT_Destroy(void **phandle);

      7.IOT_MQTT_CheckStateNormal 查看当前的连接状态

    • 该接口用于查询MQTT的连接状态。但是,该接口并不能立刻检测到设备断网,只会在有数据发送或是keepalive时才能侦测到disconnect。
      1. /**
      2. * @brief check whether MQTT connection is established or not.
      3. *
      4. * @param [in] handle: specify the MQTT client.
      5. *
      6. * @retval true : MQTT in normal state.
      7. * @retval false : MQTT in abnormal state.
      8. * @see None.
      9. */
      10. int IOT_MQTT_CheckStateNormal(void *handle);

    MQTT保活

    设备端会在keepalive_interval_ms时间间隔发送ping request,然后等待ping response。如果设备端在keepalive_interval_ms时间内无法收到ping response,或是在进行send以及recv时发生error,套件就认为此时网络断开而需要进行重连。

    1. IOT_MQTT_Construct里面可以设置keepalive_interval_ms的取值,套件通过这个取值来作为心跳间隔时间。
    2. keepalive_interval_ms的取值是60000~300000

    具体使用方式请参看\sample\mqtt\mqtt-example.c的示例。

本文导读目录