Dans un article précédent, nous avons examiné
le protocole Modbus , qui est la norme industrielle de facto pour l'interaction
M2M . Développé en 1979, il présente un certain nombre d'inconvénients importants que MQTT résout.
Le protocole MQTT est assez jeune (normalisé seulement en 2016), mais a déjà réussi à être largement utilisé dans l'industrie et l'IoT. Il a été spécialement conçu pour être aussi compact que possible, pour les canaux Internet instables et les appareils de faible puissance, et vous permet de garantir la livraison des messages en cas de perte et de déconnexion de paquets.
Caractéristiques clés du protocole MQTT:
- Compact et léger - surcharge de transfert de données minimale pour économiser le trafic.
- Résistance aux pertes - livraison garantie dans des conditions de connexions réseau instables.
- Asynchrone - vous permet de desservir un grand nombre d'appareils et ne dépend pas des retards du réseau.
- Prise en charge de la qualité de service - la capacité de contrôler la priorité des messages et de garantir la livraison des messages au destinataire.
- Configuration dynamique - ne nécessite pas de coordination préalable des champs et des formats de données, peut être configuré à la volée.
- Fonctionne pour NAT - les clients peuvent être derrière NAT, seul le serveur (courtier) doit avoir une véritable IP. Vous permet de vous passer de VPN et de redirection de port.
- Adressage pratique - les champs de données ont des noms de texte compréhensibles par l'homme. Pas besoin de se souvenir des adresses numériques et des décalages de bits.
Dans l'article, nous allons comparer MQTT et Modbus, analyser la structure du protocole, les concepts de base et essayer d'utiliser le courtier MQTT cloud comme exemple dans une connexion Internet instable.
Historique du protocole MQTT
MQTT a été développé par IBM en 1999 et a été initialement utilisé en interne pour ses solutions.
En novembre 2011, IBM et Eurotech ont annoncé leur participation au groupe de travail Eclipse M2M et le transfert du code MQTT au projet Eclipse Paho.
En 2013, le consortium
OASIS (Organisation pour l'avancement des normes d'information structurée) a entamé le processus de normalisation du protocole MQTT. Jusqu'à présent, la spécification du protocole a été publiée sous licence gratuite, et des sociétés comme Eurotech (anciennement Arcom) utilisent déjà le protocole dans leurs produits.
En octobre 2014, OASIS a publié la première norme officielle de protocole MQTT.
En 2016, le protocole a été normalisé par l'Organisation internationale de normalisation ISO et a reçu le numéro ISO / IEC 20922.
Depuis 2014, l'intérêt pour le protocole commence à croître rapidement et, à en juger par le calendrier de Google Trends, il dépasse aujourd'hui l'intérêt pour Modbus.
Benchmark Google TrendsConcepts de base
MQTT a une architecture client-serveur. La messagerie s'effectue via un serveur central appelé courtier. Dans des conditions normales, les clients ne peuvent pas communiquer directement entre eux et tous les échanges de données ont lieu via un courtier.
Les clients peuvent agir en tant que fournisseurs de données (éditeur) et destinataires de données (abonné). Dans une traduction russe, ces termes sont souvent traduits en tant qu'éditeur et abonné, mais pour éviter toute confusion, nous n'utiliserons que la terminologie d'origine.
Dans le protocole MQTT, les clients communiquent entre eux via un nœud centralAu niveau de l'application, le protocole s'exécute au-dessus de TCP / IP et peut facilement connecter des objets distants directement sur Internet, sans avoir besoin de tunnels VPN. Il suffit au courtier d'avoir une véritable adresse IP et tous les clients peuvent s'y connecter. Dans ce cas, les clients peuvent se trouver derrière NAT. Étant donné que les clients établissent la connexion dans le protocole MQTT, la redirection de port n'est pas requise pour établir une connexion, tandis que dans Modbus / TCP, le serveur établit une connexion (maître), ce qui nécessite une accessibilité directe au réseau.
Le port standard du courtier MQTT pour les connexions TCP entrantes est
1883 . Lorsque vous utilisez une connexion SSL sécurisée, le port
8883 est utilisé .
Courtier
Un courtier est le centre central MQTT pour l'interaction avec les clients. L'échange de données entre les clients se produit uniquement via un courtier. Le courtier peut être un logiciel serveur ou un contrôleur. Ses tâches comprennent la réception de données de clients, le traitement et le stockage de données, la livraison de données aux clients et la surveillance de la livraison des messages.
Éditeur / abonné
Pour comprendre la différence entre Publisher et Subscriber, prenons un exemple simple: un capteur d'humidité mesure l'humidité dans une pièce, et si elle descend en dessous d'un certain niveau, l'humidificateur s'allume.
Dans ce cas, le capteur d'humidité agit comme un
éditeur : sa tâche consiste uniquement à publier des données au courtier. L'humidificateur agit en tant
qu'abonné : il s'abonne aux mises à jour des données d'humidité et reçoit les données actuelles du courtier, tandis que l'humidificateur peut décider à quel moment activer l'humidification.
Dans ce schéma, les clients MQTT, c'est-à-dire le capteur et l'humidificateur, ne sont pas conscients de leur existence mutuelle et n'interagissent pas directement. Le courtier peut recevoir des données de diverses sources, les manipuler, par exemple, calculer la valeur moyenne de plusieurs capteurs et renvoyer les données traitées à l'abonné.
L'éditeur envoie des données au courtier, l'abonné s'abonne aux mises à jour de ces donnéesDans le même temps, l'asynchronie du protocole MQTT prévoit que le capteur et l'humidificateur peuvent être en ligne à des moments différents, perdre des paquets et être inaccessibles. Le courtier se chargera de stocker en mémoire les dernières données reçues du capteur et assurera leur remise à l'humidificateur.
Sujet
MQTT utilise des sujets pour identifier les entités, dans la traduction russe, ils sont également appelés canaux. Les rubriques sont constituées de caractères UTF8 et ont une structure arborescente similaire à un système de fichiers UNIX. Il s'agit d'un mécanisme pratique pour nommer des entités sous une forme lisible par l'homme.
Un exemple de sujets dans MQTT
Cette approche vous permet de voir visuellement quelles données sont transmises, et il est pratique de développer et de déboguer le code sans avoir à mémoriser l'adresse numérique du placement de données, comme cela se fait dans Modbus.
Les sujets incluent également la syntaxe générique, familière à ceux qui ont travaillé avec le système de fichiers UNIX. Le caractère générique peut être à un niveau et à plusieurs niveaux.
Un caractère générique à un niveau est indiqué par un
+ .
Par exemple, pour recevoir des données de capteurs de température dans toutes les pièces de la maison, l'abonné doit s'abonner à un tel sujet:
home/+/temperature
En conséquence, il s'abonnera pour recevoir des données de ces capteurs:
home/kitchen/temperature home/sleeping-room/temperature home/living-room/temperature home/outdoor/temperature
Un caractère générique à plusieurs niveaux est indiqué par le symbole "
# ".
Un exemple d'obtention de données de tous les capteurs dans toutes les pièces de la maison:
home/#
L'abonnement à un tel sujet vous permettra de recevoir des données de tels capteurs:
home/kitchen/temperature home/kitchen/humidity home/kitchen/light home/sleeping-room/temperature home/sleeping-room/humidity home/sleeping-room/light ....
Identification du client
Pour le contrôle d'accès, MQTT fournit une authentification client, contrairement au protocole Modbus, qui n'a pas une telle fonction. Les champs suivants sont utilisés pour le contrôle d'accès:
ClientId - (champ obligatoire) identifiant unique du client. Doit être unique pour chaque client. La version actuelle de la norme MQTT 3.1.1 vous permet d'utiliser le champ ClientId vide si vous n'avez pas besoin d'enregistrer l'état de la connexion.
Nom d'utilisateur - (champ facultatif) connexion pour l'authentification, au format UTF-8. Peut ne pas être unique. Par exemple, un groupe de clients peut se connecter avec le même nom d'utilisateur / mot de passe.
Mot de passe - (champ facultatif) peut être envoyé uniquement avec le champ Nom d'utilisateur, tandis que le nom d'utilisateur peut être transmis sans le champ Mot de passe. 65535 octets maximum. Il est important de savoir que le nom et le mot de passe sont transmis en clair, par conséquent, si les données sont transmises sur des réseaux publics, vous devez utiliser SSL pour crypter la connexion.
Structure du package
Comme mentionné ci-dessus, dans le protocole MQTT, les clients établissent toujours une connexion, qu'ils soient destinataires (abonné) ou fournisseurs (éditeur) de données. Nous analyserons le paquet avec la connexion qui a été interceptée à l'aide du programme Wireshark.
Paquet MQTT transmis sur un canal non chiffréL'en-tête TCP montre que le paquet a été envoyé sur le port 1883, c'est-à-dire que le chiffrement n'est pas utilisé, ce qui signifie que toutes les données sont disponibles sous forme claire, y compris l'identifiant et le mot de passe.
Titre
Le type de message est Connect (commande 0x0001), établissant une connexion avec le courtier. Équipes principales: connexion, déconnexion, publication, abonnement, désabonnement. Il existe également des commandes d'accusé de réception, garder en vie, etc.
Flag DUP - signifie que le message est retransmis, il est utilisé uniquement dans les types de message PUBLISH, SUBSCRIBE, UNSUBSCRIBE, PUBREL, pour les cas où le courtier n'a pas reçu de confirmation de la réception du message précédent.
Niveau QoS - indicateur de qualité de service. Nous aborderons ce sujet plus en détail ultérieurement.
Conserver - les données publiées avec l'indicateur de conservation sont stockées sur le courtier. Lors de la souscription ultérieure à ce sujet, le courtier enverra immédiatement un message avec cet indicateur. Utilisé uniquement dans les messages de type Publier.
Utilisation pratique

