c# 批量mqtt_C#使用 MQTTnet 快速实现 MQTT 通信(文末有完整Demo下载)

MQTT(一)C#使用 MQTTnet 快速实现 MQTT 通信(文末有完整Demo下载)

原创weixin_pwtank1983 发布于2018-02-03 10:22:24 阅读数 36681 收藏

展开

目录

MQTT(一)C#使用 MQTTnet 快速实现 MQTT 通信(文末有完整Demo下载)

MQTT(二)在windows64位上安装Python环境

MQTT(三)Python客户端+net客户端+net服务端 简单通信

MQTT(四)树莓派开机自动运行Python客户端

MQTT(五)EMQ开源MQTT消息服务器

1 什么是 MQTT ?

MQTT(Message Queuing Telemetry Transport,消息队列遥测传输)是 IBM 开发的一个即时通讯协议,有可能成为物联网的重要组成部分。MQTT 是基于二进制消息的发布/订阅编程模式的消息协议,如今已经成为 OASIS 规范,由于规范很简单,非常适合需要低功耗和网络带宽有限的 IoT 场景。MQTT官网

2 MQTTnet

MQTTnet 是一个基于 MQTT 通信的高性能 .NET 开源库,它同时支持 MQTT 服务器端和客户端。而且作者也保持更新,目前支持新版的.NET core,这也是选择 MQTTnet 的原因。 MQTTnet 在 Github 并不是下载最多的 .NET 的 MQTT 开源库,其他的还 MqttDotNet、nMQTT、M2MQTT 等

MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker). The implementation is based on the documentation from http://mqtt.org/.

3 创建项目并导入类库

这里我们使用 Visual Studio 2017 创建一个空解决方案,并在其中添加两个项目,即一个服务端和一个客户端,服务端项目模板选择最新的 .NET Core 控制台应用,客户端项目选择传统的 WinForm 窗体应用程序。.NET Core 项目模板如下图所示:

在解决方案在右键单击-选择“管理解决方案的 NuGet 程序包”-在“浏览”选项卡下面搜索 MQTTnet,为服务端项目和客户端项目都安装上 MQTTnet 库,当前最新稳定版为 2.4.0。项目结构如下图所示:

4 服务端

MQTT 服务端主要用于与多个客户端保持连接,并处理客户端的发布和订阅等逻辑。一般很少直接从服务端发送消息给客户端(可以使用 mqttServer.Publish(appMsg); 直接发送消息),多数情况下服务端都是转发主题匹配的客户端消息,在系统中起到一个中介的作用。

4.1 创建服务端并启动

创建服务端最简单的方式是采用 MqttServerFactory 对象的 CreateMqttServer 方法来实现,该方法需要一个MqttServerOptions 参数。

var options = new MqttServerOptions();

var mqttServer = new MqttServerFactory().CreateMqttServer(options);

通过上述方式创建了一个 IMqttServer 对象后,调用其 StartAsync 方法即可启动 MQTT 服务。值得注意的是:之前版本采用的是 Start 方法,作者也是紧跟 C# 语言新特性,能使用异步的地方也都改为异步方式。

await mqttServer.StartAsync();

4.2 验证客户端

在 MqttServerOptions 选项中,你可以使用 ConnectionValidator 来对客户端连接进行验证。比如客户端ID标识 ClientId,用户名 Username 和密码 Password 等。

var options = new MqttServerOptions

{

ConnectionValidator = c =>

{

if (c.ClientId.Length < 10)

{

return MqttConnectReturnCode.ConnectionRefusedIdentifierRejected;

}

if (c.Username != "xxx" || c.Password != "xxx")

{

return MqttConnectReturnCode.ConnectionRefusedBadUsernameOrPassword;

}

return MqttConnectReturnCode.ConnectionAccepted;

}

};

4.3 相关事件

服务端支持 ClientConnected、ClientDisconnected 和 ApplicationMessageReceived 事件,分别用来检查客户端连接、客户端断开以及接收客户端发来的消息。

