C#实现MQTT over WebSocket

如何在网页端实现MQTT消息的发布和订阅?

  • 实现MQTT功能,可以发布和订阅主题
  • 通过WebSocket协议将MQTT消息转发给对应的网页端

带着这个实现思路,采用C#控制台程序实现MQTT服务端功能,web端可以直接使用websocket插件与服务端双向通讯。

  1. 新建C#控制台程序,.net framework框架版本4.5.2
  2. 引用Fleck框架,版本1.2.0
  3. 引用MQTTnet框架,版本4.3.1.873

1、WebSocket功能实现

socketDic 字典key值记录每个客户端,value记录每个客户端对应的SN,代表设备的序列号;

每当有网页端客户端连接的时候,自动将客户端套接字加入字典中;

每当网页关闭,服务端自动断开客户端套接字连接,并从缓存字典中移除此客户端套接字;

当网页端客户端套接字发送包含SN:开头的字符,表示网页端发送了设备的SN,服务端接收此消息,并解析设备SN,并与对应的客户端套接字绑定对应关系,在缓存字典中维护对应关系。

class Server
{
    static Dictionary<IWebSocketConnection, string> socketDic = new Dictionary<IWebSocketConnection, string>();
        static void Main()
        {
            FleckLog.Level = LogLevel.Debug;
            var server = new WebSocketServer("ws://0.0.0.0:8181");
            //var socketDic = new Dictionary<IWebSocketConnection, string>();
            server.Start(socket =>
                {
                    socket.OnOpen = () =>
                        {
                            Console.WriteLine("Open!");
                            socketDic.Add(socket, null);
                        };
                    socket.OnClose = () =>
                        {
                            Console.WriteLine("Close!");
                            socketDic.Remove(socket);
                        };
                    socket.OnMessage = message =>
                        {
                            if (message.Contains("SN:") && socketDic.ContainsKey(socket))
                            {
                                socketDic[socket] = message.Replace("SN:", "");
                            }
                        };
                });
            Client();
            var input = Console.ReadLine();
            while (input != "exit")
            {
                foreach (var socket in socketDic.ToList())
                {
                    socket.Key.Send(input);
                }
                input = Console.ReadLine();
            }

        }
}

2、MQTT功能实现

MQTT服务器采用EMQX公共服务器测试,服务器域名:broker.emqx.io,端口:1883,用户名:emqx_test,密码:emqx_test

订阅主题名:123、12345

MQTT接收消息事件ApplicationMessageReceivedAsync,判断消息主题与缓存字典中的套接字是否对应,如消息主题与缓存字典SN存在包含关系,则认为此消息与网页端套接字存在关联关系,则将消息推送给此套接字。

具体实现代码:

static IMqttClient client;
        public static async void Client()
        {
            try
            {
                client = new MqttFactory().CreateMqttClient() as MqttClient;
                var build = new MqttClientOptionsBuilder()
                    .WithClientId(Guid.NewGuid().ToString().Replace("-", "").ToUpper())
                    .WithCredentials("emqx_test", "emqx_test")
                    .WithTcpServer("broker.emqx.io", 1883)
                    .WithCleanSession(true)
                    .WithKeepAlivePeriod(TimeSpan.FromSeconds(100.5));
                client.ConnectedAsync += _mqttClient_ConnectedAsync;
                await client.ConnectAsync(build.Build());
                
                client.DisconnectedAsync += _mqttClient_DisconnectedAsync;
                client.ApplicationMessageReceivedAsync += _mqttClient_ApplicationMessageReceivedAsync;
 
                await client.SubscribeAsync(
                    new MqttClientSubscribeOptions
                    {
                        TopicFilters = new List<MqttTopicFilter> {new MqttTopicFilter() //订阅消息对象
                        {
                            Topic = "123",  //订阅消息主题
                            QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce  //消息类型
                        },new MqttTopicFilter() //订阅消息对象
                        {
                            Topic = "12345",  //订阅消息主题
                            QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce  //消息类型
                        }
                    }
                    });
            }
            catch (Exception e)
            {
                Console.WriteLine($"连接失败");
            }
        }

        private static Task _mqttClient_ApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs arg)
        {
            var msg = arg.ApplicationMessage.ConvertPayloadToString();
            foreach (var item in socketDic)
            {
                if (item.Value!=null && arg.ApplicationMessage.Topic.Contains(item.Value))
                {
                    item.Key.Send($"hello {item.Value},{msg}");
                }
            }
            Console.WriteLine(arg.ApplicationMessage.ConvertPayloadToString());
            return Task.FromResult("");
        }

        private static Task _mqttClient_DisconnectedAsync(MqttClientDisconnectedEventArgs arg)
        {
            Console.WriteLine($"客户端“{client.Options.ClientId}”已断开MQTT服务器!");
            return Task.FromResult("");
        }

        private static Task _mqttClient_ConnectedAsync(MqttClientConnectedEventArgs arg)
        {
            Console.WriteLine($"客户端“{client.Options.ClientId}”已连接MQTT服务器!");
            return Task.FromResult("");
        }

