MQTT 客户端收发 MQTT 消息

本文主要介绍如何使用 MQTT 客户端收发 MQTT 消息,并给出示例代码供前期开发测试参考,包括资源创建、环境准备、示例代码、注意事项等。

注意:

本文给出的实例均基于 Eclipse Paho Java SDK 实现,SDK 下载请参见 MQTT 接入准备。如使用其他第三方的客户端,请适当修改。

1. 资源创建

使用 MQ 提供的 MQTT 服务,首先需要核实应用中使用的 Topic 资源是否已经创建,如果没有,请先去控制台创建 Topic,Group ID 等资源。

创建资源时需要根据需求选择对应的 Region,例如 MQTT 需要使用华北2的接入点,那么 Topic 等资源就在华北2 创建,资源创建具体请参见创建资源

注意:MQTT 使用的多级子 Topic 不需要创建,代码里直接使用即可,没有限制。

2. 环境准备

使用 MQTT 协议来收发消息,需要根据应用平台选择合适的客户端。本示例运行在 Java 平台,使用 Eclipse Paho Java SDK 构建。首先引入 Maven 依赖,POM 文件配置如下:

 
  1. <dependencies>
  2. <dependency>
  3. <groupId>org.eclipse.paho</groupId>
  4. <artifactId>org.eclipse.paho.client.mqttv3</artifactId>
  5. <version>1.2.0</version>
  6. </dependency>
  7. <dependency>
  8. <groupId>commons-codec</groupId>
  9. <artifactId>commons-codec</artifactId>
  10. <version>1.10</version>
  11. </dependency>
  12. </dependencies>
  13. <repositories>
  14. <repository>
  15. <id>Eclipse Paho Repo</id>
  16. <url>https://repo.eclipse.org/content/repositories/paho-releases/</url>
  17. </repository>
  18. <repository>
  19. <id>snapshots-repo</id>
  20. <url>https://oss.sonatype.org/content/repositories/snapshots</url>
  21. <releases>
  22. <enabled>false</enabled>
  23. </releases>
  24. <snapshots>
  25. <enabled>true</enabled>
  26. </snapshots>
  27. </repository>
  28. </repositories>

3. MQTT 发送消息

本段示例代码演示如何使用 MQTT 客户端发送普通消息和 P2P 的点对点消息,其中用到的工具 MacSignature 参考下文。

 
  1. public class MQTTSendMsg {
  2. public static void main(String[] args) throws IOException {
  3. /**
  4. * 设置当前用户私有的 MQTT 的接入点。例如此处示意使用 XXX,实际使用请替换用户自己的接入点。接入点的获取方法是,在控制台创建 MQTT 实例,每个实例都会分配一个接入点域名。
  5. */
  6. final String broker ="tcp://XXXX.mqtt.aliyuncs.com:1883";
  7. /**
  8. * 设置阿里云的 AccessKey,用于鉴权
  9. */
  10. final String acessKey ="XXXXXX";
  11. /**
  12. * 设置阿里云的 SecretKey,用于鉴权
  13. */
  14. final String secretKey ="XXXXXXX";
  15. /**
  16. * 发消息使用的一级 Topic,需要先在 MQ 控制台里创建
  17. */
  18. final String topic ="XXXX";
  19.  
  20. /**
  21. * MQTT 的 ClientID,一般由两部分组成,GroupID@@@DeviceID
  22. * 其中 GroupID 在 MQ 控制台里创建
  23. * DeviceID 由应用方设置,可能是设备编号等,需要唯一,否则服务端拒绝重复的 ClientID 连接
  24. */
  25. final String clientId ="GID_XXX@@@ClientID_XXXX";
  26. String sign;
  27. MemoryPersistence persistence = new MemoryPersistence();
  28. try {
  29. final MqttClient sampleClient = new MqttClient(broker, clientId, persistence);
  30. final MqttConnectOptions connOpts = new MqttConnectOptions();
  31. System.out.println("Connecting to broker: " + broker);
  32. /**
  33. * 计算签名,将签名作为 MQTT 的 password。
  34. * 签名的计算方法,参考工具类 MacSignature,第一个参数是 ClientID 的前半部分,即 GroupID
  35. * 第二个参数阿里云的 SecretKey
  36. */
  37. sign = MacSignature.macSignature(clientId.split("@@@")[0], secretKey);
  38. connOpts.setUserName(acessKey);
  39. connOpts.setServerURIs(new String[] { broker });
  40. connOpts.setPassword(sign.toCharArray());
  41. connOpts.setCleanSession(true);
  42. connOpts.setKeepAliveInterval(90);
  43. connOpts.setAutomaticReconnect(true);
  44. connOpts.setMqttVersion(MQTT_VERSION_3_1_1);
  45. sampleClient.setCallback(new MqttCallbackExtended() {
  46. public void connectComplete(boolean reconnect, String serverURI) {
  47. System.out.println("connect success");
  48. //连接成功,需要上传客户端所有的订阅关系
  49. }
  50. public void connectionLost(Throwable throwable) {
  51. System.out.println("mqtt connection lost");
  52. }
  53. public void messageArrived(String topic, MqttMessage mqttMessage) throws Exception {
  54. System.out.println("messageArrived:" + topic + "------" + new String(mqttMessage.getPayload()));
  55. }
  56. public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
  57. System.out.println("deliveryComplete:" + iMqttDeliveryToken.getMessageId());
  58. }
  59. });
  60. sampleClient.connect(connOpts);
  61. for (int i = 0; i < 10; i++) {
  62. try {
  63. String scontent = new Date()+"MQTT Test body" + i;
  64. //此处消息体只需要传入 byte 数组即可,对于其他类型的消息,请自行完成二进制数据的转换
  65. final MqttMessage message = new MqttMessage(scontent.getBytes());
  66. message.setQos(0);
  67. System.out.println(i+" pushed at "+new Date()+" "+ scontent);
  68. /**
  69. *消息发送到某个主题 Topic,所有订阅这个 Topic 的设备都能收到这个消息。
  70. * 遵循 MQTT 的发布订阅规范,Topic 也可以是多级 Topic。此处设置了发送到二级 Topic
  71. */
  72. sampleClient.publish(topic+"/notice/", message);
  73. /**
  74. * 如果发送 P2P 消息,二级 Topic 必须是“p2p”,三级 Topic 是目标的 ClientID
  75. * 此处设置的三级 Topic 需要是接收方的 ClientID
  76. */
  77. String p2pTopic =topic+"/p2p/GID_mqttdelay3@@@DEVICEID_001";
  78. sampleClient.publish(p2pTopic,message);
  79. } catch (Exception e) {
  80. e.printStackTrace();
  81. }
  82. }
  83. } catch (Exception me) {
  84. me.printStackTrace();
  85. }
  86. }
  87. }