Maintenant, après nous être familiarisés avec la théorie, essayons de travailler avec MQTT dans la pratique. Pour cela, nous utiliserons le programme ouvert
Mosquitto , qui peut fonctionner à la fois en mode client et en mode serveur (courtier). Il fonctionne sous Windows, macOS, Linux. Le programme est très pratique pour le débogage et l'étude du protocole MQTT, alors qu'il est également largement utilisé dans les opérations industrielles. Nous l'utiliserons comme client pour envoyer et recevoir des données d'un courtier cloud distant.
De nombreux fournisseurs de cloud fournissent des services de courtage MQTT, tels que
Microsoft Azure IoT Hub ,
Amazon AWS IoT et autres. Dans cet exemple, nous utiliserons le service Cloudmqtt.com, car il a l'inscription la plus simple, et un tarif gratuit suffit pour la formation.
Après l'enregistrement, les détails de connexion à un courtier sont disponibles dans votre compte. Puisque nous nous connectons au serveur via des réseaux Internet publics, il est raisonnable d'utiliser un port SSL pour crypter le trafic.
Détails d'accès au courtier MQTT dans le compte personnel du fournisseur de cloudLa flexibilité du protocole MQTT permet au client de transférer des données non définies précédemment sur le courtier. En d'autres termes, il n'est pas nécessaire de pré-créer les rubriques nécessaires dans lesquelles Publisher peut écrire des données. En utilisant les données reçues de votre compte personnel, nous essaierons de composer manuellement une demande de publication de données sur le sujet
habr / test / random et lecture de celui-ci.
mosquitto_sub - utilitaire client abonné
mosquitto_pub - utilitaire client éditeur
Tout d'abord, connectez-vous au courtier en tant qu'abonné et abonnez-vous pour recevoir des données du sujet
habr / test / aléatoire .
mosquitto_sub -d --capath /etc/ssl/certs/ --url mqtts://hwjspxxt:7oYugN7Fa5Aa@postman.cloudmqtt.com:27529/habr/test/random Client mosq/zEPZz0glUiR4aEipZA sending CONNECT Client mosq/zEPZz0glUiR4aEipZA received CONNACK (0) Client mosq/zEPZz0glUiR4aEipZA sending SUBSCRIBE (Mid: 1, Topic: habr/test/random, QoS: 0, Options: 0x00) Client mosq/zEPZz0glUiR4aEipZA received SUBACK
On peut voir que la connexion a réussi, et nous nous sommes abonnés au sujet
habr / test / random , et maintenant nous attendons des données dans ce sujet du courtier.
Puisqu'une connexion SSL est utilisée, pour vérifier le certificat, vous devez spécifier le chemin par lequel le programme recherchera les certificats de chiffrement racine. Étant donné que le service dans notre exemple utilise un certificat émis par une autorité de certification de confiance, nous indiquons le chemin d'accès au magasin système pour les certificats racine: --capath / etc / ssl / certs /
Dans le cas d'un certificat auto-signé, vous devez spécifier le chemin d'accès à l'autorité de certification souhaitée. Il est également important de prendre en compte la différence de format URI pour les connexions SSL - mqtt s : // et les connexions non chiffrées - mqtt: //. En cas d'erreur de vérification de certificat, le programme se termine sans message d'erreur. Pour une sortie plus détaillée, vous pouvez utiliser le commutateur --debug
Essayons maintenant de publier les données dans le sujet sans interrompre le premier programme.
mosquitto_pub -d --capath /etc/ssl/certs/ --url mqtt://hwjspxxt:7oYugN7Fa5Aa@postman.cloudmqtt.com:27529/habr/test/random -m " !" Client mosq/sWjh9gf8DRASrRZjk6 sending CONNECT Client mosq/sWjh9gf8DRASrRZjk6 received CONNACK (0) Client mosq/sWjh9gf8DRASrRZjk6 sending PUBLISH (d0, q0, r0, m1, 'habr/test/random', ... (22 bytes)) Client mosq/sWjh9gf8DRASrRZjk6 sending DISCONNECT
On peut voir que les données ont bien été reçues par le serveur et publiées dans la rubrique souhaitée. Dans le même temps, dans la première fenêtre dans laquelle le programme mosquitto_sub s'exécute, nous voyons comment le message a été reçu, même Unicode fonctionne, le message est en russe.
Client mosq/zEPZz0glUiR4aEipZA received PUBLISH (d0, q0, r0, m0, 'habr/test/random', ... (22 bytes)) !
QoS et garantie de livraison
Cependant, l'envoi d'un message en temps réel ne surprendra personne, car la même chose peut être faite même avec l'utilitaire banal
nc . Par conséquent, nous allons essayer de simuler une connexion instable entre l'abonné et l'expéditeur. Imaginez que les deux clients fonctionnent via GPRS, avec une énorme perte de paquets, et même une connexion TCP réussie est rare, et vous devez vous assurer que l'abonné est assuré de recevoir un message d'expéditeur. Dans ce cas, les options de QoS viennent à la rescousse.
Par défaut, l'indicateur
QoS est défini
sur 0 pour les messages, ce qui signifie «Déclenchez et oubliez»: Publisher publie le message sur le courtier, mais n'exige pas que le message soit garanti pour être remis à l'abonné. Cela convient aux données dont la perte n'est pas critique, par exemple pour les mesures régulières d'humidité ou de température.
QoS 1: au moins une fois - au moins une . Ce drapeau signifie que jusqu'à ce que l'éditeur reçoive une confirmation de livraison à l'abonné, cette publication sera envoyée au courtier, puis à l'abonné. Ainsi, l'abonné doit recevoir ce message au moins une fois.
QoS 2: Exactement une fois - garantie . Le drapeau QoS, qui offre la plus haute garantie de livraison de messages grâce à l'utilisation de procédures supplémentaires pour la confirmation et l'achèvement de la publication (PUBREC, PUBREL, PUBCOMP). Applicable aux situations où il est nécessaire d'exclure toute perte et duplication de données des capteurs. Par exemple, lorsqu'une alarme est déclenchée à partir d'un message reçu, un appel d'urgence est effectué.
Pour simuler une mauvaise communication, désactivez les deux clients et essayez d'envoyer un message avec la priorité QoS la plus élevée, et ajoutez également l'option Conserver pour que le message envoyé soit enregistré sur le courtier.
mosquitto_pub --retain --qos 2 -d --capath /etc/ssl/certs/ --url mqtt://hwjspxxt:7oYugN7Fa5Aa@postman.cloudmqtt.com:27529/habr/test/random -m " !" Client mosq/Xwhua3GAyyY9mMd05V sending CONNECT Client mosq/Xwhua3GAyyY9mMd05V received CONNACK (0) Client mosq/Xwhua3GAyyY9mMd05V sending PUBLISH (d0, q2, r1, m1, 'habr/test/random', ... (37 bytes)) Client mosq/Xwhua3GAyyY9mMd05V received PUBREC (Mid: 1) Client mosq/Xwhua3GAyyY9mMd05V sending PUBREL (m1) Client mosq/Xwhua3GAyyY9mMd05V received PUBCOMP (Mid: 1, RC:0) Client mosq/Xwhua3GAyyY9mMd05V sending DISCONNECT
Maintenant, après un certain temps, notre destinataire a finalement pu établir une connexion à Internet et connecté au courtier:
mosquitto_sub -d --capath /etc/ssl/certs/ -d --url mqtts://hwjspxxt:7oYugN7Fa5Aa@postman.cloudmqtt.com:27529/habr/test/random Client mosq/VAzcLVMB1MiWhYxoJS sending CONNECT Client mosq/VAzcLVMB1MiWhYxoJS received CONNACK (0) Client mosq/VAzcLVMB1MiWhYxoJS sending SUBSCRIBE (Mid: 1, Topic: habr/test/random, QoS: 0, Options: 0x00) Client mosq/VAzcLVMB1MiWhYxoJS received SUBACK Subscribed (mid: 1): 0 Client mosq/r6UwPnDvx8aNInpPF6 received PUBLISH (d0, q0, r1, m0, 'habr/test/random', ... (37 bytes)) !
Conclusion
MQTT est un protocole moderne et avancé, dépourvu de nombreux inconvénients de ses prédécesseurs. Sa flexibilité vous permet d'ajouter des appareils clients sans configurer de courtier, ce qui permet de gagner beaucoup de temps. Le seuil d'entrée pour comprendre et configurer le protocole est assez bas, et la présence de bibliothèques pour de nombreux langages de programmation vous permet de choisir n'importe quelle pile technologique pour le développement. La garantie de remise des messages distingue MQTT de ses prédécesseurs et vous permet de ne pas perdre de temps à développer inutilement vos propres mécanismes de contrôle d'intégrité au niveau du réseau.