MQTT学习(三)--使用paho-mqtt和JQuery创建MQTT客户端

在之前的两篇文章中分解介绍了如何搭建MQTT服务器和MQTT桌面客户端,为了更好的体现MQTT多平台适应性,本篇文章将来说明如何使用paho-mqtt.js和JQuery.js来创建一个Web版的MQTT客户端。
在具体的需求上,仍与上篇中的WPF版MQTT客户端的需求保持一致。下面将直接说明实现过程。

MQTT学习(二)–使用MQTTNet在WPF框架下搭建MQTT客户端


1.引入paho-mqtt.js和JQuery.js

目前常见的关于MQTT协议的JavaScript库有MQTT.js、paho-mqtt.js等,其中MQTT.js是需要Node.js的相关环境的,因为本人计算机上暂时没有Node.js环境,所以这个示例选用paho-mqtt.js来实现。paho-mqtt.js的源码可以在GitHub上找到。

paho.mqtt.javascript

paho-mqtt.js的引入可以使用网络地址引入

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

考虑到有时需要在非互联网情况下开发测试,所以也可以将文件下载下来从本地引入。paho-mqtt.js可以从GitHub库里下载到本地。
JQuery.js是经常用到的库了。如何引入和使用方法就不再多做介绍了。对于这两个库我都是下载到本地项目引入的。

2.页面布局

页面布局和上一篇中的WPF版客户端基本保持一致,代码如下:

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title></title>
	<meta charset="utf-8" />
    <script src="Scripts/jquery.min.js"></script>
    <script src="Scripts/paho-mqtt.js"></script>
</head>
<body>
    <table>
        <tr>
            <td></td>
            <td><input id="txtIp" type="text" value="127.0.0.1"/></td>
            <td><input id="txtPort" type="text" value="61623"/></td>
            <td><input id="btnConnect" type="button" value="连接"/></td>
            <td><input id="btnDisconnect" type="button" value="断开" disabled="disabled"/></td>
            <td></td>
        </tr>
        <tr>
            <td></td>
            <td colspan="3">
                <div id="subTopics">
                </div>
            </td>
            <td><input id="btnSubscribe" type="button" value="订阅" disabled="disabled"/></td>
            <td></td>
        </tr>
        <tr>
            <td></td>
            <td colspan="4">
                <ul id="logResult"></ul>
            </td>
            <td></td>
        </tr>
        <tr>
            <td></td>
            <td>
                <select id="pubTopics">
                </select>
            </td>
            <td colspan="2"><input id="txtContent" type="text"/></td>
            <td><input id="btnPublish" type="button" value="发布" disabled="disabled"/></td>
            <td></td>
        </tr>
    </table>
</body>
</html>
    

呈现效果如下:
在这里插入图片描述

3.数据结构

同之前的桌面版客户端一样,我在JS脚本中定义了几个简单的数据结构。

        //所有主题
        var allTopics = [
            { "Topic": "/data/alarm", "Describe": "报警" },
            { "Topic": "/data/message", "Describe": "消息" },
            { "Topic": "/data/notify", "Describe": "通知" }
        ];

        //选中订阅主题
        var selectedTopics = [];

        //选中发布主题
        var currentTopic;

        //客户端选项
        var option = {
            "ServerUri": "127.0.0.1",
            "ServerPort": 61623,
            "UserName": "admin",
            "Password": "password",
            "ClientId": "",
            "TimeOut": 5,
            "KeepAlive": 100,
            "CleanSession": false,
            "SSL":false
        }

        //客户端
        var client;

4.数据初始化