其中 ClientConnected 和 ClientDisconnected 事件的事件参数一个客户端连接对象 ConnectedMqttClient,通过该对象可以获取客户端ID标识 ClientId 和 MQTT 版本 ProtocolVersion。

ApplicationMessageReceived 的事件参数包含了客户端ID标识 ClientId 和 MQTT 应用消息 MqttApplicationMessage 对象,通过该对象可以获取主题 Topic、QoS QualityOfServiceLevel 和消息内容 Payload 等信息。

5 客户端

MQTT 与 HTTP 不同,后者是基于请求/响应方式的,服务器端无法直接发送数据给客户端。而 MQTT 是基于发布/订阅模式的,所有的客户端均与服务端保持连接状态。

那么客户端之间是如何通信的呢?

具体逻辑是:某些客户端向服务端订阅它感兴趣(主题)的消息,另一些客户端向服务端发布(主题)消息,服务端将订阅和发布的主题进行匹配,并将消息转发给匹配通过的客户端。

5.1 创建客户端并连接

使用 MQTTnet 创建 MQTT 也非常简单,只需要使用 MqttClientFactory 对象的 CreateMqttClient 方法即可。

var mqttClient = new MqttClientFactory().CreateMqttClient();

创建客户端对象后,调用其异步方法 ConnectAsync 来连接到服务端。

await mqttClient.ConnectAsync(options);

调用该方法时需要传递一个 MqttClientTcpOptions 对象(之前的版本是在创建对象时使用该选项),该选项包含了客户端ID标识 ClientId、服务端地址(可以使用IP地址或域名)Server、端口号 Port、用户名 UserName、密码 Password 等信息。

var options = new MqttClientTcpOptions

{

Server = "127.0.0.1",

ClientId = "c001",

UserName = "u001",

Password = "p001",

CleanSession = true

};

5.2 相关事件

客户端支持 Connected、Disconnected 和 ApplicationMessageReceived 事件,用来处理客户端与服务端连接、客户端从服务端断开以及客户端收到消息的事情。

5.2 订阅消息

客户端连接到服务端之后,可以使用 SubscribeAsync 异步方法订阅消息,该方法可以传入一个可枚举或可变参数的主题过滤器 TopicFilter 参数,主题过滤器包含主题名和 QoS 等级。

mqttClient.SubscribeAsync(new List {

new TopicFilter("家/客厅/空调/#", MqttQualityOfServiceLevel.AtMostOnce)

});

5.3 发布消息

发布消息前需要先构建一个消息对象 MqttApplicationMessage,最直接的方法是使用其实构造函数,传入主题、内容、Qos 等参数。

mqttClient.SubscribeAsync(new List {

new TopicFilter("家/客厅/空调/#", MqttQualityOfServiceLevel.AtMostOnce)

});

得到 MqttApplicationMessage 消息对象后,通过客户端对象调用其 PublishAsync 异步方法进行消息发布。

mqttClient.PublishAsync(appMsg);

6 跟踪消息

MQTTnet 提供了一个静态类 MqttNetTrace 来对消息进行跟踪,该类可用于服务端和客户端。MqttNetTrace 的事件TraceMessagePublished 用于跟踪服务端和客户端应用的日志消息,比如启动、停止、心跳、消息订阅和发布等。事件参数MqttNetTraceMessagePublishedEventArgs 包含了线程ID ThreadId、来源 Source、日志级别 Level、日志消息 Message、异常信息 Exception 等。

MqttNetTrace.TraceMessagePublished += MqttNetTrace_TraceMessagePublished;

private static void MqttNetTrace_TraceMessagePublished(object sender, MqttNetTraceMessagePublishedEventArgs e)

{

Console.WriteLine($">> 线程ID:{e.ThreadId} 来源:{e.Source} 跟踪级别:{e.Level} 消息: {e.Message}");

if (e.Exception != null)

{

Console.WriteLine(e.Exception);

}

}

同时 MqttNetTrace 类还提供了4个不同消息等级的静态方法,Verbose、Information、Warning 和 Error,用于给出不同级别的日志消息,该消息将会在 TraceMessagePublished 事件中输出,你可以使用 e.Level 进行过虑。