3、网页端实现

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
    <title>websocket client</title>
    <script type="text/javascript">
        var start = function () {
            var inc = document.getElementById('incomming');
            var wsImpl = window.WebSocket || window.MozWebSocket;
            var form = document.getElementById('sendForm');
            var input = document.getElementById('sendText');
            
            inc.innerHTML += "connecting to server ..<br/>";

            // create a new websocket and connect
            window.ws = new wsImpl('ws://localhost:8181/');

            // when data is comming from the server, this metod is called
            ws.onmessage = function (evt) {
                inc.innerHTML += evt.data + '<br/>';
            };

            // when the connection is established, this method is called
            ws.onopen = function () {
                inc.innerHTML += '.. connection open<br/>';
                ws.send("SN:12345");
            };

            // when the connection is closed, this method is called
            ws.onclose = function () {
                inc.innerHTML += '.. connection closed<br/>';
            }

			//form.addEventListener('submit', function(e){
			//	e.preventDefault();
			//	var val = input.value;
			//	ws.send(val);
			//	input.value = "";
			//}); 
        }
        window.onload = start;
    </script>
</head>
<body>
	<form id="sendForm">
		<input id="sendText" placeholder="Text to send" />
	</form>
    <pre id="incomming"></pre>
</body>
</html>

4、测试

启动WebSocket服务器控制台应用程序;

运行MQTT.fx工具,新建连接

给12345主题发布消息,

观察网页端是否收到消息,

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/252837.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

力扣刷题-二叉树-二叉树的所有路径

257 二叉树的所有路径 给定一个二叉树&#xff0c;返回所有从根节点到叶子节点的路径。 说明: 叶子节点是指没有子节点的节点。 示例: 思路 参考&#xff1a; https://www.programmercarl.com/0257.%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E6%89%80%E6%9C%89%E8%B7%AF%E5%BE…

MNIST内置手写数字数据集的实现

torchvision库 torchivision库是PyTorch中用来处理图像和视频的一个辅助库&#xff0c;接下来我们就会使用torchvision库加载内置的数据集进行分类模型的演示 为了统一数据加载和处理代码&#xff0c;PyTorch提供了两个类用于处理数据加载&#xff0c;他们分别是torch.utils.…

进程通信知识基础【Linux】——下篇

目录 前文 一&#xff0c;命名管道 创建命名管道 1. getline——c库 2. unlink——系统接口 实践代码 common.hpp client.cpp server.cpp Log.cpp 二&#xff0c;共享内存&#xff08;system V接口&#xff09; 1. 创建共享内存 shmget接口 2. 删除共享内存 常见…

PMP项目管理 - 相关方管理

系列文章目录 PMP项目管理 - 质量管理 PMP项目管理 - 采购管理 PMP项目管理 - 资源管理 PMP项目管理 - 风险管理 PMP项目管理 - 沟通管理 现在的一切都是为将来的梦想编织翅膀&#xff0c;让梦想在现实中展翅高飞。 Now everything is for the future of dream weaving wing…

【一种用opencv实现高斯曲线拟合的方法】

背景&#xff1a; 项目中需要实现数据的高斯拟合&#xff0c;进而提取数据中标准差&#xff0c;手头只有opencv库&#xff0c;经过资料查找验证&#xff0c;总结该方法。 基础知识&#xff1a; 1、opencv中solve可以实现对矩阵参数的求解&#xff1b; 2、线的拟合就是对多项…

【深度强化学习】确定性策略梯度算法 DDPG

前面讲到如 REINFORCE&#xff0c;Actor-Critic&#xff0c;TRPO&#xff0c;PPO 等算法&#xff0c;它们都是随机性策略梯度算法&#xff08;Stochastic policy&#xff09;&#xff0c;在广泛的任务上表现良好&#xff0c;因为这类方法鼓励了算法探索&#xff0c;给出的策略是…

探索 Vim:一个强大的文本编辑器

引言&#xff1a; Vim&#xff08;Vi IMproved&#xff09;是一款备受推崇的文本编辑器&#xff0c;拥有强大的功能和高度可定制性&#xff0c;提供丰富的编辑和编程体验。本文将探讨 Vim 的基本概念、使用技巧以及为用户带来的独特优势。 简介和发展 1. Vim 的简介和历史 V…

【二分查找】自写二分函数的总结

