使用 MQTTnet 实现 MQTT 通信示例
2023-10-23 10:51 古兆洋 阅读(1599) 评论(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 开源库还有 MqttDotNet、nMQTT、M2MQTT 等,不过这几个里面,目前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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了