mosquitto 与 websocket 的结合

前言

mosquitto 做为一个消息代理, 客户端与 mosquitto 服务端的通讯时基于 MQTT 协议的, 而如今的主流 web 应用时呈如今浏览器中, 这意味着用户与服务端只能经过 HTTP 或者 HTTPS 这类浏览器能理解的协议传输, 因此后端还要创建一个代理层, 将 HTTP 协议传输的内容解析一下以 MQTT 协议发送到 mosquitto, 最后再由 mosquitto 发送到硬件端. javascript

在浏览器支持的协议中, 还有一个适用于长链接的 WS 协议, 参考: 浏览器中常见网络协议介绍. 若是客户端直接经过 websocket 链接到 mosquitto 端, 那么就不须要中间的后端代理层. 后端只须要一个推送服务和控制系统就能够实现对客户端的监听, 控制, 推送(固然, 若是业务量巨大, 业务逻辑复杂, 代理层仍是有必要的, 由于这样不但能够为 mosquitto 过滤一些没必要要的业务, 并且能够作一些数据统计, 设计事件钩子).html

在编译 mosquitto 和它的验证插件 mosquitto-auth-plug 的时候, 我注意到 mosquitto 有一个监听的 9001 端口, 过后查了一下, 发现 mosquitto 开放的这个端口是支持直接与客户端进行 websocket 通讯的.java

启用 mosquitto websocket 模式

其实纯粹的 MQTT 服务器是没有这个功能的, mosquitto 须要在编译的时候设置 configure.mkweb

WITH_WEBSOCKETS := yes

所幸的是, eclipse 官方的 docker 镜像 已经支持了 websocket 通讯方式. 只须要在 mosquitto.conf 中启用:ajax

port 1883
listener 9001
protocol websockets

使用 paho-mqtt.js 经过 websocket 与 mosquitto 通讯

eclipse 提供了用于 浏览器客户端利用 javascript 和 mosquitto 进行 websocket 通讯的 paho-mqtt.js, 这是这个 js 库的文档: Paho.MQTT DOC.docker

客户端与服务端的双通道通讯

咱们假设有客户端用户为 client, 服务端用户为 server. 为他们分配的主题与权限为:json

id | username |      topic         | rw 
----+----------+--------------------+----
  1 | client   | /p/client/upload   |  2
  2 | server   | /p/client/upload   |  2
  3 | client   | /p/client/download |  1
  4 | server   | /p/client/download |  2

那么 server 和 client 都可以在 /p/client/upload 读写. 咱们约定只有 client 在 /p/client/upload 写, server 只读 /p/client/upload, 这个 topic 是用于 client 向 server 发送消息.小程序

对于 topic /p/client/download, server 可读可写, client 只读, 这个 topic 是用于 client 接受 server 向下推送的消息, 具体的逻辑以下:
![图片描述后端

注意这里的 client 是对 /p/client/upload 具备可读可写权限的, 意味者它能够读取来自 /p/client/upload 的信息, 可是实际上这里没有人往这个主题发布消息(除非 client 本身往这里发消息), 因此 client 拥有可读权限也没有关系.微信小程序

客户端的 javascript 通讯例子

建议看一下这篇文章: Using MQTT Over WebSockets with Mosquitto, 它比较详细地介绍了客户端经过 websocket 于 mosquitto 服务器通讯的方式.
好比对于 client.html:
先引入 paho-mqtt.js, 这里咱们先用 cloudflare.com CDN上的这段 js:

<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.2/mqttws31.js"></script>

下面是建立客户端 websocket 的例子:

var mqtt;
var host = 'mosquitto';
var port = 9001;

// onConnect 事件
function onConnect() {
    console.log('connected.');
    var raw_message = 'Hello World!';
    message = new Paho.MQTT.Message(raw_message);
    message.destinationName = '/p/client/upload';
    console.log('sending message: ' + raw_message );
    mqtt.send(message);

    // 订阅 download topic
    var subOptions = {
        qos: 1,
        onSuccess: onSubscribe
    };
    mqtt.subscribe('/p/client/download', subOptions);
}

// 订阅主题成功事件
function onSubscribe(context) {
    console.log('subscribe success');
    console.log(context);
}

// 链接失败事件
function onFailure(message) {
    console.log('connect failed.');
}

// onMessageArrived 事件
function onMessageArrived(message) {
    console.log('new message arrived...');
    console.log(message.payloadString);
}


// 创建 MQTT websocket 链接
function MQTTconnect() {
    console.log('connecting to ' + host + ':' + port);
    mqtt = new Paho.MQTT.Client(host, port, 'clientid');
    var options = {
        timeout: 3,
        onSuccess: onConnect,
        onFailure: onFailure,
        userName: 'client',
        password: '123456',
        mqttVersion: 4
    };
    mqtt.onMessageArrived = onMessageArrived;
    mqtt.connect(options);
}

这里咱们利用 paho-mqtt.js 新建了一个 mqtt, 而后绑定 onSuccess(链接成功), onFailure(链接失败) onMessageArrived(消息到来)等事件, 以后用 options 里的配置链接到远程的 mosquitto 服务器. 链接成功后, client 向 server 发送一条消息到 /p/client/upload topic, 通知服务端已经创建链接. 发送以后 mqtt 又订阅 /p/client/download, 准备接受来自服务端的信息.

好比, 服务端能够这样向客户端推送消息:

import paho.mqtt.publish as publish
import time

HOST = 'mosquitto'
PORT = 1883


if __name__ == '__main__':
    client_id = time.strftime('%Y%m%d%H%M%S', time.localtime(time.time()))
    publish.single(
        '/p/client/download', 'hello mqtt', qos=2, hostname=HOST, port=PORT,
        client_id=client_id, auth={'username': 'server', 'password': '123456'})

这就是基于双通道的服务端于客户端通讯

客户端与服务端的单通道通讯

单通道通讯原理相似, 可是因为客户端可能有多台设备, 好比手机端, 微信小程序端, PC 端. 那么身份的验证不能直接由 mosquitto 肯定, 应该在消息体内肯定. 好比咱们用 json 做为消息的承载方式. 消息体能够这样:

{
    "timestamp": "1539141568",
    "client_id": "1001001",
    "message_type": "ping",
    "data": {"detail": "content"},
    "token": "auth_token"
}

这里应该按照具体的业务需求进行鉴权设计.

相关文章
相关标签/搜索