4. MQTT 接收消息

本段代码演示如何使用 MQTT 客户端订阅消息,接收普通的消息以及点对点消息。

 
  1. public class MQTTRecvMsg {
  2. public static void main(String[] args) throws IOException {
  3. /**
  4. * 设置当前用户私有的 MQTT 的接入点。例如此处示意使用 XXX,实际使用请替换用户自己的接入点。接入点的获取方法是,在控制台创建 MQTT 实例,每个实例都会分配一个接入点域名。
  5. */
  6. final String broker ="tcp://XXXX.mqtt.aliyuncs.com:1883";
  7. /**
  8. * 设置阿里云的 AccessKey,用于鉴权
  9. */
  10. final String acessKey ="XXXXXX";
  11. /**
  12. * 设置阿里云的 SecretKey,用于鉴权
  13. */
  14. final String secretKey ="XXXXXXX";
  15. /**
  16. * 发消息使用的一级 Topic,需要先在 MQ 控制台里创建
  17. */
  18. final String topic ="XXXX";
  19.  
  20. /**
  21. * MQTT 的 ClientID,一般由两部分组成,GroupID@@@DeviceID
  22. * 其中 GroupID 在 MQ 控制台里创建
  23. * DeviceID 由应用方设置,可能是设备编号等,需要唯一,否则服务端拒绝重复的 ClientID 连接
  24. */
  25. final String clientId ="GID_XXXX@@@ClientID_XXXXXX";
  26. String sign;
  27. MemoryPersistence persistence = new MemoryPersistence();
  28. try {
  29. final MqttClient sampleClient = new MqttClient(broker, clientId, persistence);
  30. final MqttConnectOptions connOpts = new MqttConnectOptions();
  31. System.out.println("Connecting to broker: " + broker);
  32. /**
  33. * 计算签名,将签名作为 MQTT 的 password
  34. * 签名的计算方法,参考工具类 MacSignature,第一个参数是 ClientID 的前半部分,即 GroupID
  35. * 第二个参数阿里云的 SecretKey
  36. */
  37. sign = MacSignature.macSignature(clientId.split("@@@")[0], secretKey);
  38. /**
  39. * 设置订阅方订阅的 Topic 集合,此处遵循 MQTT 的订阅规则,可以是一级 Topic,二级 Topic,P2P 消息请订阅/p2p
  40. */
  41. final String[] topicFilters=new String[]{topic+"/notice/",topic+"/p2p"};
  42. final int[]qos={0,0};
  43. connOpts.setUserName(acessKey);
  44. connOpts.setServerURIs(new String[] { broker });
  45. connOpts.setPassword(sign.toCharArray());
  46. connOpts.setCleanSession(true);
  47. connOpts.setKeepAliveInterval(90);
  48. connOpts.setAutomaticReconnect(true);
  49. sampleClient.setCallback(new MqttCallbackExtended() {
  50. public void connectComplete(boolean reconnect, String serverURI) {
  51. System.out.println("connect success");
  52. //连接成功,需要上传客户端所有的订阅关系
  53. sampleClient.subscribe(topicFilters,qos);
  54. }
  55. public void connectionLost(Throwable throwable) {
  56. System.out.println("mqtt connection lost");
  57. }
  58. public void messageArrived(String topic, MqttMessage mqttMessage) throws Exception {
  59. System.out.println("messageArrived:" + topic + "------" + new String(mqttMessage.getPayload()));
  60. }
  61. public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
  62. System.out.println("deliveryComplete:" + iMqttDeliveryToken.getMessageId());
  63. }
  64. });
  65. //客户端每次上线都必须上传自己所有涉及的订阅关系,否则可能会导致消息接收延迟
  66. sampleClient.connect(connOpts);
  67. //每个客户端最多允许存在30个订阅关系,超出限制可能会丢弃导致收不到部分消息
  68. sampleClient.subscribe(topicFilters,qos);
  69. Thread.sleep(Integer.MAX_VALUE);
  70. } catch (Exception me) {
  71. me.printStackTrace();
  72. }
  73. }
  74. }