7 运行效果

以下分别是服务端、客户端1和客户端2的运行效果,其中客户端1和客户端2只是同一个项目运行了两个实例。客户端1用于订阅传感器的“温度”数据,并模拟上位机(如 APP 等)发送开关控制命令;客户端2订阅上位机传来的“开关”控制命令,并模拟温度传感器上报温度数据。

7.1 服务端

7.2 客户端1

7.2 客户端2

8 Demo代码

8.1 服务端代码

using MQTTnet;

using MQTTnet.Core.Adapter;

using MQTTnet.Core.Diagnostics;

using MQTTnet.Core.Protocol;

using MQTTnet.Core.Server;

using System;

using System.Text;

using System.Threading;

namespace MqttServerTest

{

class Program

{

private static MqttServer mqttServer = null;

static void Main(string[] args)

{

MqttNetTrace.TraceMessagePublished += MqttNetTrace_TraceMessagePublished;

new Thread(StartMqttServer).Start();

while (true)

{

var inputString = Console.ReadLine().ToLower().Trim();

if (inputString == "exit")

{

mqttServer?.StopAsync();

Console.WriteLine("MQTT服务已停止!");

break;

}

else if (inputString == "clients")

{

foreach (var item in mqttServer.GetConnectedClients())

{

Console.WriteLine($"客户端标识:{item.ClientId},协议版本:{item.ProtocolVersion}");

}

}

else

{

Console.WriteLine($"命令[{inputString}]无效!");

}

}

}

private static void StartMqttServer()

{

if (mqttServer == null)

{

try

{

var options = new MqttServerOptions

{

ConnectionValidator = p =>

{

if (p.ClientId == "c001")

{

if (p.Username != "u001" || p.Password != "p001")

{

return MqttConnectReturnCode.ConnectionRefusedBadUsernameOrPassword;

}

}

return MqttConnectReturnCode.ConnectionAccepted;

}

};

mqttServer = new MqttServerFactory().CreateMqttServer(options) as MqttServer;

mqttServer.ApplicationMessageReceived += MqttServer_ApplicationMessageReceived;

mqttServer.ClientConnected += MqttServer_ClientConnected;

mqttServer.ClientDisconnected += MqttServer_ClientDisconnected;

}

catch (Exception ex)

{

Console.WriteLine(ex.Message);

return;

}

}

mqttServer.StartAsync();

Console.WriteLine("MQTT服务启动成功!");

}

private static void MqttServer_ClientConnected(object sender, MqttClientConnectedEventArgs e)

{

Console.WriteLine($"客户端[{e.Client.ClientId}]已连接,协议版本:{e.Client.ProtocolVersion}");

}

private static void MqttServer_ClientDisconnected(object sender, MqttClientDisconnectedEventArgs e)

{

Console.WriteLine($"客户端[{e.Client.ClientId}]已断开连接!");

}

private static void MqttServer_ApplicationMessageReceived(object sender, MqttApplicationMessageReceivedEventArgs e)

{

Console.WriteLine($"客户端[{e.ClientId}]>> 主题:{e.ApplicationMessage.Topic} 负荷:{Encoding.UTF8.GetString(e.ApplicationMessage.Payload)} Qos:{e.ApplicationMessage.QualityOfServiceLevel} 保留:{e.ApplicationMessage.Retain}");

}

private static void MqttNetTrace_TraceMessagePublished(object sender, MqttNetTraceMessagePublishedEventArgs e)

{

/*Console.WriteLine($">> 线程ID:{e.ThreadId} 来源:{e.Source} 跟踪级别:{e.Level} 消息: {e.Message}");

if (e.Exception != null)

{

Console.WriteLine(e.Exception);

}*/

}

}

}

8.2 客户端代码

using MQTTnet;

using MQTTnet.Core;

using MQTTnet.Core.Client;

using MQTTnet.Core.Packets;

using MQTTnet.Core.Protocol;

using System;

using System.Collections.Generic;

using System.Text;

using System.Threading.Tasks;

using System.Windows.Forms;

