MQTT fait déjà la partie difficile d'une application de chat : il pousse chaque message vers tous les abonnés en temps réel. Un chat sur MQTT se résume à trois décisions : comment les salons correspondent aux topics, comment fonctionne la présence, et comment empêcher quelqu'un de poster sous le nom d'un autre. Ce tutoriel construit un chat multi-salons fonctionnel dans un seul fichier HTML avec MQTT.js, connecté en WebSocket sécurisé, puis montre comment le tester et le durcir.
Conception des topics
| Topic | Payload | QoS | Retenu | Rôle |
|---|---|---|---|---|
testmqtt/chat/<room>/messages/<user> | {"text":"…","ts":…} | 1 | Non | Messages du chat |
testmqtt/chat/<room>/presence/<user> | {"status":"online"} ou {"status":"offline"} | 1 | Oui | Qui est dans le salon |
Deux choix de ce schéma font l'essentiel du travail :
- L'expéditeur fait partie du topic. Un client s'abonne à
testmqtt/chat/<room>/messages/+et lit le nom d'utilisateur dans le dernier niveau du topic. Avec une ACL qui n'autorise chaque utilisateur à écrire que sous son propre nom (voir plus bas), l'expéditeur ne peut plus être usurpé. Un champ"user"dans le JSON, lui, pourrait l'être. - La présence est un message retenu par utilisateur. Les messages retenus sont livrés dès qu'un client s'abonne : un nouvel arrivant voit donc immédiatement la liste complète des membres, sans rien demander à personne.
Les salons sont simplement des valeurs différentes de <room> : vous avez autant de salons que vous voulez sans aucune configuration du broker. Les bonnes pratiques de conception des topics expliquent pourquoi des hiérarchies stables avec l'identifiant à la fin, comme celle-ci, passent bien à l'échelle.
Présence avec messages retenus et Last Will
La présence combine deux fonctionnalités MQTT :
- Après la connexion, le client publie
{"status":"online"}en retenu sur son propre topic de présence. - Dans le paquet CONNECT, ce même client enregistre un Last Will :
{"status":"offline"}, lui aussi retenu, sur le même topic.
Si la connexion se termine sans DISCONNECT MQTT propre (onglet fermé, portable en veille, réseau coupé), le broker publie le will. Comme il est retenu, il remplace le message « online » et tous les membres voient l'utilisateur passer hors ligne. Quand l'utilisateur clique sur Leave, le client efface volontairement sa présence en publiant un payload retenu vide, ce qui supprime le message retenu.
La page de chat complète
Enregistrez ceci sous chat.html et ouvrez-le deux fois : chat.html?user=alice et chat.html?user=bob. Ajoutez &room=votreequipe pour choisir un salon. Sur l'endpoint de test partagé, prenez un nom de salon peu courant pour ne pas discuter avec des inconnus.
@@CHAT@@
Quelques détails à noter :
textContent, jamaisinnerHTML. Les messages du chat sont des entrées non fiables venant d'autres utilisateurs. Les afficher en HTML ouvre une faille de cross-site scripting.- Les noms de salon et d'utilisateur sont assainis pour qu'un nom contenant
/,+ou#ne puisse pas sortir de son niveau de topic. - Vos propres messages reviennent par le broker. C'est votre confirmation de livraison. Si vous préférez les afficher localement, MQTT 5 permet de s'abonner avec
{ qos: 1, nl: true }(No Local) pour que le broker ne vous les renvoie pas. - Les payloads mal formés sont ignorés au lieu de faire planter le gestionnaire, car toute personne ayant le droit de publier peut envoyer n'importe quoi.
Si la configuration de MQTT.js est nouvelle pour vous, commencez par MQTT dans le navigateur en JavaScript pur, qui explique les options de connexion et la démarche de débogage.
Tester
Ouvrez deux onglets (alice et bob) et passez ces vérifications :
- Les messages circulent dans les deux sens. Chaque message apparaît dans les deux onglets, avec l'expéditeur tiré du topic.
- Les retardataires voient la présence. Ouvrez un troisième onglet : la liste des membres se remplit immédiatement grâce aux messages retenus.
- Le will se déclenche. Fermez l'onglet de bob. Alice doit voir bob grisé en quelques secondes.
- Leave fait le ménage. Cliquez sur Leave dans un onglet : l'utilisateur disparaît de la liste au lieu d'apparaître hors ligne.
Pour voir le trafic brut, abonnez-vous à testmqtt/chat/lobby/# dans le client MQTT en ligne, ou depuis un terminal :
mosquitto_sub -h broker.connectmq.com -t 'testmqtt/chat/lobby/#' -v
mosquitto_pub -h broker.connectmq.com -q 1 \
-t testmqtt/chat/lobby/messages/deploy-bot \
-m "{\"text\":\"Build 412 deployed\",\"ts\":$(date +%s)000}"La seconde commande montre que n'importe quel service (CI, supervision, backend) peut poster dans un salon avec un seul publish. Pour d'autres façons d'éprouver le will, voir tester le Last Will.
Observer le lobby dans le client en ligne
Imposer l'identité avec des ACL
Sur un endpoint de test ouvert, n'importe qui peut publier sur testmqtt/chat/lobby/messages/alice. En production, donnez à chaque utilisateur ses propres identifiants sur le broker et une ACL qui lie les topics au nom d'utilisateur. Avec Mosquitto, les règles pattern remplacent %u par le nom d'utilisateur authentifié :
# Everyone can read every room
pattern read testmqtt/chat/+/#
# ...but may only write under their own name
pattern write testmqtt/chat/+/messages/%u
pattern write testmqtt/chat/+/presence/%uLe broker vérifie le will avec les mêmes règles : un client ne peut donc pas non plus enregistrer un will sur le topic de présence d'un autre. Utilisez ensuite les tests de permissions ACL pour vérifier qu'une publication sur le topic d'un autre utilisateur est vraiment refusée. En MQTT 3.1.1, une telle publication est généralement ignorée sans bruit : vérifiez donc qu'elle n'arrive jamais plutôt que d'attendre une erreur.
Ce que MQTT ne fournit pas
| Besoin | MQTT seul | Solution courante |
|---|---|---|
| Historique des messages | Non (seulement le dernier message retenu par topic) | Un abonné côté backend qui écrit en base de données, plus un endpoint HTTP d'historique |
| Livraison à un utilisateur absent pendant des heures | Les sessions persistantes mettent les messages en file, mais dans les limites du broker | Base de données plus une récupération « depuis la dernière visite » à la reconnexion |
| Accusés de lecture et indicateur de saisie | Non intégré | Topics supplémentaires, par ex. .../typing/<user> en QoS 0 |
| Modération, suppression de messages | Non | Service backend avec droit de publication sur un topic système |
Les sessions persistantes nécessitent aussi un broker qui conserve l'état de session. Ce n'est pas le cas de l'endpoint de test partagé : testez donc le rattrapage des messages sur votre propre instance.
Un chat en temps réel sur MQTT, c'est avant tout de la conception de topics : des salons comme niveaux de topic, l'expéditeur dans le topic, une présence retenue avec Last Will et des ACL qui maintiennent chaque utilisateur dans son couloir. Construisez-le dans un fichier HTML, vérifiez chaque comportement avec deux onglets et un abonné qui observe #, et n'ajoutez une base de données que lorsque les utilisateurs ont besoin d'un historique.
Questions fréquentes
MQTT est-il un bon protocole pour un chat ?
Pour la diffusion en direct, oui : le publish/subscribe correspond naturellement à des salons, les messages sont petits et le Last Will fournit la présence gratuitement. MQTT ne stocke toutefois pas l'historique : associez-le à une base de données si les utilisateurs doivent remonter le fil ou rattraper ce qu'ils ont manqué hors ligne.
Comment afficher qui est en ligne avec MQTT ?
Chaque client publie un message retenu 'online' sur son propre topic de présence et enregistre un Last Will retenu 'offline' sur ce même topic. Les nouveaux clients reçoivent tous les messages de présence retenus à l'abonnement, et le broker publie le will quand un client disparaît.
Comment empêcher les utilisateurs de se faire passer pour d'autres ?
Mettez le nom de l'expéditeur dans le topic, par exemple chat/<salon>/messages/<utilisateur>, et utilisez une ACL côté broker qui n'autorise chaque utilisateur à publier que sous son propre nom. Les destinataires font alors confiance au topic, pas à un champ de nom dans le payload.


