MQTT协议与阿里云IoT物联网平台

1.MQTT协议介绍

1.1 MQTT协议

MQTT(消息队列遥测传输) 是基于 TCP/IP 协议栈而构建的支持在各方之间异步通信的消息协议。MQTT在空间和时间上将消息发送者与接收者分离,因此可以在不可靠的网络环境中进行扩展。虽然叫做消息队列遥测传输,但它与消息队列毫无关系,而是使用了发布和订阅(Pub/Sub)的模型。

MQTT 是一种轻量级的、灵活的网络协议,致力于为 IoT 开发人员实现适当的平衡:

  • 这个轻量级协议可在严重受限的设备硬件和高延迟/带宽有限的网络上实现。
  • 它的灵活性使得为 IoT 设备和服务的多样化应用场景提供支持成为可能。

1.2 MQTT Client库

MQTT Client 库在很多语言中都有实现,包括 Embedded C、C、Java、JavaScript、Python、C++、C#、Go、iOS、Android等。Eclipse Paho的MQTT库下载地址:https://www.eclipse.org/paho/downloads.php

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5bJMI6AS-1576547662519)(https://cdn.nlark.com/lark/0/2018/png/15292/1543486508533-898d509b-7f51-414b-b251-ecf7d60ea3b9.png “”)]

下面开发实践基于Nodejs版mqtt,获取地址 https://www.npmjs.com/package/mqtt

1.3 MQTT报文

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LYQuhB6m-1576547662520)(https://cdn.nlark.com/lark/0/2018/png/15292/1545100927916-203ccc7f-ab6c-4d55-b1af-790868bc67c9.png “”)]

1.3.1 固定报头Fixed header
Bit
7
6
5
4
3
2
1
0
byte 1
MQTT控制报文的类型
用于指定控制报文类型的标志位
byte 2,3,4,5
剩余长度,最大4个字节

控制报文类型

名字报文流动方向描述
Reserved0禁止保留
CONNECT1Client -> Brokerdevice连接IoT平台
CONNACK2Broker -> ClientIoT平台确认连接结果
PUBLISH3双向发布消息
PUBACK4双向QoS=1消息发布收到确认
PUBREC5双向IoT不支持
PUBREL6双向IoT不支持
PUBCOMP7双向IoT不支持
SUBSCRIBE8Client -> Brokerdevice订阅IoT平台Topic
SUBACK9Broker -> ClientIoT平台确认订阅结果
UNSUBSCRIBE10Client -> Brokerdevice取消订阅IoT平台Topic
UNSUBACK11Broker -> ClientIoT平台确认取消订阅结果
PINGREQ12Client -> Brokerdevice发送心跳请求到IoT平台
PINGRESP13Broker -> ClientIoT平台响应device心跳
DISCONNECT14Client -> Brokerdevice断开IoT平台连接
Reserved15禁止保留

控制报文类型标志位

控制报文固定报头标志Bit 3Bit 2Bit 1Bit 0
PUBLISHMQTT 3.1.1使用DUPQoSQoSRETAIN

剩余长度

字节数最小值最大值
10 (0x00)127 (0x7F)
2128 (0x80, 0x01)16 383 (0xFF, 0x7F)
316 384 (0x80, 0x80, 0x01)2 097 151 (0xFF, 0xFF, 0x7F)
42 097 152 (0x80, 0x80, 0x80, 0x01)268 435 455 (0xFF, 0xFF, 0xFF, 0x7F)

注:阿里云IoT的单个payload最大256K

1.3.2 可变报头Variable header

某些MQTT控制报文包含一个可变报头部分。它在固定报头和负载之间。可变报头的内容根据报文类型的不同而不同。可变报头的报文标识符(Packet Identifier)字段存在于在多个类型的报文里。

报文标识符字节 Packet Identifier bytes
Bit7 - 0
byte 1报文标识符 MSB
byte 2报文标识符 LSB
控制报文报文标识符
PUBLISH需要(如果QoS =1,2)
PUBACK需要
PUBREC需要
PUBREL需要
PUBCOMP需要
SUBSCRIBE需要
SUBACK需要
UNSUBSCRIBE需要
UNSUBACK需要
1.3.3 有效载荷Payload

以下MQTT控制报文在报文的最后部分包含一个有效载荷。对于PUBLISH来说有效载荷就是业务消息。

控制报文有效载荷
CONNECT需要
PUBLISH可选
SUBSCRIBE需要
SUBACK需要
UNSUBSCRIBE需要

2.与阿里云IoT平台建立连接

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6ZS8jZui-1576547662521)(https://cdn.nlark.com/lark/0/2018/png/15292/1545100387365-193c5672-a670-4b6d-a792-6fcb8f1329e8.png “”)]

2.1 CONNECT

阿里云IoT物联网平台的MQTT协议不支持will消息,CONNECT 消息内容参数如下:

参数
说明
cleanSession
此标志指定连接是否是持久性的。
0为持久会话,QoS=1消息不会丢失;
1为非持久会话,清理离线消息。
clientId
客户端标识符
username
代理的身份验证和授权凭证。
password
代理的身份验证和授权凭证。
keepAlive
心跳时间, IoT平台约定心跳范围 30s~1200s

其中clientId,username,password由设备三元组(productKey,deviceName,deviceSecret)按照规则生成,具体规则如下:

clientId
id+"|securemode=3,signmethod=hmacsha1,timestamp="+timestamp+"|"
id :表示客户端ID,64字符内。其中 ||内为扩展参数。
securemode:安全模式;2为TLS加密,3为非加密
signmethod:签名算法类型。 timestamp:当前时间毫秒值。
username
deviceName+"&"+productKey
password
sign_hmac(deviceSecret,content)
sign_hmac为clientId中的signmethod算法类型
content为如下拼接字符串: " clientId${ id} deviceName${deviceName} productKey${productKey} timestamp${timestamp}"

官方文档:https://help.aliyun.com/document_detail/73742.html

设备端代码示例(Nodejs版) client.js

/**
"dependencies": { "mqtt": "2.18.8" }
*/
const crypto = require('crypto');
const mqtt = require('mqtt');
//设备身份三元组+区域
const deviceConfig = {
    productKey: "替换",
    deviceName: "替换",
    deviceSecret: "替换",
    regionId: "cn-shanghai"
};
//根据三元组生成mqtt连接参数
const options = initMqttOptions(deviceConfig);
const url = `tcp://${deviceConfig.productKey}.iot-as-mqtt.${deviceConfig.regionId}.aliyuncs.com:1883`;

//2.建立连接
const client = mqtt.connect(url, options);

client.on('packetsend', function (packet){
  console.log('send '+packet.cmd+' packet =>',packet)
})

client.on('packetreceive', function (packet){
  console.log('receive '+packet.cmd+' packet =>',packet)
})


//IoT平台mqtt连接参数初始化
function initMqttOptions(deviceConfig) {

    const params = {
        productKey: deviceConfig.productKey,
        deviceName: deviceConfig.deviceName,
        timestamp: Date.now(),
        clientId: Math.random().toString(36).substr(2),
    }
    //CONNECT参数
    const options = {
        keepalive: 60, //60s
        clean: false, //cleanSession保持持久会话
        protocolVersion: 4 //MQTT v3.1.1
    }
    //1.生成clientId,username,password
    options.password = signHmacSha1(params, deviceConfig.deviceSecret);
    options.clientId = `${params.clientId}|securemode=3,signmethod=hmacsha1,timestamp=${params.timestamp}|`;
    options.username = `${params.deviceName}&${params.productKey}`;

    return options;
}

/*
  生成基于HmacSha1的password
  参考文档:https://help.aliyun.com/document_detail/73742.html?#h2-url-1
*/
function signHmacSha1(params, deviceSecret) {

    let keys = Object.keys(params).sort();
    // 按字典序排序
    keys = keys.sort();
    const list = [];
    keys.map((key) => {
        list.push(`${key}${params[key]}`);
    });
    const contentStr = list.join('');
    return crypto.createHmac('sha1', deviceSecret)
            .update(contentStr)
            .digest('hex');
}

2.2 CONNACK

receive connack packet => Packet {
  cmd: 'connack',
  retain: false,
  qos: 0,
  dup: false,
  length: 2,
  topic: null,
  payload: null,
  sessionPresent: false,
  returnCode: 0 
}

2.4 PINGRESP

send pingreq packet => { cmd: 'pingreq' }

2.5 PINGRESP

receive pingresp packet => Packet {
  cmd: 'pingresp',
  retain: false,
  qos: 0,
  dup: false,
  length: 0,
  topic: null,
  payload: null 
}

2.6 DISCONNECT

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZSGNnsZZ-1576547662522)(https://cdn.nlark.com/lark/0/2018/png/15292/1545103111650-1aa28138-a253-4787-bbb9-e696174ec880.png “”)]

3. 发布数据

3.1 PUBLISH

//3.属性数据上报
const topic = `/sys/${deviceConfig.productKey}/${deviceConfig.deviceName}/thing/event/property/post`;
setInterval(function() {
    //发布数据到topic
    client.publish(topic, getPostData(),{qos:1});
}, 5 * 1000);

function getPostData() {
    const payloadJson = {
        id: Date.now(),
        params: {
            temperature: Math.floor((Math.random() * 20) + 10),
            humidity: Math.floor((Math.random() * 20) + 60)
        },
        method: "thing.event.property.post"
    }

    console.log("===postData\n topic=" + topic)
    console.log(payloadJson)

    return JSON.stringify(payloadJson);
}

send publish packet => { cmd: 'publish',
  topic: '/sys/a1hQSwFledE/eud1jXfEgCsAiP2eId9Q/thing/event/property/post',
  payload: '{"id":1543896481106,"params":{"temperature":23,"humidity":73},"method":"thing.event.property.post"}',
  qos: 1,
  retain: false,
  messageId: 38850,
  dup: false 
}

3.2 PUBACK

receive puback packet => Packet {
  cmd: 'puback',
  retain: false,
  qos: 0,
  dup: false,
  length: 2,
  topic: null,
  payload: null,
  messageId: 38850 
}

4. 接收数据

4.1 SUBSCRIBE

//4.订阅主题,接收指令
const subTopic = `/${deviceConfig.productKey}/${deviceConfig.deviceName}/control`;
client.subscribe(subTopic)
client.on('message', function(topic, message) {
    console.log("topic " + topic)
    console.log("message " + message)
})

SUBSCRIBE消息体

send subscribe packet => { cmd: 'subscribe',
  subscriptions: 
   [ { topic: '/a1hQSwFledE/eud1jXfEgCsAiP2eId9Q/control', qos: 0 } ],
  qos: 1,
  retain: false,
  dup: false,
  messageId: 38851 
}

4.2 SUBACK

SUBACK消息体

receive suback packet => Packet {
  cmd: 'suback',
  retain: false,
  qos: 0,
  dup: false,
  length: 3,
  topic: null,
  payload: null,
  granted: [ 128 ],
  messageId: 38851 
}

4.3 UNSUBSCRIBE

send unsubscribe packet => { cmd: 'unsubscribe',
  qos: 1,
  messageId: 34323,
  unsubscriptions: [ '/a1hQSwFledE/eud1jXfEgCsAiP2eId9Q/control' ] 
}

4.4 UNSUBACK

receive unsuback packet => Packet {
  cmd: 'unsuback',
  retain: false,
  qos: 0,
  dup: false,
  length: 2,
  topic: null,
  payload: null,
  messageId: 34323 
}

5. 服务质量QoS

服务质量
Quality of Service
描述
阿里云IoT
QoS=0
最多一次的传输,可能会收不到消息
支持
QoS=1
至少一次的传输,一定会收到消息,可能重复
支持
QoS=2
有且仅有一次的传输
不支持

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NM8NSxCE-1576547662523)(https://cdn.nlark.com/lark/0/2018/png/15292/1545098670115-30f219ce-21ad-470e-a951-a6b00a268209.png “”)]

6. 设备掉线重连

设备与阿里云IoT的订阅关系在云端保持,除非设备主动unsubscribe,否则订阅关系不清理。设备重连后,依然保持之前的订阅关系,不需要重复订阅。

7. 传输层安全TLS1.2

设备和IoT平台之间的链路可以通过TLS v1.2加密。
如果使用TLS加密,需要下载根证书。
CONNECT参数中clientId的securemode=2

https://help.aliyun.com/document_detail/73742.html

在这里插入图片描述

  • 14
    点赞
  • 112
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值