代码改变世界

使用 MQTTnet 实现 MQTT 通信示例

2023-10-23 10:51  古兆洋  阅读(1038)  评论(0编辑  收藏  举报

转自:https://cloud.tencent.com/developer/article/2103199

一、什么是 MQTT ?

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

二、MQTTnet

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/.

MQTT 开源库还有 MqttDotNetnMQTTM2MQTT 等,不过这几个里面,目前star最多的还是MQTTnet(编辑时间2018.5.9)。

三、创建项目并导入类库

在解决方案在右键单击-选择“管理解决方案的 NuGet 程序包”-在“浏览”选项卡下面搜索 MQTTnet,为服务端项目和客户端项目都安装上 MQTTnet 库。示例中使用的是2.7.5.0版本,不同版本最低要求的.net版本或其它支持,在NuGet选中MQTTNet,右侧可以看到具体描述。

客户端简单Demo可以见官方文档:https://github.com/chkr1011/MQTTnet/wiki/Client 本文示例程序github:https://github.com/landbroken/MQTTLearning

评论有不知道怎么用示例程序的,简单解释一下。目录一开始建的有点问题,调试稍微麻烦一点,懒得改了。

  • 1、clone或download项目
  • 2、vs2015或以上版本打开MQTTLearning/MqttServerTest/MqttServerTest.sln
  • 3、服务器端示例在SeverTest项目里面
  • 4、客户端示例在ClientTest项目里面 调试: 方法1)是vs里两个项目设为同时启动; 方法2)一端用生成的exe启动,一端在vs里用debug启动

一般可以直接打开的,万一vs有路径依赖问题:

  • 1、如果项目路径依赖有问题,删掉重新添加一遍SeverTest.csproj和ClientTest.csproj
  • 2、如果MQTTnet 库引用路径有问题,删掉从packages里面重新引用一遍,或者nuget里面添加一下

3.1 服务器

直接跑一下示例程序就知道了 下面简单解释一下主要部分。

3.1.1 初始化并启动服务器

首先,初始化并启动服务器

1、这里是异步启动,用的2.7.5版本的库

1 Task.Run(async () => { 
2     await StartMqttServer_2_7_5(); });

2、配置设置 WithDefaultEndpointPort是设置使用的端口,协议里默认是用1883,不过调试我改成8222了。 WithConnectionValidator是用于连接验证,验证client id,用户名,密码什么的。示例没用数据库,随便写死了两个值。 还有其他配置选项,比如加密协议,可以在官方文档里看看,示例就是先简单能用。

1 // Configure MQTT server.
2                 var optionsBuilder = new MqttServerOptionsBuilder()
3                     .WithConnectionBacklog(100)
4                     .WithDefaultEndpointPort(8222)
5                     .WithConnectionValidator(ValidatingMqttClients())
6                     ;

3、添加事件触发

ApplicationMessageReceived 是服务器接收到消息时触发的事件,可用来响应特定消息。

ClientConnected 是客户端连接成功时触发的事件。

ClientDisconnected 是客户端断开连接时触发的事件。

1 mqttServer = new MqttFactory().CreateMqttServer();
2                 mqttServer.ApplicationMessageReceived += MqttServer_ApplicationMessageReceived;
3                 mqttServer.ClientConnected += MqttServer_ClientConnected;
4                 mqttServer.ClientDisconnected += MqttServer_ClientDisconnected;

异步启动mqtt服务器,实际项目可能要加一个启动失败处理

1 Task.Run(async () => { 
2     await mqttServer.StartAsync(optionsBuilder.Build()); });

示例里面为了简单调试玩的,写了几个触发命令,在启动后,输入对于字符串即可触发。

字符串 效果
exit 关闭mqtt服务
hello: 发送topic为topic/hello的消息,payload为冒号后面的数据
control: 发送topic为topic/control的消息,payload为冒号后面的数据
subscribe: 订阅topic为冒号后面的消息

 

3.1.2 消息发送

mqtt的消息包含topic和payload两部分。topic就是消息主题(类型),用于另外一端判断这个消息是干什么用的。payload就是实际想要发送的数据。 WithTopic给一个topic。 WithPayload给一个msg。 WithAtMostOnceQoS设置QoS,至多1次。也可以设为别的。 PublishAsync异步发送出去。

1 string topic = "topic/hello";
2 var message = new MqttApplicationMessageBuilder()
3                 .WithTopic(topic)
4                 .WithPayload(msg)
5                 .WithAtMostOnceQoS()
6                 .WithRetainFlag()
7                 .Build();
8 await mqttServer.PublishAsync(message);

