前端能力的演进带来了更先进的浏览器功能,显著提升了Web端的用户交互与数据通信。 其中WebSocket是关键:作为一种可靠的通信协议,它重新定义了Web应用中的实时双向通信。
同时,MQTT以其轻量结构和高可靠性著称,尤其适合资源受限、网络不稳定的环境。 该协议已成为IoT生态中消息传输的基石。 其低开销消息传递特性与Web端高效通信需求相契合, 尤其适用于移动设备以及传感器监控、关键通知等需要即时数据交换的场景。
MQTT over WebSocket不仅是技术组合,更是网络通信方式的转变。 它让MQTT消息可直接在浏览器中收发,充分发挥MQTT与WebSocket的优势。 这种结合带来丰富可能:展示实时设备信息、接收实时告警、在移动Web应用中进行高效通信等。
WebSocket简介
WebSocket是一种通信协议,使Web客户端与服务器通过一条长连接实现实时双向通信。 它明显不同于传统HTTP请求/响应模式,支持持续、低延迟的数据交换。 通过HTTP升级握手建立,支持未加密(ws://)和加密(wss://)连接。 WebSocket是需实时内容更新的Web应用的重要基础,提供高效、持久的通信通道。
MQTT over WebSocket的优势
MQTT与WebSocket的结合为Web端IoT应用带来诸多优势。 通过MQTT over WebSocket,传统非Web的MQTT协议可有效延伸到Web应用。 该适配很有必要,因为Web浏览器本身不支持MQTT,需借助WebSocket作为桥梁。
该结合的主要特点包括:
- 资源利用高效:MQTT为轻量协议,适合带宽或设备能力有限的场景。在WebSocket上使用时,可在Web应用场景下高效利用网络与设备资源。
- 实时通信:WebSocket提供持久连接,实现低延迟实时数据交换。
- 双向通信:WebSocket支持全双工,可同时双向传输数据。结合MQTT发布/订阅模型,实现动态、可交互的通信场景。
- 与Web生态兼容:通过MQTT over WebSocket,协议可与浏览器安全模型兼容,便于接入Web应用,而无需额外插件或特殊配置。
- 保留MQTT特性:集成不牺牲MQTT原生功能(如保留消息、遗嘱、clean session),保障功能完整可靠。
TBMQ中的MQTT over WebSocket
TBMQ使用WS(WebSocket)和WSS(WebSocket Secure)两个监听器支持WebSocket通信。 可在此查看监听器概览。
WebSocket相关参数详情请参阅此处
(查找 LISTENER_WS_ENABLED 及相关环境变量)。
快速开始
本指南演示如何使用 MQTT.js 库连接TBMQ、订阅主题并收发消息的示例。
安装TBMQ
开始前,请确保TBMQ已成功安装。 不同平台安装步骤请参阅安装选项文档。
本指南使用以下快速安装说明。
|
对于已安装 Docker 的 Linux 或 macOS 用户,建议执行以下命令: |
|
对于已安装 Docker Desktop 的 Windows 用户,建议按以下说明执行。 注意: 请确保下载的 PowerShell 脚本可在您的系统上运行。
|
安装MQTT WebSocket客户端
安装MQTT.js前,请确保已安装Node.js运行环境。 安装Node.js可参考此处。
安装Node.js后,可使用 npm 或 yarn 安装MQTT.js库。
1
npm install mqtt --save
1
yarn add mqtt
若需全局安装,可使用npm的 -g 参数或yarn的 global 选项。
1
npm install -g mqtt
1
yarn global add mqtt
注意:全局安装可能引发版本冲突,请谨慎使用。
连接客户端
连接选项、订阅主题及发布消息的详细说明请参阅 MQTT.js文档。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
const mqtt = require('mqtt');
const url = 'ws://localhost:8084/mqtt';
const topic = 'sensors/temperature';
const message = 'Hello World';
const options = {
clean: true,
clientId: 'tbmq_websockets_client_id',
username: 'tbmq_websockets_username',
password: null
};
console.log('Connecting client...');
const client = mqtt.connect(url, options); // connect client
client.on('connect', function () {
console.log('Client connected!');
client.subscribe(topic, function (error) { // subscribe to a topic
if (!error) {
client.publish(topic, message); // publish a message
}
});
});
client.on('message', (topic, message) => { // handle received messages
console.log(`Received message. Payload: ${message.toString()}. Topic: ${topic}`);
client.end(); // end client session
});
client.on('error', (error) => { // handle errors
console.log('Error: ', error?.message);
});
client.on('packetreceive', (packet) => { // handle received packet
console.log('Packet receive...', packet);
});
client.on('packetsend', (packet) => { // handle sent packet
console.log('Packet send...', packet);
});
client.on('reconnect', () => {
console.log('Reconnecting...');
});
client.on('close', () => {
console.log('Closing client...');
});
请将以上代码保存到名为ws_example.js的文件中,然后执行它。
1
node ws_example.js
上述示例建立了一个WebSocket客户端并连接到TBMQ。 成功连接后,客户端订阅’sensors/temperature’主题。 成功订阅后,客户端向同一主题发布消息。 随后,在收到该消息后,客户端断开连接,有效关闭链接。
以下是执行ws_example.js文件的输出:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
Connecting client...
Packet receive... Packet {
cmd: 'connack',
retain: false,
qos: 0,
dup: false,
length: 2,
topic: null,
payload: null,
sessionPresent: false,
returnCode: 0
}
Client connected!
Packet send... {
cmd: 'subscribe',
subscriptions: [ { topic: 'sensors/temperature', qos: 0 } ],
messageId: 64109
}
Packet receive... Packet {
cmd: 'suback',
retain: false,
qos: 0,
dup: false,
length: 3,
topic: null,
payload: null,
granted: [ 0 ],
messageId: 64109
}
Packet send... {
cmd: 'publish',
topic: 'sensors/temperature',
payload: 'Hello World',
qos: 0,
retain: false,
messageId: 0,
dup: false
}
Packet receive... Packet {
cmd: 'publish',
retain: false,
qos: 0,
dup: false,
length: 32,
topic: 'sensors/temperature',
payload: <Buffer 48 65 6c 6c 6f 20 57 6f 72 6c 64>
}
Received message. Payload: Hello World. Topic: sensors/temperature
Packet send... { cmd: 'disconnect' }
Closing client...
此外,你可以使用WebSocket客户端订阅主题并接收消息,以验证结果。

连接详情
URL ws://localhost:8084/mqtt由以下几个部分组成:
- ws://:指定WebSocket协议方案。可以是ws(非加密连接)或wss(加密连接)。
- localhost:指运行TBMQ的本地机器的主机名。如适用,可替换为DNS主机名。
- 8084:WebSocket服务器监听传入连接的端口号。
- /mqtt:TBMQ用于MQTT over WebSocket的必需路径。选择’/mqtt’作为路径是基于MQTT规范。
MQTT over WebSocket Secure (WSS)
在TBMQ中使用MQTT over WebSocket Secure (WSS)为数据提供增强的安全性。 它加密通信,确保设备与broker之间发送的信息免受未授权访问。
在TBMQ中使用MQTT over WebSocket Secure时,理解知名证书颁发机构(CA)签发的证书与自签名证书之间的区别至关重要。 知名CA签发的证书提供更高级别的信任,并被客户端和浏览器广泛认可。 这使其成为面向公众应用的理想选择,因为它们向用户保证连接是安全的,且服务器已通过受信任的权威机构认证。
另一方面,自签名证书可用于内部或测试目的。 虽然它们提供与CA签发证书相同级别的加密,但缺少来自公认CA的信任背书。 这意味着客户端连接到服务器时可能会收到安全警告。 自签名证书对开发或私有网络来说经济实惠,但由于最终用户的信任问题,不建议用于公共或生产环境。
总之,为了在TBMQ中获得最大的安全性和用户信任,最好对公共部署使用知名CA签发的证书, 而自签名证书适合内部或开发环境。
双向认证,也称为双向TLS/SSL认证,涉及客户端和服务器通过证书链相互验证身份。 虽然这是一种强健的安全措施,但在浏览器环境中使用WSS实现时会面临挑战。 在典型的浏览器场景中,服务器向客户端出示其证书,浏览器根据受信任CA列表进行验证。 这是标准的单向SSL认证过程。
然而,对于双向SSL认证,客户端还需要向服务器出示证书。 浏览器环境中的挑战在于浏览器并不普遍支持WebSocket连接的客户端证书。 这种支持的缺失源于浏览器安全模型和用户界面的复杂性, 使得无缝实现WSS的客户端证书处理变得困难。 此外,在浏览器环境中管理客户端证书可能很繁琐,并带来用户体验挑战。
因此,虽然双向认证在技术上是可行的且高度安全,但在浏览器中使用WSS的实际实现是有限的,通常不可行。 因此,对于使用WSS的Web应用程序,通常使用单向SSL认证配合额外的安全层(如API密钥或OAuth令牌)来确保安全通信。
在非浏览器环境(如Node.js)以及Python和Java等编程语言中,使用适当的MQTT库时, 双向认证可以无缝运行,仍然是一种非常有效的安全措施。
让我们看看示例。请确保WSS监听器已正确启用和配置。
要建立双向认证连接,请确保已创建类型为’X.509 Certificate Chain’的MQTT客户端凭据,
并指定客户端证书的通用名称(CN)。请参阅此指南获取详细说明。
将example.com替换为你的实际DNS,将/path/to/your/client/key/file.pem、/path/to/your/client/cert/file.pem
和/path/to/your/ca/cert/file.pem替换为相应的证书文件路径。
或者,如果你倾向于在以下示例中通过’Basic’凭据(单向认证)进行认证,可以将options.username设置为’tbmq_websockets_username’而不是’null’。 此外,你需要注释掉设置options的行,如’options.key’、’options.cert’以及其他与客户端证书相关的行。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
const mqtt = require('mqtt');
const fs = require('fs');
const url = 'wss://example.com:8085/mqtt';
const topic = 'sensors/temperature';
const message = 'Hello World';
// File paths
const keyFile = '/path/to/your/client/key/file.pem';
const certFile = '/path/to/your/client/cert/file.pem';
const caFile = '/path/to/your/ca/cert/file.pem';
const options = {
clean: true,
clientId: 'tbmq_websockets_client_id',
username: null,
password: null
};
try {
const dataKey = fs.readFileSync(keyFile);
const dataCert = fs.readFileSync(certFile);
const dataCa = fs.readFileSync(caFile);
// Set the certificate and key options
options.key = dataKey;
options.cert = dataCert;
options.ca = dataCa;
options.rejectUnauthorized = true;
console.log('Connecting client...');
const client = mqtt.connect(url, options); // connect client
client.on('connect', function () {
console.log('Client connected!');
client.subscribe(topic, function (error) { // subscribe to a topic
if (!error) {
client.publish(topic, message); // publish a message
}
});
});
client.on('message', (topic, message) => { // handle received messages
console.log(`Received message. Payload: ${message.toString()}. Topic: ${topic}`);
client.end(); // end client session
});
client.on('error', (error) => { // handle errors
console.log('Error: ', error?.message);
});
client.on('packetreceive', (packet) => { // handle received packet
console.log('Packet receive...', packet);
});
client.on('packetsend', (packet) => { // handle sent packet
console.log('Packet send...', packet);
});
client.on('reconnect', () => {
console.log('Reconnecting...');
});
client.on('close', () => {
console.log('Closing client...');
});
} catch (err) {
console.error('Error reading certificate file:', err);
}
请将以上代码保存到名为wss_example.js的文件中,然后执行它。
1
node wss_example.js
成功执行wss_example.js文件后,你应该能看到与ws_example.js类似的输出。
总结
在本指南中,我们探讨了MQTT与WebSocket的强大组合,这是一种以高效性和灵活性增强IoT通信的解决方案。 我们深入了解了MQTT over WebSocket如何提供在Web环境中无缝运行的实时双向通信通道,确保数据快速可靠地交换。 在安全性方面,我们讨论了SSL/TLS加密的实现以保障安全数据传输,以及知名CA签发证书与自签名证书的区别。 此外,我们还涉及了实际方面,包括代码示例和浏览器环境中双向认证的限制。 本指南旨在为任何希望实现MQTT over WebSocket的人提供全面的参考资源,确保高效、安全且可靠的IoT通信系统。