namespace MqttClientWin

{

public partial class FmMqttClient : Form

{

private MqttClient mqttClient = null;

public FmMqttClient()

{

InitializeComponent();

Task.Run(async () => { await ConnectMqttServerAsync(); });

}

private async Task ConnectMqttServerAsync()

{

if (mqttClient == null)

{

mqttClient = new MqttClientFactory().CreateMqttClient() as MqttClient;

mqttClient.ApplicationMessageReceived += MqttClient_ApplicationMessageReceived;

mqttClient.Connected += MqttClient_Connected;

mqttClient.Disconnected += MqttClient_Disconnected;

}

try

{

var options = new MqttClientTcpOptions

{

Server = "127.0.0.1",

ClientId = Guid.NewGuid().ToString().Substring(0, 5),

UserName = "u001",

Password = "p001",

CleanSession = true

};

await mqttClient.ConnectAsync(options);

}

catch (Exception ex)

{

Invoke((new Action(() =>

{

txtReceiveMessage.AppendText($"连接到MQTT服务器失败!" + Environment.NewLine + ex.Message + Environment.NewLine);

})));

}

}

private void MqttClient_Connected(object sender, EventArgs e)

{

Invoke((new Action(() =>

{

txtReceiveMessage.AppendText("已连接到MQTT服务器!" + Environment.NewLine);

})));

}

private void MqttClient_Disconnected(object sender, EventArgs e)

{

Invoke((new Action(() =>

{

txtReceiveMessage.AppendText("已断开MQTT连接!" + Environment.NewLine);

})));

}

private void MqttClient_ApplicationMessageReceived(object sender, MqttApplicationMessageReceivedEventArgs e)

{

Invoke((new Action(() =>

{

txtReceiveMessage.AppendText($">> {Encoding.UTF8.GetString(e.ApplicationMessage.Payload)}{Environment.NewLine}");

})));

}

private void BtnSubscribe_ClickAsync(object sender, EventArgs e)

{

string topic = txtSubTopic.Text.Trim();

if (string.IsNullOrEmpty(topic))

{

MessageBox.Show("订阅主题不能为空!");

return;

}

if (!mqttClient.IsConnected)

{

MessageBox.Show("MQTT客户端尚未连接!");

return;

}

mqttClient.SubscribeAsync(new List {

new TopicFilter(topic, MqttQualityOfServiceLevel.AtMostOnce)

});

txtReceiveMessage.AppendText($"已订阅[{topic}]主题" + Environment.NewLine);

txtSubTopic.Enabled = false;

btnSubscribe.Enabled = false;

}

private void BtnPublish_Click(object sender, EventArgs e)

{

string topic = txtPubTopic.Text.Trim();

if (string.IsNullOrEmpty(topic))

{

MessageBox.Show("发布主题不能为空!");

return;

}

string inputString = txtSendMessage.Text.Trim();

var appMsg = new MqttApplicationMessage(topic, Encoding.UTF8.GetBytes(inputString), MqttQualityOfServiceLevel.AtMostOnce, false);

mqttClient.PublishAsync(appMsg);

}

}

}

9 本文的Demo下载地址

点击下载 Demo

https://download.csdn.net/download/panwen1111/11018592

pw的其他原创文章导航

C#的MQTT系列

MQTT(一)C#使用 MQTTnet 快速实现 MQTT 通信(文末有完整Demo下载)

MQTT(二)在windows64位上安装Python环境

MQTT(三)Python客户端+net客户端+net服务端 简单通信

MQTT(四)树莓派开机自动运行Python客户端

MQTT(五)EMQ开源MQTT消息服务器

C#的阿里物联网平台

阿里物联网平台(一)Windows系统+VS2017 模拟设备端接入

阿里物联网平台(二).net 实现移动端(WEB、HTML)与设备端通讯

落地项目

落地项目-智慧海绵城市

落地项目-智能焊机,钢塑管行业物联网应用

手持安卓小票打印一体机,小票打印应用

省城建设计院智慧海绵城市示范工程

————————————————

