MQTT.fx是一款基于Eclipse Paho,使用Java语言编写的MQTT客户端工具,支持通过Topic订阅和发布消息。本文以MQTT.fx为例,介绍模拟设备以MQTT协议接入物联网平台的操作步骤。

前提条件

已在物联网平台控制台创建产品和设备,并获取设备证书信息(ProductKey、DeviceName和DeviceSerect)。具体操作,请参见创建产品单个创建设备批量创建设备

使用MQTT.fx接入

  1. 访问MQTT.fx官网,下载并安装MQTT.fx Version 1.7.1版本软件。
  2. 打开MQTT.fx软件,单击菜单栏中的Extras,选择Edit Connection Profiles
    编辑mqtt的基本信息
  3. Edit Connection Profiles页面,完成以下参数的设置。
    1. 设置基本信息。
      编辑mqtt.fx的基本信息
      参数 说明 示例
      Profile Name 输入您的自定义名称。 iot connection
      Profile Type 选择为MQTT Broker
      MQTT Broker Profile Settings
      Broker Address 接入域名。

      接入域名格式:${YourProductKey}.iot-as-mqtt.${YourRegionId}.aliyuncs.com

      • ${YourProductKey}:请替换为设备所属产品的ProductKey。可从物联网平台控制台设备详情页面获取。
      • ${YourRegionId}:请参见地域和可用区,替换为您的Region ID。
      a1oGs******.iot-as-mqtt.cn-shanghai.aliyuncs.com
      • a1oGs******为本示例产品的productKey
      • cn-shanghai为本示例所在地域。
      Broker Port 设置为1883
      Client ID MQTT.fx的Client ID

      固定格式:${ClientID}|securemode=${Mode},signmethod=${SignMethod}|

      需替换以下参数值:

      • ${ClientId}为设备、App或Web等场景下的Client ID信息,多数情况下为设备的ID信息。可取任意值,长度在64个字符以内。建议使用设备的MAC地址或SN码。
      • ${Mode}为安全模式。
        • TCP直连模式设置为securemode=3,无需设置SSL/TLS信息。
        • TLS直连模式设置为securemode=2,需要设置SSL/TLS信息。
      • ${SignMethod}为算法类型,支持hmacmd5hmacsha1
      注意
      • MQTT.fx的Client ID和设备的${ClientId},切勿混淆。
      • 不要遗漏参数之间及最后的竖线(|)。
      • 设置参数时,请确保参数值中或参数值的前后均没有空格。
      • 输入Client ID信息后,请勿单击Generate
      12345|securemode=2,signmethod=hmacsha1|
      • 12345为示例设备的clientId
      • securemode=2为示例选择的模式,TLS直连模式。
      • signmethod=hmacsha1为示例选择的加密算法。

      General栏下的设置项可保持系统默认,也可以根据您的具体需求设置。

    2. 单击User Credentials,设置User NamePassword
      设置mqtt.fx的User Credentials
      参数 参数说明 示例
      User Name 由设备名DeviceName、and(&)和产品ProductKey组成。

      固定格式:${DeviceName}&${ProductKey}

      Light&a1oGs******
      • Light为设备的DeviceName。
      • a1oGs******为设备的ProductKey。
      Password 通过选择的加密方法,以设备的DeviceSecret为密钥,将参数和参数值拼接后,加密生成Password。
      注意
      • 如果您使用的MQTT.fx版本,在粘贴Password后不显示具体的字符串,只要光标已从输入框的前部移至了后部,则表示粘贴成功,请勿重复粘贴。
      • 请注意参数和参数值中字母的大小写。

      通过以下两种方法生成Password。

      • 工具生成:

        单击下载Password生成小工具。解压文件后,双击sign.html文件,根据网页提示,配置参数,生成password。

        • productKeydeviceNamedeviceSecret:设备证书信息。可在控制台设备详情页查看。
        • timestamp:(可选)时间戳。
        • clientId:设备的ID信息,与MQTT.fx的Client ID${clientId}一致。
        • method:选择签名算法类型,与MQTT.fx的Client ID${SignMethod}一致。
      • 通过加密函数手动生成,本文提供hmacsha1的加密Demo,请参见附录:加密Demo

        其中:

        • ${productKey}${deviceName}${deviceSecret}:需替换为您的设备证书信息。
        • ${clientId}:替换为MQTT.fx的Client ID${clientId}
      • 工具生成示例:工具生成密码示例
      • 手动生成

        本示例没有选择可选参数timestamp。

        函数加密
    3. TLS直连模式(即securemode=2)下,需要选择SSL/TLS,选中Enable SSL/TLS,设置Protocol。建议Protocol选择为TLSv1.2
      说明 TCP直连模式(即securemode=3)下,无需设置SSL/TLS信息,直接进入下一步。
      设置mqtt.fx的SSL/TLS
  4. 设置完成后,单击右下角的OK
  5. 单击Connect
    连接mqtt.fx到物联网

