【前端】MQTT:通信与聊天室实战
本文是关于 MQTT.js 使用的详细长文教程。文章会循序渐进,从入门到进阶,涵盖常用功能、实用技巧以及代码示例,帮助读者深入理解如何在 JavaScript 环境中使用 MQTT 协议。整个教程将分为以下几个部分:
MQTT 协议
MQTT(Message Queuing Telemetry Transport)是一种轻量级的发布/订阅协议,设计用于低带宽、不稳定网络环境,广泛应用于物联网(IoT)领域。它基于客户端-代理模式,支持三种消息质量服务(QoS)等级,能够满足不同场景的需求。
MQTT.js 安装
MQTT.js 是一个客户端库,用于在 JavaScript 中使用 MQTT 协议。它支持 Node.js 和浏览器环境,提供了简洁的 API,使得开发者能够快速集成 MQTT 协议。
-
使用 npm 安装(适用于 Node.js 环境):
npm install mqtt --save
-
在浏览器环境中,可以通过 CDN 引入:
<script src="https://unpkg.com/mqtt/dist/mqtt.min.js"></script>
基础使用
连接到 MQTT 代理
首先,连接到 MQTT 代理服务器是使用 MQTT.js 的第一步。可以通过 mqtt.connect()
方法进行连接。
const mqtt = require('mqtt');
const client = mqtt.connect('mqtt://broker.hivemq.com');
client.on('connect', () => {
console.log('Connected to MQTT broker');
});
在这个示例中,我们使用了 HiveMQ 的公共 MQTT 代理作为例子,你也可以将 mqtt://broker.hivemq.com
替换为你自己的代理地址。
订阅与发布消息
连接成功后,你可以订阅主题以接收消息,或者发布消息到指定主题。
订阅消息
client.subscribe('my/topic', (err) => {
if (!err) {
console.log('Subscribed to topic: my/topic');
}
});
发布消息
client.publish('my/topic', 'Hello MQTT', (err) => {
if (!err) {
console.log('Message sent to my/topic');
}
});
消息 QoS 等级
MQTT 协议支持三种 QoS 等级:
- QoS 0:最多一次,不保证消息送达。
- QoS 1:至少一次,保证消息送达,但可能重复。
- QoS 2:只有一次,确保消息送达且不重复。
client.publish('my/topic', 'Hello QoS 1', { qos: 1 });
好的,我们来详细讲解如何在 Linux 服务器上部署 MQTT 代理(以 Mosquitto 为例),并设置用户名和密码管理。
MQTT.js 配置与管理
设置连接选项
你可以通过 connect()
方法的第二个参数传递配置项来定制连接选项,例如设置客户端ID、用户名、密码等。
const options = {
clientId: 'mqttjs_' + Math.random().toString(16).substr(2, 8),
username: 'user',
password: 'password',
keepalive: 60,
clean: true
};
const client = mqtt.connect('mqtt://broker.hivemq.com', options);
连接回调与错误处理
可以使用 on('connect')
、on('error')
等事件处理函数来监听连接状态:
client.on('connect', () => {
console.log('Connected successfully');
});
client.on('error', (err) => {
console.error('Connection error: ', err);
});
连接与安全
使用 TLS 加密连接
为了确保连接的安全性,MQTT.js 支持通过 TLS 加密连接。你可以设置 mqtts://
协议来使用加密连接。
const client = mqtt.connect('mqtts://broker.hivemq.com', {
port: 8883,
username: 'user',
password: 'password'
});
MQTT 认证与授权
有些 MQTT 代理需要认证才能连接,MQTT.js 支持传递认证信息,如用户名和密码。
const options = {
username: 'username',
password: 'password'
};
const client = mqtt.connect('mqtt://broker.hivemq.com', options);
高级功能与技巧
持久化会话与遗嘱消息
你可以通过配置持久化会话和遗嘱消息,增强客户端在断开连接后的一些行为。
const options = {
clean: false, // 保持会话
will: {
topic: 'last/will',
payload: 'Client disconnected unexpectedly',
qos: 1
}
};
const client = mqtt.connect('mqtt://broker.hivemq.com', options);
消息订阅与发布的优化
当你需要发布大量消息时,可以选择设置 QoS 0 来减少网络负担,或者使用 保留消息 来减少客户端的接收负担。
构建聊天室应用
技术栈
- HTML/CSS/JavaScript:构建前端页面和交互
- MQTT.js:通过 WebSocket 与 MQTT 代理进行消息通信
- Bootstrap:用于布局和响应式设计(可选,但本教程不聚焦于其详细使用)
初始化项目
我们从一个简单的 HTML 文件开始,包含必要的依赖项。项目不依赖于任何后端,因此我们将使用免费的 MQTT 代理服务器进行通信。我们选择 broker.emqx.io,它是一个公开的 MQTT 代理,可以用于测试。
创建项目文件
在你计算机上创建一个文件夹,例如 mqtt-chatroom
,然后在其中创建一个 HTML 文件,如 index.html
。
引入必要的库
首先,我们需要引入 MQTT.js 和 Bootstrap(用于美化布局和使其响应式):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Responsive MQTT Chatroom</title>
<!-- 引入 Bootstrap -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<!-- 引入 MQTT.js -->
<script src="https://unpkg.com/mqtt/dist/mqtt.min.js"></script>
</head>
<body>
<div class="container mt-4">
<h2 class="text-center">MQTT Chatroom</h2>
<!-- 聊天框区域 -->
<div id="chat" class="mb-4" style="height: 60vh; overflow-y: auto; background-color: #f8f9fa; border: 1px solid #ddd; padding: 10px;"></div>
<!-- 输入区域 -->
<div class="row">
<div class="col-12 col-md-2 mb-2 mb-md-0">
<input type="text" id="username" class="form-control" placeholder="Your Name">
</div>
<div class="col-12 col-md-8 mb-2 mb-md-0">
<input type="text" id="message" class="form-control" placeholder="Type your message here...">
</div>
<div class="col-12 col-md-2">
<button id="sendBtn" class="btn btn-primary btn-block">Send</button>
</div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.2/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
连接到 MQTT 代理
初始化 MQTT 连接
在前端页面加载时,我们需要通过 MQTT.js 连接到一个 MQTT 代理。此处我们使用 broker.emqx.io 作为测试代理。它支持 WebSocket 协议,允许我们通过浏览器与其通信。
在 HTML 文件中添加以下 JavaScript 代码来连接 MQTT 代理。
<script>
// 连接到 MQTT 服务器
const client = mqtt.connect("ws://broker.emqx.io:8083/mqtt");
// 连接成功时的回调
client.on("connect", () => {
console.log("Connected to MQTT broker");
});
</script>
处理连接
当连接成功后,我们将显示一条日志消息。你可以通过浏览器的控制台查看此信息。为了更好地管理连接,我们将通过 client.on('connect', ...)
回调函数确保连接成功。
发布和订阅消息
发布消息
聊天室的核心功能是能够发送消息并将其展示给所有订阅者。我们通过 MQTT.js 提供的 client.publish()
方法来实现发布消息。
为了让消息在聊天室中展示,我们将每个消息格式化为 JSON 对象,包含 username
和 data
(即消息内容)。
<script>
// 发送消息的函数
function sendMessage() {
const username = document.getElementById("username").value.trim();
const message = document.getElementById("message").value.trim();
if (username && message) {
const payload = JSON.stringify({ username, data: message });
client.publish("chatroom", payload); // 发布到 chatroom 房间
document.getElementById("message").value = ""; // 清空输入框
} else {
alert("Please enter both your name and a message.");
}
}
document.getElementById("sendBtn").addEventListener("click", sendMessage);
</script>
订阅消息
为了接收并展示消息,我们需要订阅 chatroom
主题。当有新的消息到达时,client.on('message', ...)
事件会被触发,接收到的消息会被解析并显示到页面上。
<script>
client.on("message", (topic, message) => {
const { username, data } = JSON.parse(message.toString());
const chat = document.getElementById("chat");
const messageDiv = document.createElement("div");
messageDiv.className = "message";
messageDiv.innerHTML = `<span class="username">${username}:</span> ${data}`;
chat.appendChild(messageDiv);
chat.scrollTop = chat.scrollHeight; // 滚动到底部
});
// 订阅 chatroom 主题
client.subscribe("chatroom", () => {
console.log("Subscribed to chatroom");
});
</script>
实现房间选择功能
为了实现房间功能,我们允许用户切换聊天室(即订阅不同的主题)。我们添加一个按钮来选择房间,并通过弹窗输入新的房间名称来动态切换。
创建房间选择按钮
在页面的顶部,我们创建一个按钮,用于选择聊天室。点击按钮时会弹出一个对话框,允许用户输入新的房间名。
<div class="room-info">
<button id="roomSelectButton" class="btn btn-secondary">
<i class="fas fa-home"></i> Select Room
</button>
<span>Current Room: <span id="roomName">chatroom</span></span>
</div>
监听房间选择操作
我们为按钮添加事件监听器,当用户选择新的房间时,我们将取消订阅当前房间并订阅新的房间。房间名将动态显示在页面上。
<script>
let currentRoom = "chatroom";
document.getElementById("roomSelectButton").addEventListener("click", () => {
const newRoom = prompt("Enter new room name:", currentRoom);
if (newRoom && newRoom !== currentRoom) {
currentRoom = newRoom;
client.unsubscribe(currentRoom); // 取消当前房间的订阅
client.subscribe(currentRoom); // 订阅新房间
document.getElementById("roomName").textContent = currentRoom; // 更新房间名
}
});
</script>
增加房间切换后的清空消息
当切换房间时,我们希望清空当前聊天室的消息,以便用户看到新的房间消息。我们可以在 subscribe
之后清空消息列表。
client.on("message", (topic, message) => {
const { username, data } = JSON.parse(message.toString());
const chat = document.getElementById("chat");
chat.innerHTML = ""; // 清空当前聊天内容
const messageDiv = document.createElement("div");
messageDiv.className = "message";
messageDiv.innerHTML = `<span class="username">${username}:</span> ${data}`;
chat.appendChild(messageDiv);
});
美化和响应式布局
为了让应用在不同设备上都能良好显示,我们使用 Bootstrap 提供的栅格系统来实现响应式布局。你可以
根据需要调整布局、按钮大小、字体等。
完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My First Cesium App</title>
<!-- 加载 Cesium 的 CSS 样式 -->
<link href="https://cesium.com/downloads/cesiumjs/releases/1.111/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.3.3/css/bootstrap.css">
<style>
/* 设置页面和Cesium容器的大小 */
html,
body,
#cesiumContainer {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
</style>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container">
<a class="navbar-brand" href="#">Touken的网站</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
aria-controls="navbarNav" aria-expanded="false" aria-label="切换导航">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button"
data-bs-toggle="dropdown" aria-expanded="false">
产品
</a>
<ul class="dropdown-menu" aria-labelledby="navbarDropdown">
<li><a class="dropdown-item" href="game.html">命悬一线</a></li>
<li><a class="dropdown-item" href="chat.html">聊天室</a></li>
<li><a class="dropdown-item" href="#">产品C</a></li>
</ul>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownBlog" role="button"
data-bs-toggle="dropdown" aria-expanded="false">
博客
</a>
<ul class="dropdown-menu" aria-labelledby="navbarDropdownBlog">
<li><a class="dropdown-item" href="#">博客文章1</a></li>
<li><a class="dropdown-item" href="#">博客文章2</a></li>
<li><a class="dropdown-item" href="#">博客文章3</a></li>
</ul>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownResources" role="button"
data-bs-toggle="dropdown" aria-expanded="false">
资源
</a>
<ul class="dropdown-menu" aria-labelledby="navbarDropdownResources">
<li><a class="dropdown-item" href="#">资源1</a></li>
<li><a class="dropdown-item" href="#">资源2</a></li>
<li><a class="dropdown-item" href="#">资源3</a></li>
</ul>
</li>
<li class="nav-item">
<a class="nav-link" href="#">关于我们</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">联系我们</a>
</li>
</ul>
</div>
</div>
</nav>
<!-- Cesium 地图容器 -->
<div id="cesiumContainer"></div>
<!-- 加载 Cesium 的 JavaScript 文件 -->
<script src="https://cesium.com/downloads/cesiumjs/releases/1.111/Build/Cesium/Cesium.js"></script>
<script>
Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJmY2YzY2RmNS1iNDQ5LTRkMDEtOGViZi05OGM3MTVjOWU1NGEiLCJpZCI6MjQ3Mzk2LCJpYXQiOjE3Mjg2NDUwOTB9.PFfVhduoFk35Pw05-XXbqPms0m3Fo5KHL0pYVvtyRqg';
const viewer = new Cesium.Viewer('cesiumContainer', {
homeButton: false,
sceneModePicker: false,
baseLayerPicker: false,
navigationHelpButton: false,
animation: false,
timeline: false,
fullscreenButton: false,
vrButton: false,
infoBox: true,
});
// 注册左键点击事件
viewer.screenSpaceEventHandler.setInputAction(function (movement) {
const cartesian = viewer.camera.pickEllipsoid(movement.position); // 使用 position 而不是 endPosition
if (cartesian) {
const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
const longitude = Cesium.Math.toDegrees(cartographic.longitude);
const latitude = Cesium.Math.toDegrees(cartographic.latitude);
alert(`经度: ${longitude.toFixed(6)}, 纬度: ${latitude.toFixed(6)}`);
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
// 隐藏商标
viewer._cesiumWidget._creditContainer.style.display = "none";
</script>
<script src="http://cdn.static.runoob.com/libs/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</body>
</html>
总结与展望
通过这篇教程,读者应该能够掌握 MQTT.js 的基础使用,理解 MQTT 协议的工作原理,并能够在自己的项目中有效地应用 MQTT.js。如果有任何问题,欢迎在评论区交流。
原文地址:https://blog.csdn.net/2303_80346267/article/details/143580477
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!