近年来随着 Web 前端的快速发展,浏览器新特性层出不穷,愈来愈多的应用能够在浏览器端经过浏览器渲染引擎实现,Web 应用的即时通讯方式 WebSocket 也所以获得了普遍的应用。javascript
WebSocket 是一种在单个 TCP 链接上进行全双工通信的协议。WebSocket 通讯协议于2011年被 IETF 定为标准 RFC 6455,并由 RFC 7936 补充规范。WebSocket API 也被 W3C 定为标准。html
WebSocket 使得客户端和服务器之间的数据交换变得更加简单,容许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只须要完成一次握手,二者之间就直接能够建立持久性的链接,并进行双向数据传输。 ^1前端
MQTT 协议第 6 章 详细约定了 MQTT 在 WebSocket [RFC6455] 链接上传输须要知足的条件,协议内容不在此详细赘述。java
两款客户端比较
Paho.mqtt.js
Paho 是 Eclipse 的一个 MQTT 客户端项目,Paho JavaScript Client 是其中一个基于浏览器的库,它使用 WebSockets 链接到 MQTT 服务器。相较于另外一个 JavaScript 链接库来讲,其功能较少,不推荐使用。git
MQTT.js
MQTT.js 是一个彻底开源的 MQTT 协议的客户端库,使用 JavaScript 编写,可用于 Node.js 和浏览器。在 Node.js 端能够经过全局安装使用命令行链接,同时支持 MQTT/TCP、MQTT/TLS、MQTT/WebSocket 链接;值得一提的是 MQTT.js 还对微信小程序有较好的支持。github
本文将使用 MQTT.js 库进行 WebSocket 的链接讲解。web
安装 MQTT.js
若是读者机器上装有 Node.js 运行环境,可直接使用 npm 命令安装 MQTT.js。shell
在当前目录安装
npm install mqtt --save
CDN 引用
或免安装直接使用 CDN 地址npm
<script src="https://unpkg.com/mqtt/dist/mqtt.min.js"></script> <script> // 将在全局初始化一个 mqtt 变量 console.log(mqtt) </script>
链接至 MQTT 服务器
本文将使用 EMQ X 提供的 免费公共 MQTT 服务器,该服务基于 EMQ X 的 MQTT 物联网云平台 建立。服务器接入信息以下:小程序
- Broker: broker.emqx.io
- TCP Port: 1883
- Websocket Port: 8083
EMQ X 使用 8083 端口用于普通链接,8084 用于 SSL 上的 WebSocket 链接。
为了简单起见,让咱们将订阅者和发布者放在同一个文件中:
const clientId = 'mqttjs_' + Math.random().toString(16).substr(2, 8) const host = 'ws://broker.emqx.io:8083/mqtt' const options = { keepalive: 60, clientId: clientId, protocolId: 'MQTT', protocolVersion: 4, clean: true, reconnectPeriod: 1000, connectTimeout: 30 * 1000, will: { topic: 'WillMsg', payload: 'Connection Closed abnormally..!', qos: 0, retain: false }, } console.log('Connecting mqtt client') const client = mqtt.connect(host, options) client.on('error', (err) => { console.log('Connection error: ', err) client.end() }) client.on('reconnect', () => { console.log('Reconnecting...') })
链接地址
上文示范的链接地址能够拆分为: ws:
// broker
. emqx.io
: 8083
/mqtt
即 协议
// 主机名
. 域名
: 端口
/ 路径
初学者容易出现如下几个错误:
- 链接地址没有指明协议:WebSocket 做为一种通讯协议,其使用
ws
(非加密)、wss
(SSL 加密) 做为协议标识。MQTT.js 客户端支持多种协议,链接地址需指明协议类型; - 链接地址没有指明端口:MQTT 并未对 WebSocket 接入端口作出规定,EMQ X 上默认使用
8083
8084
分别做为非加密链接、加密链接端口。而 WebSocket 协议默认端口同 HTTP 保持一致 (80/443),不填写端口则代表使用 WebSocket 的默认端口链接;而使用标准 MQTT 链接时则无需指定端口,如 MQTT.js 在 Node.js 端可使用mqtt://localhost
链接至标准 MQTT 1883 端口,当链接地址是mqtts://localhost
则链接到 8884 端口; - 链接地址无路径:MQTT-WebSoket 统一使用
/path
做为链接路径,链接时需指明,在 EMQ X 上使用的路径为/mqtt
; - 协议与端口不符:使用了
wss
链接却链接到8083
端口; - 在 HTTPS 下使用非加密的 WebSocket 链接: Google 等机构在推动 HTTPS 的同时也经过浏览器约束进行了安全限定,即 HTTPS 链接下浏览器会自动禁止使用非加密的
ws
协议发起链接请求; - 证书与链接地址不符: 篇幅较长,详见下文 EMQ 启用 SSL/TLS 加密链接。
链接选项
上面代码中, options
是客户端链接选项,如下是主要参数说明,其他参数详见https://www.npmjs.com/package/mqtt#connect。
- keepalive:心跳时间,默认 60秒,设置 0 为禁用;
- clientId: 客户端 ID ,默认经过
'mqttjs_' + Math.random().toString(16).substr(2, 8)
随机生成; - username:链接用户名(可选);
- password:链接密码(可选);
- clean:true,设置为 false 以在离线时接收 QoS 1 和 2 消息;
- reconnectPeriod:默认 1000 毫秒,两次从新链接之间的间隔,客户端 ID 重复、认证失败等客户端会从新链接;
- connectTimeout:默认 30 * 1000毫秒,收到 CONNACK 以前等待的时间,即链接超时时间;
- will:遗嘱消息,当客户端严重断开链接时,Broker 将自动发送的消息。 通常格式为:
- topic:要发布的主题
- payload:要发布的消息
- qos:QoS
- retain:保留标志
订阅/取消订阅
链接成功以后才能订阅,且订阅的主题必须符合 MQTT 订阅主题规则;
注意 JavaScript 的异步非阻塞特性,只有在 connect 事件后才能确保客户端已成功链接,或经过 client.connected
判断是否链接成功:
client.on('connect', () => { console.log('Client connected:' + clientId) // Subscribe client.subscribe('testtopic', { qos: 0 }) })
// Unsubscribe client.unubscribe('testtopic', () => { console.log('Unsubscribed') })
发布/接收消息
发布消息到某主题,发布的主题必须符合 MQTT 发布主题规则,不然将断开链接。发布以前无需订阅该主题,但要确保客户端已成功链接:
// Publish client.publish('testtopic', 'ws connection demo...!', { qos: 0, retain: false })
// Received client.on('message', (topic, message, packet) => { console.log('Received Message: ' + message.toString() + '\nOn topic: ' + topic) })
微信小程序
MQTT.js 库对微信小程序特殊处理,使用 wxs
协议标识符。注意小程序开发规范中要求必须使用加密链接,链接地址应相似为 wxs://broker.emqx.io:8084/mqtt
。
EMQ X 启用 SSL/TLS 加密链接
EMQ 内置自签名证书,默认已经启动了加密的 WebSocket 链接,但大部分浏览器会报证书无效错误如 net::ERR_CERT_COMMON_NAME_INVALID
(Chrome、360 等 webkit 内核浏览器在开发者模式下, Console 选项卡 能够查看大部分链接错误)。致使该错误的缘由是浏览器没法验证自签名证书的有效性,读者需从证书颁发机构购买可信任证书,并参考该篇文章中的相应部分进行配置操做:EMQ X MQTT 服务器启用 SSL/TLS 安全链接。
这里就总结启用 SSL/TLS 证书须要具有的条件是:
- 将域名绑定到 MQTT 服务器公网地址:CA 机构签发的证书签名是针对域名的;
- 申请证书:向 CA 机构申请所用域名的证书,注意选择一个可靠的 CA 机构且证书要区分泛域名与主机名;
- 使用加密链接的时候选择
wss
协议,并 使用域名链接 :绑定域名-证书以后,必须使用域名而非 IP 地址进行链接,这样浏览器才会根据域名去校验证书以在经过校验后创建链接。
EMQ X 配置
打开 etc/emqx.conf
配置文件,修改如下配置:
# wss 监听地址 listener.wss.external = 8084 # 修改密钥文件地址 listener.wss.external.keyfile = etc/certs/cert.key # 修改证书文件地址 listener.wss.external.certfile = etc/certs/cert.pem
完成后重启 EMQ X 便可。
可使用你的证书与密钥文件直接替换到 etc/certs/ 下。
在 Nginx 上配置反向代理与证书
使用 Nginx 来反向代理并加密 WebSocket 能够减轻 EMQ X 服务器计算压力,同时实现域名复用,同时经过 Nginx 的负载均衡能够分配多个后端服务实体。
# 建议 WebSocket 也绑定到 443 端口 listen 443, 8084; server_name example.com; ssl on; ssl_certificate /etc/cert.crt; # 证书路径 ssl_certificate_key /etc/cert.key; # 密钥路径 # upstream 服务器列表 upstream emq_server { server 10.10.1.1:8883 weight=1; server 10.10.1.2:8883 weight=1; server 10.10.1.3:8883 weight=1; } # 普通网站应用 location / { root www; index index.html; } # 反向代理到 EMQ X 非加密 WebSocket location / { proxy_redirect off; # upstream proxy_pass http://emq_server; proxy_set_header Host $host; # 反向代理保留客户端地址 proxy_set_header X-Real_IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr:$remote_port; # WebSocket 额外请求头 proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection “upgrade”; }
其它资源
项目完整代码请见:https://github.com/emqx/MQTT-Client-Examples/tree/master/mqtt-client-WebSocket
一款在线的 MQTT WebSocket 链接测试工具:https://www.emqx.io/cn/mqtt/mqtt-websocket-toolkit
版权声明: 本文为 EMQ 原创,转载请注明出处。
原文连接:https://www.emqx.io/cn/blog/connect-to-mqtt-broker-with-websocket