下行通信测试

从物联网平台发送消息,在MQTT.fx上接收消息,测试MQTT.fx与物联网平台连接是否成功 。
说明 MQTT.fx模拟的设备端,支持非透传的消息通信。要实现透传信息通信,您可以使用SDK或在真实设备上测试。
  1. 在MQTT.fx上单击Subscribe
  2. Subscribe文本框中,输入一个设备具有订阅权限的自定义Topic后,再单击Subscribe,订阅这个自定义Topic。
    说明
    • 您可以在对应产品的产品详情页面的Topic 类列表页签下,单击自定义 Topic页签,查看自定义Topic的权限。
    • 输入自定义Topic时,其中的${deviceName}需更改为您的设备名称。

    更多信息,请参见自定义Topic

    订阅成功后,该Topic将显示在列表中。

    自定义Topic显示在mqtt.fx订阅列表中
  3. 登录物联网平台控制台,找到该设备的设备详情页面,在Topic列表页签下,单击已订阅的Topic对应的发布消息
    在mqtt.fx订阅的Topic下发布消息
  4. 输入消息内容,单击确认
    在mqtt.fx订阅的Topic下输入要发布的消息
  5. 回到MQTT.fx上,查看是否接收到消息。
    mqtt.fx接收到消息

上行通信测试

在MQTT.fx上发送消息,通过查看设备日志,测试MQTT.fx与物联网平台连接是否成功 。

  1. 在MQTT.fx上,单击Publish
  2. Publish文本框中,输入一个设备具有发布权限的Topic;然后在文本编辑页面,输入要发送的消息内容,然后单击Publish
    在mqtt.fx发布消息
  3. 登录物联网平台控制台,选择监控运维 > 日志服务,在云端运行日志页签下查看该设备到云的消息。
    查看mqtt.fx发布到物联网平台的消息

查看日志

在MQTT.fx上,单击Log查看操作日志和错误提示日志。

查看mqtt.fx的log

附录:加密Demo

package com.aliyun.iot.util;

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

public class SignDemo {

    public static String sign(Map<String, String> params, String deviceSecret, String signMethod) {
        //将参数Key按字典顺序排序
        String[] sortedKeys = params.keySet().toArray(new String[] {});
        Arrays.sort(sortedKeys);

        //生成规范化请求字符串
        StringBuilder canonicalizedQueryString = new StringBuilder();
        for (String key : sortedKeys) {
            if ("sign".equalsIgnoreCase(key)) {
                continue;
            }
            canonicalizedQueryString.append(key).append(params.get(key));
        }

        try {
            String key = deviceSecret;
            return encryptHMAC(signMethod, canonicalizedQueryString.toString(), key);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static String encryptHMAC(String signMethod, String content, String key) throws Exception {
        SecretKey secretKey = new SecretKeySpec(key.getBytes("utf-8"), signMethod);
        Mac mac = Mac.getInstance(secretKey.getAlgorithm());
        mac.init(secretKey);
        byte[] data = mac.doFinal(content.getBytes("utf-8"));
        return bytesToHexString(data);
    }

    public static final String bytesToHexString(byte[] bArray) {

        StringBuffer sb = new StringBuffer(bArray.length);
        String sTemp;
        for (int i = 0; i < bArray.length; i++) {
            sTemp = Integer.toHexString(0xFF & bArray[i]);
            if (sTemp.length() < 2) {
                sb.append(0);
            }
            sb.append(sTemp.toUpperCase());
        }
        return sb.toString();
    }

    public static void main(String args[]) {
        
        Map<String, String> params = new HashMap<String, String>();
        params.put("productKey", "${productKey}");
        params.put("deviceName", "${deviceName}");
        params.put("clientId", "${clientId}");
        //时间戳,可选参数。传入时,删除下面两行代码前的正斜线
        //String t = System.currentTimeMillis() + "";
        //params.put("timestamp", t);

        String sign = SignDemo.sign(params, "${deviceSecret}", "hmacsha1");
        System.out.print(sign);
    }

}