3.1.3 完整源码

  1 using MQTTnet;
  2 using MQTTnet.Diagnostics;
  3 using MQTTnet.Protocol;
  4 using MQTTnet.Server;
  5 using System;
  6 using System.Collections.Generic;
  7 using System.Security.Cryptography.X509Certificates;
  8 using System.Text;
  9 using System.Threading;
 10 using System.Threading.Tasks;
 11 
 12 namespace MqttServerTest
 13 { 
 14    
 15     class Program
 16     { 
 17    
 18         private static IMqttServer mqttServer = null;
 19         private static List<string> connectedClientId = new List<string>();
 20 
 21         static void Main(string[] args)
 22         { 
 23    
 24             Task.Run(async () => { 
 25     await StartMqttServer_2_7_5(); });
 26 
 27             // Write all trace messages to the console window.
 28             MqttNetGlobalLogger.LogMessagePublished += MqttNetTrace_TraceMessagePublished;
 29 
 30             //2.4.0版本
 31             //MqttNetTrace.TraceMessagePublished += MqttNetTrace_TraceMessagePublished;
 32             //new Thread(StartMqttServer).Start();
 33             while (true)
 34             { 
 35    
 36                 if (mqttServer==null)
 37                 { 
 38    
 39                     Console.WriteLine("Please await mqttServer.StartAsync()");
 40                     Thread.Sleep(1000);
 41                     continue;
 42                 }
 43 
 44                 var inputString = Console.ReadLine().ToLower().Trim();
 45 
 46                 if (inputString == "exit")
 47                 { 
 48    
 49                     Task.Run(async () => { 
 50     await EndMqttServer_2_7_5(); });
 51                     Console.WriteLine("MQTT服务已停止!");
 52                     break;
 53                 }
 54                 else if (inputString == "clients")
 55                 { 
 56    
 57                     var connectedClients = mqttServer.GetConnectedClientsAsync();
 58 
 59                     Console.WriteLine($"客户端标识:");
 60                     //2.4.0
 61                     //foreach (var item in mqttServer.GetConnectedClients())
 62                     //{ 
 63    
 64                     // Console.WriteLine($"客户端标识:{item.ClientId},协议版本:{item.ProtocolVersion}");
 65                     //}
 66                 }
 67                 else if (inputString.StartsWith("hello:"))
 68                 { 
 69    
 70                     string msg = inputString.Substring(6);
 71                     Topic_Hello(msg);
 72                 }
 73                 else if (inputString.StartsWith("control:"))
 74                 { 
 75    
 76                     string msg = inputString.Substring(8);
 77                     Topic_Host_Control(msg);
 78                 }
 79                 else if (inputString.StartsWith("subscribe:"))
 80                 { 
 81    
 82                     string msg = inputString.Substring(10);
 83                     Subscribe(msg);
 84                 }
 85                 else
 86                 { 
 87    
 88                     Console.WriteLine($"命令[{inputString}]无效!");
 89                 }
 90                 Thread.Sleep(100);
 91             }
 92         }
 93 
 94         private static void MqttServer_ClientConnected(object sender, MqttClientConnectedEventArgs e)
 95         { 
 96    
 97             Console.WriteLine($"客户端[{e.Client.ClientId}]已连接,协议版本:{e.Client.ProtocolVersion}");
 98             connectedClientId.Add(e.Client.ClientId);
 99         }
100 
101         private static void MqttServer_ClientDisconnected(object sender, MqttClientDisconnectedEventArgs e)
102         { 
103    
104             Console.WriteLine($"客户端[{e.Client.ClientId}]已断开连接!");
105             connectedClientId.Remove(e.Client.ClientId);
106         }
107 
108         private static void MqttServer_ApplicationMessageReceived(object sender, MqttApplicationMessageReceivedEventArgs e)
109         { 
110    
111             Console.WriteLine("### RECEIVED APPLICATION MESSAGE ###");
112             Console.WriteLine($"客户端[{e.ClientId}]>>");
113             Console.WriteLine($"+ Topic = {e.ApplicationMessage.Topic}");
114             Console.WriteLine($"+ Payload = {Encoding.UTF8.GetString(e.ApplicationMessage.Payload)}");
115             Console.WriteLine($"+ QoS = {e.ApplicationMessage.QualityOfServiceLevel}");
116             Console.WriteLine($"+ Retain = {e.ApplicationMessage.Retain}");
117             Console.WriteLine();
118         }
119 
120         private static void MqttNetTrace_TraceMessagePublished(object sender, MqttNetLogMessagePublishedEventArgs e)
121         { 
122    
123             var trace = $">> [{e.TraceMessage.Timestamp:O}] [{e.TraceMessage.ThreadId}] [{e.TraceMessage.Source}] [{e.TraceMessage.Level}]: {e.TraceMessage.Message}";
124             if (e.TraceMessage.Exception != null)
125             { 
126    
127                 trace += Environment.NewLine + e.TraceMessage.Exception.ToString();
128             }
129 
130             Console.WriteLine(trace);
131         }
132 
133         #region 2.7.5
134 
135         private static async Task StartMqttServer_2_7_5()
136         { 
137    
138             if (mqttServer == null)
139             { 
140    
141                 // Configure MQTT server.
142                 var optionsBuilder = new MqttServerOptionsBuilder()
143                     .WithConnectionBacklog(100)
144                     .WithDefaultEndpointPort(8222)
145                     .WithConnectionValidator(ValidatingMqttClients())
146                     ;
147 
148                 // Start a MQTT server.
149                 mqttServer = new MqttFactory().CreateMqttServer();
150                 mqttServer.ApplicationMessageReceived += MqttServer_ApplicationMessageReceived;
151                 mqttServer.ClientConnected += MqttServer_ClientConnected;
152                 mqttServer.ClientDisconnected += MqttServer_ClientDisconnected;
153 
154                 Task.Run(async () => { 
155     await mqttServer.StartAsync(optionsBuilder.Build()); });
156                 //mqttServer.StartAsync(optionsBuilder.Build());
157                 Console.WriteLine("MQTT服务启动成功!");
158             }
159         }
160 
161         private static async Task EndMqttServer_2_7_5()
162         { 
163    
164             if (mqttServer!=null)
165             { 
166    
167                 await mqttServer.StopAsync();
168             }
169             else
170             { 
171    
172                 Console.WriteLine("mqttserver=null");
173             }
174         }
175 
176         private static Action<MqttConnectionValidatorContext> ValidatingMqttClients()
177         { 
178    
179             // Setup client validator. 
180             var options =new MqttServerOptions();
181             options.ConnectionValidator = c =>
182             { 
183    
184                 Dictionary<string, string> c_u = new Dictionary<string, string>();
185                 c_u.Add("client001", "username001");
186                 c_u.Add("client002", "username002");
187                 Dictionary<string, string> u_psw = new Dictionary<string, string>();
188                 u_psw.Add("username001", "psw001");
189                 u_psw.Add("username002", "psw002");
190 
191                 if (c_u.ContainsKey(c.ClientId) && c_u[c.ClientId] == c.Username)
192                 { 
193    
194                     if (u_psw.ContainsKey(c.Username) && u_psw[c.Username] == c.Password)
195                     { 
196    
197                         c.ReturnCode = MqttConnectReturnCode.ConnectionAccepted;
198                     }
199                     else
200                     { 
201    
202                         c.ReturnCode = MqttConnectReturnCode.ConnectionRefusedBadUsernameOrPassword;
203                     }
204                 }
205                 else
206                 { 
207    
208                     c.ReturnCode = MqttConnectReturnCode.ConnectionRefusedIdentifierRejected;
209                 }
210             };
211             return options.ConnectionValidator;
212         }
213         
214         private static void Usingcertificate(ref MqttServerOptions options)
215         { 
216    
217             var certificate = new X509Certificate(@"C:\certs\test\test.cer", "");
218             options.TlsEndpointOptions.Certificate = certificate.Export(X509ContentType.Cert);
219             var aes = new System.Security.Cryptography.AesManaged();
220 
221         }
222 
223         #endregion
224 
225         #region Topic
226 
227         private static async void Topic_Hello(string msg)
228         { 
229    
230             string topic = "topic/hello";
231 
232             //2.4.0版本的
233             //var appMsg = new MqttApplicationMessage(topic, Encoding.UTF8.GetBytes(inputString), MqttQualityOfServiceLevel.AtMostOnce, false);
234             //mqttClient.PublishAsync(appMsg);
235 
236             var message = new MqttApplicationMessageBuilder()
237                 .WithTopic(topic)
238                 .WithPayload(msg)
239                 .WithAtMostOnceQoS()
240                 .WithRetainFlag()
241                 .Build();
242             await mqttServer.PublishAsync(message);
243         }
244 
245         private static async void Topic_Host_Control(string msg)
246         { 
247    
248             string topic = "topic/host/control";
249 
250             var message = new MqttApplicationMessageBuilder()
251                 .WithTopic(topic)
252                 .WithPayload(msg)
253                 .WithAtMostOnceQoS()
254                 .WithRetainFlag(false)
255                 .Build();
256             await mqttServer.PublishAsync(message);
257         }
258 
259         /// <summary>
260         /// 替指定的clientID订阅指定的内容
261         /// </summary>
262         /// <param name="topic"></param>
263         private static void Subscribe(string topic)
264         { 
265    
266             List<TopicFilter> topicFilter = new List<TopicFilter>();
267             topicFilter.Add(new TopicFilterBuilder()
268                 .WithTopic(topic)
269                 .WithAtMostOnceQoS()
270                 .Build());
271             //给"client001"订阅了主题为topicFilter的payload
272             mqttServer.SubscribeAsync("client001", topicFilter);
273             Console.WriteLine($"Subscribe:[{"client001"}],Topic:{topic}");
274         }
275 
276         #endregion
277     }
278 }

3.2 客户端

未完待续

参考文献

1、《使用 MQTTnet 快速实现 MQTT 通信》:链接 这篇文章是vs2017+.net core+mqttnet2.4.0的,目前库已经更新了好几个版本,如果用最新版的不能直接运行文章里的程序。 2、MQTT官网 3、开源库地址:MQTTnet 4、开源库对应文档:https://github.com/chkr1011/MQTTnet/wiki/Client

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/152873.html原文链接:https://javaforall.cn