版权声明:本文为CSDN博主「weixin_pwtank1983」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/panwen1111/article/details/79245161

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

5月16日 AJAX

主页面&#xff1a; <head> <script src"../jquery-2.2.3.min.js"></script> </head><body> <div> <input type"text" id"uid" /><span id"xinxi"></span> </div> </…

linux signal 符号表6,gdb调试信息__000_linux-gnu_00000000_002_rw-p__169IT.COM

很少使用gdb调试&#xff0c;现在用了&#xff0c;又不知道是什么意思&#xff0c;求大侠指教*** glibc detected *** /home/hhbgk/url/url: free(): invalid next size (normal): 0x0809ac30 *** Backtrace: /lib/i386-linux-gnu/libc.so.6(0x6ff22)[0x1a0f22]/lib/i386-linux…

pyecharts查看版本_pyecharts v1 版本 学习笔记 折线图,面积图

折线图折线图 基本demoimport pyecharts.options as optsfrom pyecharts.charts import Linec (Line().add_xaxis(["衬衫", "毛衣", "领带", "裤子", "风衣", "高跟鞋", "袜子"]).add_yaxis(商家A, [1…

【Django】Django Debug Toolbar调试工具配置

正在发愁怎么调试Django&#xff0c;就遇到了Django Debug Toolbar这个利器。 先说遇到的问题&#xff1a; 网上也有教程&#xff0c;不过五花八门的&#xff0c;挨个尝试了一遍&#xff0c;也没有成功运行。最后终于找到问题所在&#xff1a; 从开发服务器日志可知&#xff1a…

单片机c语言模块化实例程序设计,单片机C语言模块化设计

原标题&#xff1a;单片机C语言模块化设计keil中实现 模块化编程.doc在使用 KEIL 的时候,我们习惯上在一个.c 的文件中把自己要写的东西按照自己思路的顺序进行顺序书写。这样是很普遍的写法,当程序比较短的时候比如几十行或者一百多行,是没有什么问题的。但是当程序很长的时候…

python中提取pdf文件某些页_人工智能|Python提取PDF中的文本并朗读

题前的话之前有老师在群里推荐这个&#xff0c;我看了一眼觉得还是蛮有趣的&#xff0c;然后就忘了……昨天又看到这个《宅男福利&#xff01;我50行Python代码让小姐姐给你读Pdf》&#xff0c;今天于是开始自己尝试&#xff0c;谁知道pdfplumber库怎么也安装不了&#xff0c;最…

基于RAF的一个小动画框

RAF也即是requestAnimationFrame&#xff0c;之前的动画都是基于setTimeout写的&#xff0c;所以为了性能方面的考虑&#xff0c;开始使用requestAnimationFrame写动画。 function animation(obj, data) {if (obj.timer) {window.cancelAnimationFrame(obj.timer)}obj.timer w…

c语言定义一个字母的范围,C语言学习知识程序设计试卷及其规范标准答案

C 语言程序设计 笔试试题题号 一 二 三 四 五 六 笔试(70%)综合 (30%)总分分数试卷说明&#xff1a;1. 笔试卷面总分100分&#xff0c;取卷面成绩的70%计入总分&#xff1b;2. 综合成绩为平时成绩(10%)和实验成绩(20%)之和&#xff0c;占总分的30%&#xff1b;3. 答题时禁止拆开…

python执行时间长被kill_用python记录运行pid,并在需要时kill掉它们的实例

我在跑爬虫程序的时候&#xff0c;由于爬虫程序的等待目标服务器返回数据的时间很长&#xff0c;而cpu占用很低&#xff0c;所以经常挂着代理一跑就跑好几百个。但是爬虫程序通常是写了死循环&#xff0c;或直到分配给该进程的任务都跑完才退出的。如果我们想中途结束掉这些任务…

[UVA315]Network(tarjan, 求割点)

题目链接&#xff1a;https://uva.onlinejudge.org/index.php?optioncom_onlinejudge&Itemid8&pageshow_problem&problem251 求割点&#xff0c;除了输入用strtok和sscanf处理输入以外&#xff0c;对于求割点的tarjan算法有了进一步理解。 特别注意88行&#xff0…

pmw调光c语言程序,51单片机led灯渐变PWM调光(渐亮渐灭)C语言和汇编源程序

; PWM 控制 LED 灯渐亮渐灭程序; 适用 ME500 开发系统; 晶振为11.0592M; 利用定时器控制产生占空比可变的 PWM 波; 按K1&#xff0c;PWM值增加&#xff0c;则占空比减小,LED 灯渐暗。; 按K2&#xff0c;PWM值减小&#xff0c;则占空比增加,LED 灯渐亮。; 当PWM值增加到最大值或…

imgaug批量椒盐噪声 python_python手写给图像加椒盐噪声和高斯噪声

此文记录自己在做图像数据增强给图像加噪时遇到的一些情况。椒盐噪音&#xff1a;简单点说就是在图像中随机加一些白点或者黑点。from skimage import ioimport randomimport numpy as npdef salt_and_pepper_noise(img, proportion0.05):noise_img imgheight,width noise_img.…

万年历c语言设计报告,C语言实训题目设计报告 万年历

for(i1;i<startday;i){printf("\t");//把每月1日之前的空格打出来}for(i1;i<monthdays;i){printf("%d\t",i);if(i7*k-startday){printf("\n");k;}}}printf("\n\n");}void nianli(){int i,j,k,startday,monthdays,month,year;sys…

ERROR ITMS-90022,90023,问题已解决

昨天打包上架&#xff0c;在使用Application Loader上传包时出现ERROR ITMS-90022,90023这两个错误&#xff0c;在此记录一下。 ERROR ITMS-90023的错误解决方法比较简单&#xff0c;在Assets.xcassets中的AppIcon里面&#xff0c;将CarPlay内的图片删除即可。 ERROR ITMS-9002…

Gridiew——表的内容居中

添加如下代码&#xff1a;ItemStyle-HorizontalAlign"Center"例子&#xff1a;<asp:BoundField DataField"ID" HeaderText"账号" SortExpression"ID" ItemStyle-HorizontalAlign"Center"/>转载于:https://www.cnblogs…

c语言编程一个象棋游戏,急求:C语言编写的中国象棋游戏一个

急求&#xff1a;C语言编写的中国象棋游戏一个來源:互聯網 2009-09-08 12:30:35 評論分類: 電腦/網絡 >> 程序設計 >> 其他編程語言問題描述:由于学习需要......热烈欢迎个位大侠&#xff0c;高手相助&#xff01;小生在此道谢了&#xff01;&#xff01;&#x…

grunt 前端开发环境搭建

1.找管理员开通gitlab权限 2.下载并安装git工具 3.下载并安装nodejs 4.安装cnpm 5.安装kulor&#xff0d;cli cnpm install -g kulor-cli cnpm install -g grunt-cli cnpm install 6.安装nginx并运行 7.进入到工作目录 运行 grunt 补充如何从远程拉代码&#xff0c;在本地配…

获取referer中的请求参数_Servlet获取AJAX POST请求中参数以form data和request payload形式传输的方法...

本文实例讲述了Servlet获取AJAX POST请求中参数以form data和request payload形式传输的方法。分享给大家供大家参考&#xff0c;具体如下&#xff1a;HTTP请求中&#xff0c;如果是get请求&#xff0c;那么表单参数以namevalue&name1value1的形式附到url的后面&#xff0c…

prim求最短路径C语言,[图论]Prim算法求最小支撑树和最短路径

这个是以前所学&#xff0c;现在总结成博文一篇。对于图论中的求解最小支撑树问题和最短路径问题都有比较经典的算法&#xff0c;比如最小支撑树可以采用“破圈法”&#xff0c;求解最短路径可以用“Dijkstra算法”。这里笔者将回顾下求解最小支撑树的Prim算法和最短路径算法。…

后台服务器端技术点(前沿了解)

1. OpenResty nginx lua 模式解决 高性能与开发效率 平衡。转载于:https://www.cnblogs.com/jhj117/p/5520250.html