数据初始化包括了订阅主题和发布主题的绑定,以及选择订阅主题和选择发布主题的事件。

        $(function () {
            BindSubTopics(allTopics);
            BindPubTopics(allTopics);

            //订阅主题选中事件
            $("#subTopics input[type=checkbox]").on("click", function () {
                var t = $(this).val();
                var topic;
                for(var i in allTopics){
                    var tmp = allTopics[i];
                    if (tmp.Topic == t) {
                        topic = tmp;
                    }
                }
                if ($(this).is(":checked")) {//选中
                    selectedTopics.push(topic);
                }
                else {//取消选择
                    if(selectedTopics.length>0){
                        for(var i in selectedTopics){
                            var tmp = selectedTopics[i];
                            if (tmp.Topic == t) {
                                selectedTopics.splice(i, 1);
                            }
                        }
                    }
                }
            });

            //发布主题选中事件
            $("#pubTopics").on("change", function () {
                var d = $("#pubTopics option:selected").text();
                var t = $("#pubTopics").val();
                currentTopic = { "Topic": t, "Describe": d }
                console.log(currentTopic);
            });

            //订阅按钮点击事件
            $("#btnSubscribe").on("click", function () {
                if (!client) {
                    alert("请连接服务端");
                    return;
                }
                if(selectedTopics.length==0){
                    alert("请选择要订阅的主题!");
                    return;
                }

                var msg = "";
                for(var i in selectedTopics){
                    var t = selectedTopics[i];
                    client.subscribe(t.Topic);
                    msg+=t.Topic+";"
                }
                WriteToStatus("成功订阅主题:" + msg);
            });

            //发布按钮点击事件
            $("#btnPublish").on("click", function () {
                if(!client){
                    alert("请连接服务端");
                    return;
                }
                if (!currentTopic) {
                    alert("请选择要发布的主题!");
                    return;
                }
                if($("#txtContent").val()==""){
                    alert("请输入要发布的内容");
                    return;
                }

                var message = new Paho.Message($("#txtContent").val());
                message.destinationName = currentTopic.Topic;
                client.send(message);

                WriteToStatus("发布了主题为" + currentTopic.Topic + "的消息:" + $("#txtContent").val())
            });
        });

        //绑定订阅主题
        function BindSubTopics(topics) {
            var html = "";
            for (var i = 0; i < topics.length;i++){
                var topic = topics[i];
                html += topic.Describe;
                html += '<input type="checkbox" value="'+topic.Topic+'"/>';
            }
            $("#subTopics").html(html);
        }

        //绑定发布主题
        function BindPubTopics(topics) {
            var html = "";
            for (var i = 0; i < topics.length; i++) {
                var topic = topics[i];
                html += '<option value="' + topic.Topic + '">' + topic.Describe + '</option>';
            }
            $("#pubTopics").html(html);
        }

5.MQTT事件

        $(function () {

            //连接按钮点击事件
            $("#btnConnect").on("click", function () {
                if ($("#txtIp").val()!="") {
                    option.ServerUri = $("#txtIp").val();
                }
                else {
                    alert("请输入服务端IP!");
                    return;
                }

                if($("#txtPort").val()!=""){
                    option.ServerPort = Number($("#txtPort").val());
                }
                else {
                    alert("请输入端口号!");
                    return;
                }
                //设置客户端标识
                option.ClientId = guid();
                //客户端实例化
                client = new Paho.Client(option.ServerUri, option.ServerPort, option.ClientId)
                client.onConnectionLost = onConnectionLost;//绑定连接断开事件
                client.onMessageArrived = onMessageArrived;//绑定接收消息事件
                //连接服务端
                client.connect({
                    invocationContext: {
                        host: option.ServerUri,//IP地址
                        port: option.ServerPort,//端口号
                        path: client.path,
                        clientId: option.ClientId//标识
                    },
                    timeout: option.TimeOut,//连接超时时间
                    keepAliveInterval: option.KeepAlive,//心跳间隔
                    cleanSession: option.CleanSession,//是否清理Session
                    useSSL: option.SSL,//是否启用SSL
                    userName: option.UserName,  //用户名
                    password: option.Password,  //密码
                    onSuccess: onConnect,//连接成功回调事件
                    onFailure: onError//连接失败回调事件
                });

            });

            //断开按钮点击事件
            $("#btnDisconnect").on("click", function () {
                client = null;

                enable($("#btnConnect"), true);
                enable($("#btnDisconnect"), false);
                enable($("#btnPublish"), false);
                enable($("#btnSubscribe"), false);
            });
        });

        //连接成功回调事件
        function onConnect() {
            WriteToStatus("连接成功!")

            enable($("#btnConnect"), false);
            enable($("#btnDisconnect"), true);
            enable($("#btnPublish"), true);
            enable($("#btnSubscribe"), true);
        }
        //连接失败回调事件
        function onError(e) {
            WriteToStatus("连接失败:" + e)

            enable($("#btnConnect"), true);
            enable($("#btnDisconnect"), false);
            enable($("#btnPublish"), false);
            enable($("#btnSubscribe"), false);
        }
        //连接断开事件
        function onConnectionLost(e) {
            if (e.errorCode !== 0) {
                WriteToStatus("连接异常断开:" + e.errorMessage);

                enable($("#btnConnect"), true);
                enable($("#btnDisconnect"), false);
                enable($("#btnPublish"), false);
                enable($("#btnSubscribe"), false);
            }
        }
        //接收消息事件
        function onMessageArrived(data) {
            WriteToStatus("收到消息:" + data.payloadString);
        }

