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

2 篇文章 0 订阅

版权声明:本文为博主原创文章,转载请注明出处,谢谢! https://blog.csdn.net/panwen1111/article/details/79245161

目录

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 开源库,其他的还 MqttDotNetnMQTTM2MQTT 等

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 项目模板如下图所示: 
.NET Core 控制台应用

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

4 服务端

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

4.1 创建服务端并启动

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

 

 
  1. var options = new MqttServerOptions();

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

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

 

await mqttServer.StartAsync();

 

4.2 验证客户端

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

 

 
  1. var options = new MqttServerOptions

  2. {

  3. ConnectionValidator = c =>

  4. {

  5. if (c.ClientId.Length < 10)

  6. {

  7. return MqttConnectReturnCode.ConnectionRefusedIdentifierRejected;

  8. }

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

  11. {

  12. return MqttConnectReturnCode.ConnectionRefusedBadUsernameOrPassword;

  13. }

  14.  
  15. return MqttConnectReturnCode.ConnectionAccepted;

  16. }

  17. };

 

4.3 相关事件

服务端支持 ClientConnectedClientDisconnected 和 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 等信息。

 

 
  1. var options = new MqttClientTcpOptions

  2. {

  3. Server = "127.0.0.1",

  4. ClientId = "c001",

  5. UserName = "u001",

  6. Password = "p001",

  7. CleanSession = true

  8. };

 

5.2 相关事件

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

5.2 订阅消息

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

 

 
  1. mqttClient.SubscribeAsync(new List<TopicFilter> {

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

  3. });

 

5.3 发布消息

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

 

 
  1. mqttClient.SubscribeAsync(new List<TopicFilter> {

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

  3. });

 

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

 

mqttClient.PublishAsync(appMsg);

 

6 跟踪消息

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

 

 
  1. MqttNetTrace.TraceMessagePublished += MqttNetTrace_TraceMessagePublished;

  2.  
  3. private static void MqttNetTrace_TraceMessagePublished(object sender, MqttNetTraceMessagePublishedEventArgs e)

  4. {

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

  6.  
  7. if (e.Exception != null)

  8. {

  9. Console.WriteLine(e.Exception);

  10. }

  11. }

 

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

7 运行效果

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

7.1 服务端

服务端

7.2 客户端1

客户端1

7.2 客户端2

客户端2

8 Demo代码

8.1 服务端代码

 

 
  1. using MQTTnet;

  2. using MQTTnet.Core.Adapter;

  3. using MQTTnet.Core.Diagnostics;

  4. using MQTTnet.Core.Protocol;

  5. using MQTTnet.Core.Server;

  6. using System;

  7. using System.Text;

  8. using System.Threading;

  9.  
  10. namespace MqttServerTest

  11. {

  12. class Program

  13. {

  14. private static MqttServer mqttServer = null;

  15.  
  16. static void Main(string[] args)

  17. {

  18. MqttNetTrace.TraceMessagePublished += MqttNetTrace_TraceMessagePublished;

  19. new Thread(StartMqttServer).Start();

  20.  
  21. while (true)

  22. {

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

  24.  
  25. if (inputString == "exit")

  26. {

  27. mqttServer?.StopAsync();

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

  29. break;

  30. }

  31. else if (inputString == "clients")

  32. {

  33. foreach (var item in mqttServer.GetConnectedClients())

  34. {

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

  36. }

  37. }

  38. else

  39. {

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

  41. }

  42. }

  43. }

  44.  
  45. private static void StartMqttServer()

  46. {

  47. if (mqttServer == null)

  48. {

  49. try

  50. {

  51. var options = new MqttServerOptions

  52. {

  53. ConnectionValidator = p =>

  54. {

  55. if (p.ClientId == "c001")

  56. {

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

  58. {

  59. return MqttConnectReturnCode.ConnectionRefusedBadUsernameOrPassword;

  60. }

  61. }

  62.  
  63. return MqttConnectReturnCode.ConnectionAccepted;

  64. }

  65. };

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

  68. mqttServer.ApplicationMessageReceived += MqttServer_ApplicationMessageReceived;

  69. mqttServer.ClientConnected += MqttServer_ClientConnected;

  70. mqttServer.ClientDisconnected += MqttServer_ClientDisconnected;

  71. }

  72. catch (Exception ex)

  73. {

  74. Console.WriteLine(ex.Message);

  75. return;

  76. }

  77. }

  78.  
  79. mqttServer.StartAsync();

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

  81. }

  82.  
  83. private static void MqttServer_ClientConnected(object sender, MqttClientConnectedEventArgs e)

  84. {

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

  86. }

  87.  
  88. private static void MqttServer_ClientDisconnected(object sender, MqttClientDisconnectedEventArgs e)

  89. {

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

  91. }

  92.  
  93. private static void MqttServer_ApplicationMessageReceived(object sender, MqttApplicationMessageReceivedEventArgs e)

  94. {

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

  96. }

  97.  
  98. private static void MqttNetTrace_TraceMessagePublished(object sender, MqttNetTraceMessagePublishedEventArgs e)

  99. {

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

  101.  
  102. if (e.Exception != null)

  103. {

  104. Console.WriteLine(e.Exception);

  105. }*/

  106. }

  107. }

  108. }