作者推荐 【动态规划】【广度优先搜索】LeetCode:2617 网格图中最少访问的格子数 本文涉及的基础知识点 二分查找算法合集 自写二分函数 的封装 我暂时只发现两种&#xff1a; 一&#xff0c;在左闭右开的区间寻找最后一个符合条件的元素&#xff0c;我封装成FindEnd函数。…

力扣刷题-二叉树-平衡二叉树

110 平衡二叉树 给定一个二叉树&#xff0c;判断它是否是高度平衡的二叉树。 本题中&#xff0c;一棵高度平衡二叉树定义为&#xff1a;一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。 示例 1: 给定二叉树 [3,9,20,null,null,15,7] 返回 true 。 给定二叉树 [1…

AUTOSAR ComM模块配置以及代码

ComM模块配置以及代码执行流程 1、基本的一个通道的配置列表 ComMNmVariant 概念的个人理解&#xff1a; FULL&#xff1a; 完全按照AUTOSAR NM方式进行调用 LIGHT &#xff1a;设置一个超时时间&#xff0c;在请求停止通信的时候开始计时&#xff0c;超时之后才会进入FULLCOM…

运维实践|采集MySQL数据出现many connection errors

文章目录 问题出现问题分析当前环境问题分析 解决方案1 检查调度事件任务是否开启2 开启调度事件任务3 创建一张日志表4 创建函数存储过程5 创建事件定时器6 开启事件调度任务7 检查核实是否创建 总结 问题出现 最近在做OGG结构化数据采集工作&#xff0c;在数据采集过程中&am…

将博客搬至微信公众号了

一、博客搬家通知 各位码友们好&#xff0c;大家是不是基本很少看 CSDN 了呢&#xff1f;平时开发是不都依靠着 chatGPT 来解决工作中的技术问题了&#xff0c;不过我觉得在工作中的使用场景各式各样的&#xff0c;具体问题还得自己具体去梳理逻辑&#xff0c;再考虑用什么样的…

C语言:求和1+1/2-1/3+1/4-1/5+……-1/99+1/100

#include<stdio.h> int main() {int i 0;double sum 0.0;int flag 1;for (i 1;i < 100;i){sum 1.0 / i * flag;flag -flag;}printf("sum%lf\n", sum);return 0; }

SpringIOC之@Primary

博主介绍&#xff1a;✌全网粉丝5W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战&#xff0c;博主也曾写过优秀论文&#xff0c;查重率极低&#xff0c;在这方面有丰富的经验…

力扣刷题-二叉树-找树左下角的值

513 找树左下角的值 给定一个二叉树的 根节点 root&#xff0c;请找出该二叉树的 最底层 最左边 节点的值。 假设二叉树中至少有一个节点。 示例 1&#xff1a; 示例 2&#xff1a; 思路 层序遍历 直接层序遍历&#xff0c;因为题目说了是最底层&#xff0c;最左边的值&a…

【数据结构—队列的实现】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言 一、队列 1.1队列的概念及结构 二、队列的实现 2.1头文件的实现—Queue.h 2.2源文件的实现—Queue.c 2.3源文件的测试—test.c 三、测试队列实际数据的展示 3.…

mysql使用st_distance_sphere函数报错Incorrect arguments to st_distance_sphere

前言 最近使用空间点位查询数据时函数报错Incorrect arguments to st_distance_sphere报错。 发现问题 因为之前是没有问题的&#xff0c;所以把问题指向了数据&#xff0c;因为是外部数据&#xff0c;不是通过系统打点获取&#xff0c;发现是因为经纬度反了&#xff0c;loc…

VRRP(虚拟路由冗余协议)

一.VRRP简介 1.VRRP是什么 Virtual route Redundancy Protocol&#xff0c;也叫虚拟路由器冗余协议。 利用VRRP&#xff0c;一组路由器协同工作&#xff0c;单只有一个处于Master状态&#xff0c;处于该状态的路由器&#xff08;的接口&#xff09;承担实际的数据流量转发任…

MapReduce序列化实例代码

1 &#xff09;需求&#xff1a;统计每个学号该月的超市消费、食堂消费、总消费 2 &#xff09;输入数据格式 序号 学号 超市消费 食堂消费 18 202200153105 8.78 12 3 &#xff09;期望输出格式 key &#xff08;学号&#xff09; value &#xff08; bean 对象&#xf…

二分查找算法的概念、原理、效率以及使用C语言循环和数组的简单实现

二分查找的概念 二分查找也称折半查找&#xff08;Binary Search&#xff09;&#xff0c;它是一种效率较高的查找方法。但是&#xff0c;折半查找要求线性表必须采用顺序存储结构&#xff0c;而且表中元素按关键字有序排列。 实现原理 首先&#xff0c;假设表中元素是按升序…