1. Введение в протокол MQTT
1.1 Протокол MQTT
MQTT(Message Queuing Telemetry Transport) — это протокол обмена сообщениями, построенный на стеке TCP/IP, который поддерживает асинхронную связь между сторонами. MQTT разделяет отправителей и получателей сообщений в пространстве и времени, поэтому его можно масштабировать в ненадежных сетевых средах. Хотя он и называется транспортом телеметрии очереди сообщений, он не имеет ничего общего с очередью сообщений и вместо этого использует модель публикации и подписки (Pub/Sub).
MQTT — это легкий и гибкий сетевой протокол, который стремится найти правильный баланс для разработчиков IoT:
- Этот упрощенный протокол может быть реализован на оборудовании устройства с жесткими ограничениями и в сетях с высокой задержкой/ограниченной пропускной способностью.
- Его гибкость позволяет поддерживать различные варианты использования устройств и услуг IoT.
1.2 Клиентская библиотека MQTT
Клиентская библиотека MQTT реализована на многих языках, включая Embedded C, C, Java, JavaScript, Python, C++, C#, Go, iOS, Android и т. д. Адрес загрузки библиотеки MQTT Eclipse Paho:Woohoo.eclipse.org/afraid/down…
Следующая практика разработки основана на версии mqtt Nodejs для получения адресаУууу, у этой лошади плюс .com/package/ нет будущего…
1.3 MQTT-сообщение
1.3.1 Фиксированный заголовок Фиксированный заголовок
Bit
|
7
|
6
|
5
|
4
|
3
|
2
|
1
|
0
|
byte 1
|
Типы пакетов управления MQTT
|
Бит флага, используемый для указания типа управляющего сообщения.
|
||||||
byte 2,3,4,5
|
Оставшаяся длина, до 4 байт
|
тип управляющего сообщения
название | ценность | Направление потока пакетов | описывать |
---|---|---|---|
Reserved | 0 | запретить | резерв |
CONNECT | 1 | Client -> Broker | устройство подключается к платформе IoT |
CONNACK | 2 | Broker -> Client | Платформа IoT подтверждает результат соединения |
PUBLISH | 3 | двусторонний | опубликовать новость |
PUBACK | 4 | двусторонний | Получено подтверждение публикации сообщения QoS=1 |
SUBSCRIBE | 8 | Client -> Broker | устройство подписывается на платформу IoT Тема |
SUBACK | 9 | Broker -> Client | Платформа IoT подтверждает результат подписки |
UNSUBSCRIBE | 10 | Client -> Broker | устройство отписаться от платформы IoT Тема |
UNSUBACK | 11 | Broker -> Client | Платформа IoT подтверждает результат отмены подписки |
PINGREQ | 12 | Client -> Broker | устройство отправляет запрос сердцебиения на платформу IoT |
PINGRESP | 13 | Broker -> Client | Платформа IoT реагирует на пульс устройства |
DISCONNECT | 14 | Client -> Broker | устройство отключает платформу IoT |
Reserved | 15 | запретить | резерв |
Бит флага типа управляющего сообщения
управляющее сообщение | фиксированный флаг заголовка | Bit 3 | Bit 2 | Bit 1 | Bit 0 |
---|---|---|---|---|---|
PUBLISH | Использование MQTT 3.1.1 | DUP | QoS | QoS | RETAIN |
оставшаяся длина
количество байтов | минимум | максимальное значение |
---|---|---|
1 | 0 (0x00) | 127 (0x7F) |
2 | 128 (0x80, 0x01) | 16 383 (0xFF, 0x7F) |
3 | 16 384 (0x80, 0x80, 0x01) | 2 097 151 (0xFF, 0xFF, 0x7F) |
4 | 2 097 152 (0x80, 0x80, 0x80, 0x01) | 268 435 455 (0xFF, 0xFF, 0xFF, 0x7F) |
Примечание. Максимальная полезная нагрузка Alibaba Cloud IoT составляет 256 КБ.
1.3.2 Переменный заголовок Переменный заголовок
Некоторые управляющие сообщения MQTT содержат переменный заголовок. Он находится между фиксированным заголовком и полезной нагрузкой. Содержимое заголовка переменной зависит от типа пакета. Поле Packet Identifier в заголовке переменной существует во многих типах пакетов.
Байты идентификатора пакета
Bit | 7 - 0 |
---|---|
byte 1 | Идентификатор сообщения, старший бит |
byte 2 | Младший бит идентификатора пакета |
управляющее сообщение | идентификатор сообщения |
---|---|
PUBLISH | Обязательно (если QoS = 1, 2) |
PUBACK | необходимость |
PUBREC | необходимость |
PUBREL | необходимость |
PUBCOMP | необходимость |
SUBSCRIBE | необходимость |
SUBACK | необходимость |
UNSUBSCRIBE | необходимость |
UNSUBACK | необходимость |
1.3.3 Полезная нагрузка
Следующее управляющее сообщение MQTT содержит полезную нагрузку в последней части сообщения. Для PUBLISH полезной нагрузкой является деловое сообщение.
управляющее сообщение | Полезная нагрузка |
---|---|
CONNECT | необходимость |
PUBLISH | по желанию |
SUBSCRIBE | необходимость |
SUBACK | необходимость |
UNSUBSCRIBE | необходимость |
2. Установите соединение с платформой Alibaba Cloud IoT.
2.1 CONNECT
Протокол MQTT платформы Alibaba Cloud IoT не поддерживает сообщение will.Параметры содержимого сообщения CONNECT следующие:
параметр
|
инструкция
|
cleanSession
|
Этот флаг указывает, является ли соединение постоянным.
0 — постоянный сеанс, сообщения QoS=1 не будут потеряны;
1 — это непостоянный сеанс, который очищает автономные сообщения.
|
clientId
|
идентификатор клиента
|
username
|
Учетные данные аутентификации и авторизации для прокси.
|
password
|
Учетные данные аутентификации и авторизации для прокси.
|
keepAlive
|
время сердцебиения,Платформа IoT согласовала диапазон сердцебиения от 30 до 1200 с
|
clientId, имя пользователя и пароль генерируются триплетом устройства (productKey, deviceName, deviceSecret) в соответствии с правилами. Конкретные правила заключаются в следующем:
clientId
|
id+"|securemode=3,signmethod=hmacsha1,timestamp="+timestamp+"|"
|
id: указывает идентификатор клиента в пределах 64 символов.. в||Внутри находится расширенный параметр.
securemode: безопасный режим; 2 для шифрования TLS, 3 для незашифрованного
signmethod: тип алгоритма подписи. timestamp: Текущее время в миллисекундах.
|
username
|
deviceName+"&"+productKey
|
|
password
|
sign_hmac(deviceSecret,content)
|
sign_hmacтип алгоритма signmethod в clientId
contentОбъедините строки следующим образом: "clientId${id}deviceName${deviceName}productKey${productKey}timestamp${timestamp}"
|
Официальная документация:help.aliyun.com/document_…
Пример кода на стороне устройства (версия 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
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)
})
ПОДПИСАТЬСЯ тело сообщения
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
|
описывать
|
Облако Интернета вещей Alibaba
|
QoS=0
|
Не более одной передачи, сообщения не могут быть получены
|
служба поддержки
|
QoS=1
|
По крайней мере, одна передача, сообщение должно быть получено, возможно, повторено
|
служба поддержки
|
QoS=2
|
одна и только одна передача
|
не поддерживается
|
6. Устройство отключается и снова подключается
Отношения подписки между устройством и Alibaba Cloud IoT поддерживаются в облаке. Если устройство активно не откажется от подписки, отношения подписки не будут очищены. После повторного подключения устройства предыдущие отношения подписки сохраняются, и повторная подписка не требуется.
7. Безопасность транспортного уровня TLS1.2
Связь между устройством и платформой IoT может быть зашифрована с помощью TLS v1.2. Если вы используете шифрование TLS, вам необходимо загрузить корневой сертификат. clientId в параметре CONNECTsecuremode=2