Протокол MQTT и платформа Alibaba Cloud IoT

Интернет вещей

1. Введение в протокол MQTT

1.1 Протокол MQTT

MQTT(Message Queuing Telemetry Transport) — это протокол обмена сообщениями, построенный на стеке TCP/IP, который поддерживает асинхронную связь между сторонами. MQTT разделяет отправителей и получателей сообщений в пространстве и времени, поэтому его можно масштабировать в ненадежных сетевых средах. Хотя он и называется транспортом телеметрии очереди сообщений, он не имеет ничего общего с очередью сообщений и вместо этого использует модель публикации и подписки (Pub/Sub).

MQTT — это легкий и гибкий сетевой протокол, который стремится найти правильный баланс для разработчиков IoT:

  • Этот упрощенный протокол может быть реализован на оборудовании устройства с жесткими ограничениями и в сетях с высокой задержкой/ограниченной пропускной способностью.
  • Его гибкость позволяет поддерживать различные варианты использования устройств и услуг IoT.

image.png | left | 642x320

1.2 Клиентская библиотека MQTT

Клиентская библиотека MQTT реализована на многих языках, включая Embedded C, C, Java, JavaScript, Python, C++, C#, Go, iOS, Android и т. д. Адрес загрузки библиотеки MQTT Eclipse Paho:Woohoo.eclipse.org/afraid/down…

image.png | left | 747x320

Следующая практика разработки основана на версии mqtt Nodejs для получения адресаУууу, у этой лошади плюс .com/package/ нет будущего…

1.3 MQTT-сообщение

image.png | left | 456x260

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
PUBREC 5 двусторонний Интернет вещей не поддерживает
PUBREL 6 двусторонний Интернет вещей не поддерживает
PUBCOMP 7 двусторонний Интернет вещей не поддерживает
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.

image.png | left | 573x260

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

image.png | left | 590x300

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
одна и только одна передача
не поддерживается

阿里云IoT的QoS.png | left | 500x247

6. Устройство отключается и снова подключается

Отношения подписки между устройством и Alibaba Cloud IoT поддерживаются в облаке. Если устройство активно не откажется от подписки, отношения подписки не будут очищены. После повторного подключения устройства предыдущие отношения подписки сохраняются, и повторная подписка не требуется.

7. Безопасность транспортного уровня TLS1.2

Связь между устройством и платформой IoT может быть зашифрована с помощью TLS v1.2. Если вы используете шифрование TLS, вам необходимо загрузить корневой сертификат. clientId в параметре CONNECTsecuremode=2

help.aliyun.com/document_…

технология Интернета вещей

iot-tech-weixin.png | center | 225x224