上文代码用到的工具类 MacSignature.java 如下:

 
  1. public class MacSignature {
  2. /**
  3. * @param text 要签名的文本
  4. * @param secretKey 阿里云 MQ SecretKey
  5. * @return 加密后的字符串
  6. * @throws InvalidKeyException
  7. * @throws NoSuchAlgorithmException
  8. */
  9. public static String macSignature(String text, String secretKey) throws InvalidKeyException, NoSuchAlgorithmException {
  10. Charset charset = Charset.forName("UTF-8");
  11. String algorithm = "HmacSHA1";
  12. Mac mac = Mac.getInstance(algorithm);
  13. mac.init(new SecretKeySpec(secretKey.getBytes(charset), algorithm));
  14. byte[] bytes = mac.doFinal(text.getBytes(charset));
  15. return new String(Base64.encodeBase64(bytes), charset);
  16. }
  17. /**
  18. * 发送方签名方法
  19. *
  20. * @param clientId MQTT ClientID
  21. * @param secretKey 阿里云 MQ SecretKey
  22. * @return 加密后的字符串
  23. * @throws NoSuchAlgorithmException
  24. * @throws InvalidKeyException
  25. */
  26. public static String publishSignature(String clientId, String secretKey) throws NoSuchAlgorithmException, InvalidKeyException {
  27. return macSignature(clientId, secretKey);
  28. }
  29. /**
  30. * 订阅方签名方法
  31. *
  32. * @param topics 要订阅的 Topic 集合
  33. * @param clientId MQTT ClientID
  34. * @param secretKey 阿里云 MQ SecretKey
  35. * @return 加密后的字符串
  36. * @throws NoSuchAlgorithmException
  37. * @throws InvalidKeyException
  38. */
  39. public static String subSignature(List<String> topics, String clientId, String secretKey) throws NoSuchAlgorithmException, InvalidKeyException {
  40. Collections.sort(topics); //以字典顺序排序
  41. String topicText = "";
  42. for (String topic : topics) {
  43. topicText += topic + "\n";
  44. }
  45. String text = topicText + clientId;
  46. return macSignature(text, secretKey);
  47. }
  48. /**
  49. * 订阅方签名方法
  50. *
  51. * @param topic 要订阅的 Topic
  52. * @param clientId MQTT ClientID
  53. * @param secretKey 阿里云 MQ SecretKey
  54. * @return 加密后的字符串
  55. * @throws NoSuchAlgorithmException
  56. * @throws InvalidKeyException
  57. */
  58. public static String subSignature(String topic, String clientId, String secretKey) throws NoSuchAlgorithmException, InvalidKeyException {
  59. List<String> topics = new ArrayList<String>();
  60. topics.add(topic);
  61. return subSignature(topics, clientId, secretKey);
  62. }
  63. }

猜你喜欢

转载自blog.csdn.net/aa1215018028/article/details/82781843