8.2 客户端代码

 

 
  1. using MQTTnet;

  2. using MQTTnet.Core;

  3. using MQTTnet.Core.Client;

  4. using MQTTnet.Core.Packets;

  5. using MQTTnet.Core.Protocol;

  6. using System;

  7. using System.Collections.Generic;

  8. using System.Text;

  9. using System.Threading.Tasks;

  10. using System.Windows.Forms;

  11.  
  12. namespace MqttClientWin

  13. {

  14. public partial class FmMqttClient : Form

  15. {

  16. private MqttClient mqttClient = null;

  17.  
  18. public FmMqttClient()

  19. {

  20. InitializeComponent();

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

  23. }

  24.  
  25. private async Task ConnectMqttServerAsync()

  26. {

  27. if (mqttClient == null)

  28. {

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

  30. mqttClient.ApplicationMessageReceived += MqttClient_ApplicationMessageReceived;

  31. mqttClient.Connected += MqttClient_Connected;

  32. mqttClient.Disconnected += MqttClient_Disconnected;

  33. }

  34.  
  35. try

  36. {

  37. var options = new MqttClientTcpOptions

  38. {

  39. Server = "127.0.0.1",

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

  41. UserName = "u001",

  42. Password = "p001",

  43. CleanSession = true

  44. };

  45.  
  46. await mqttClient.ConnectAsync(options);

  47. }

  48. catch (Exception ex)

  49. {

  50. Invoke((new Action(() =>

  51. {

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

  53. })));

  54. }

  55. }

  56.  
  57. private void MqttClient_Connected(object sender, EventArgs e)

  58. {

  59. Invoke((new Action(() =>

  60. {

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

  62. })));

  63. }

  64.  
  65. private void MqttClient_Disconnected(object sender, EventArgs e)

  66. {

  67. Invoke((new Action(() =>

  68. {

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

  70. })));

  71. }

  72.  
  73. private void MqttClient_ApplicationMessageReceived(object sender, MqttApplicationMessageReceivedEventArgs e)

  74. {

  75. Invoke((new Action(() =>

  76. {

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

  78. })));

  79. }

  80.  
  81. private void BtnSubscribe_ClickAsync(object sender, EventArgs e)

  82. {

  83. string topic = txtSubTopic.Text.Trim();

  84.  
  85. if (string.IsNullOrEmpty(topic))

  86. {

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

  88. return;

  89. }

  90.  
  91. if (!mqttClient.IsConnected)

  92. {

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

  94. return;

  95. }

  96.  
  97. mqttClient.SubscribeAsync(new List<TopicFilter> {

  98. new TopicFilter(topic, MqttQualityOfServiceLevel.AtMostOnce)

  99. });

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

  102. txtSubTopic.Enabled = false;

  103. btnSubscribe.Enabled = false;

  104. }

  105.  
  106. private void BtnPublish_Click(object sender, EventArgs e)

  107. {

  108. string topic = txtPubTopic.Text.Trim();

  109.  
  110. if (string.IsNullOrEmpty(topic))

  111. {

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

  113. return;

  114. }

  115.  
  116. string inputString = txtSendMessage.Text.Trim();

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

  118. mqttClient.PublishAsync(appMsg);

  119. }

  120. }

  121. }

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)与设备端通讯

  • 落地项目

落地项目-智慧海绵城市

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值