6.相关辅助方法

        //状态输出
        function WriteToStatus(data) {
            var now = new Date();
            var message = '[' + now.toLocaleTimeString() + ']' + data;
            console.log(message);
            $("#logResult").append('<li>' + message + '</li>');
        }

        //生成GUID
        function guid() {
            function S4() {
                return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
            }
            return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4());
        }

        //切换按钮状态
        function enable(button,enabled) {
            if (enabled) {
                button.removeAttr("disabled");
            }
            else {

                button.attr("disabled", "disabled");
            }
        }

7.测试实例

代码编写完成后,启动运行来测试一下实际效果。启动Apollo并打开管理界面,分别打开三个Web客户端页面,并输入IP地址和端口,然后连接服务端。

注意:paho-mqtt.js默认使用的是WebSocket连接,所以端口号应使用配置中ws对应的端口号,而非tcp对应的端口号。

在服务端的管理界面,可以看到Connectors选项卡中的ws分组下有三个连接。说明连接成功,客户端运行正常。选择主题订阅、发布,数据的收发也都正常。
在这里插入图片描述

接下来再打开一个WPF版的客户端,连接服务端,订阅、收发消息也都正常。
在这里插入图片描述

经过上面的测试说明桌面客户端和Web客户端可以无缝连接,相互通信了。后续将考虑采用更多不同形式的终端来实现信息互通。


实践出真知。

源代码地址

  • 6
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
paho.mqtt.embedded-c是一个C语言实现的MQTT客户端库,它提供了MQTT协议的完整实现,可以运行在嵌入式系统中。如果你需要在C++项目中使用MQTT客户端,可以使用paho.mqtt.embedded-c库的C++封装,也就是paho.mqtt.cpp库。 paho.mqtt.cpp库是一个基于paho.mqtt.embedded-c库的C++封装,它提供了更加易用的C++ API,使得在C++项目中使用MQTT更加方便。 下面是一个使用paho.mqtt.cpp库实现MQTT客户端的示例代码: ```c++ #include <cstdlib> #include <iostream> #include <cstring> #include <chrono> #include <thread> #include "mqtt/async_client.h" const std::string SERVER_ADDRESS("tcp://localhost:1883"); const std::string CLIENT_ID("paho_cpp_async_subcribe"); const std::string TOPIC("hello"); class callback : public virtual mqtt::callback { public: virtual void connection_lost(const std::string& cause) override { std::cout << "Connection lost: " << cause << std::endl; } virtual void message_arrived(mqtt::const_message_ptr msg) override { std::cout << "Message arrived" << std::endl; std::cout << "Topic: " << msg->get_topic() << std::endl; std::cout << "Payload: " << msg->to_string() << std::endl; } virtual void delivery_complete(mqtt::delivery_token_ptr token) override { std::cout << "Delivery complete" << std::endl; } }; int main(int argc, char* argv[]) { mqtt::async_client client(SERVER_ADDRESS, CLIENT_ID); callback cb; client.set_callback(cb); mqtt::connect_options conn_opts; conn_opts.set_keep_alive_interval(20); conn_opts.set_clean_session(true); std::cout << "Connecting to the MQTT server..." << std::flush; try { mqtt::token_ptr conntok = client.connect(conn_opts); conntok->wait(); std::cout << "OK" << std::endl; } catch (const mqtt::exception& exc) { std::cerr << "\nERROR: Unable to connect to MQTT server: " << exc.what() << std::endl; return 1; } mqtt::token_ptr subtok = client.subscribe(TOPIC, 0); subtok->wait(); std::cout << "Subscribed to topic: " << TOPIC << std::endl; while (true) { std::this_thread::sleep_for(std::chrono::seconds(1)); } client.unsubscribe(TOPIC)->wait(); client.disconnect()->wait(); return 0; } ``` 在这个示例代码中,我们使用paho.mqtt.cpp库来连接到MQTT服务器,订阅一个主题,然后等待消息的到来。当消息到来时,我们会打印出来消息的主题和内容。 如果你需要在C++项目中使用MQTT客户端paho.mqtt.cpp库会是一个不错的选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值