MQTT消息使用場景廣泛,在本地做了個小demo。服務器使用的MQTTnet 3.1.2版本,最新4.1.0版本服務器權限認證網上沒找到,所以就使用3.1.2版本了。
一、服務端
引用MQTTnet 3.1.2版本
using MQTT; using MQTTnet.Client; using MQTTnet.Protocol; using MQTTnet; using MQTTnet.Server; using System; using System.Collections.Generic; using System.Text; using System.Threading; using System.Threading.Tasks; using MQTTnet.Client.Receiving; namespace MQTT { internal class Program { static void Main(string[] args) { MqttServerClass serverClass = new MqttServerClass(); serverClass.StartMqttServer().Wait(); Console.ReadLine(); } } public static class Config { public static int Port { get; set; } = 1883; public static string UserName { get; set; } = "cyw"; public static string Password { get; set; } = "123456"; } public class UserInstance { public string ClientId { get; set; } public string UserName { get; set; } public string Password { get; set; } } public class MqttServerClass { private IMqttServer mqttServer; private List<MqttApplicationMessage> messages = new List<MqttApplicationMessage>(); public async Task StartMqttServer() { try { if (mqttServer == null) { var optionsBuilder = new MqttServerOptionsBuilder() .WithDefaultEndpoint() .WithDefaultEndpointPort(Config.Port) .WithConnectionValidator( c => { var flag = c.Username == Config.UserName && c.Password == Config.Password; if (!flag) { c.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword; return; } //設置代碼爲 Success c.ReasonCode = MqttConnectReasonCode.Success; //instances.Add(new UserInstance() //緩存到內存的List集合當中 //{ // ClientId = c.ClientId, // UserName = c.Username, // Password = c.Password //}); }) //訂閱攔截器 .WithSubscriptionInterceptor( c => { if (c == null) return; c.AcceptSubscription = true; }) //應用程序消息攔截器 .WithApplicationMessageInterceptor( c => { if (c == null) return; c.AcceptPublish = true; }) //clean sesison是否生效 .WithPersistentSessions(); mqttServer = new MqttFactory().CreateMqttServer(); //客戶端斷開連接攔截器 //mqttServer.UseClientDisconnectedHandler(c => //{ // //var user = instances.FirstOrDefault(t => t.ClientId == c.ClientId); // //if (user != null) // //{ // // instances.Remove(user); // //} //}); //服務開始 mqttServer.StartedHandler = new MqttServerStartedHandlerDelegate(OnMqttServerStarted); //服務停止 mqttServer.StoppedHandler = new MqttServerStoppedHandlerDelegate(OnMqttServerStopped); //客戶端連接 mqttServer.ClientConnectedHandler = new MqttServerClientConnectedHandlerDelegate(OnMqttServerClientConnected); //客戶端斷開連接(此事件會覆蓋攔截器) mqttServer.ClientDisconnectedHandler = new MqttServerClientDisconnectedHandlerDelegate(OnMqttServerClientDisconnected); //客戶端訂閱 mqttServer.ClientSubscribedTopicHandler = new MqttServerClientSubscribedTopicHandlerDelegate(OnMqttServerClientSubscribedTopic); //客戶端取消訂閱 mqttServer.ClientUnsubscribedTopicHandler = new MqttServerClientUnsubscribedTopicHandlerDelegate(OnMqttServerClientUnsubscribedTopic); //服務端收到消息 mqttServer.ApplicationMessageReceivedHandler = new MqttApplicationMessageReceivedHandlerDelegate(OnMqttServerApplicationMessageReceived); await mqttServer.StartAsync(optionsBuilder.Build()); //主動發送消息到客戶端 //await mqttServer.PublishAsync(new // MqttApplicationMessage //{ // Topic = "testtopic", // Payload = Encoding.UTF8.GetBytes("dsdsd") //}); //mqttServer.GetClientStatusAsync(); //mqttServer.GetRetainedApplicationMessagesAsync(); //mqttServer.GetSessionStatusAsync(); } } catch (Exception ex) { Console.WriteLine($"MQTT Server start fail.>{ex.Message}"); } } private void OnMqttServerStarted(EventArgs e) { if (mqttServer.IsStarted) { Console.WriteLine("MQTT服務啓動完成!"); } } private void OnMqttServerStopped(EventArgs e) { if (!mqttServer.IsStarted) { Console.WriteLine("MQTT服務停止完成!"); } } private void OnMqttServerClientConnected(MqttServerClientConnectedEventArgs e) { Console.WriteLine($"客戶端[{e.ClientId}]已連接"); } private void OnMqttServerClientDisconnected(MqttServerClientDisconnectedEventArgs e) { Console.WriteLine($"客戶端[{e.ClientId}]已斷開連接!"); } private void OnMqttServerClientSubscribedTopic(MqttServerClientSubscribedTopicEventArgs e) { Console.WriteLine($"客戶端[{e.ClientId}]已成功訂閱主題[{e.TopicFilter}]!"); } private void OnMqttServerClientUnsubscribedTopic(MqttServerClientUnsubscribedTopicEventArgs e) { Console.WriteLine($"客戶端[{e.ClientId}]已成功取消訂閱主題[{e.TopicFilter}]!"); } private void OnMqttServerApplicationMessageReceived(MqttApplicationMessageReceivedEventArgs e) { messages.Add(e.ApplicationMessage); Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")); Console.WriteLine($"客戶端[{e.ClientId}]>> Topic[{e.ApplicationMessage.Topic}] Payload[{Encoding.UTF8.GetString(e.ApplicationMessage.Payload ?? new byte[] { })}] Qos[{e.ApplicationMessage.QualityOfServiceLevel}] Retain[{e.ApplicationMessage.Retain}]"); } } }
二、訂閱消息
引用的MQTTnet最新4.1.0版本,新增MqttClientService類,實例化client。然後連接到服務端後訂閱topic。
using MQTTnet; using MQTTnet.Client; using MQTTnet.Protocol; using System; using System.Collections.Generic; using System.Text; using System.Threading.Tasks; namespace MQTTClient { public class MqttClientService { public static IMqttClient _mqttClient; public IMqttClient MqttClientStart(string ip, int port, string username, string pwd, string clientId) { var optionsBuilder = new MqttClientOptionsBuilder() .WithTcpServer(ip, port) // 要訪問的mqtt服務端的 ip 和 端口號 .WithCredentials(username, pwd) // 要訪問的mqtt服務端的用戶名和密碼 .WithClientId(clientId) // 設置客戶端id .WithCleanSession() .WithTls(new MqttClientOptionsBuilderTlsParameters { UseTls = false // 是否使用 tls加密 }); var clientOptions = optionsBuilder.Build(); _mqttClient = new MqttFactory().CreateMqttClient(); //_mqttClient.ConnectedAsync += _mqttClient_ConnectedAsync; // 客戶端連接成功事件 //_mqttClient.DisconnectedAsync += _mqttClient_DisconnectedAsync; // 客戶端連接關閉事件 //_mqttClient.ApplicationMessageReceivedAsync += _mqttClient_ApplicationMessageReceivedAsync; // 收到消息事件 _mqttClient.ConnectAsync(clientOptions); return _mqttClient; } } }
using MQTTnet.Client; using MQTTnet.Protocol; using System; using System.Text; using System.Threading.Tasks; namespace MQTTClient { internal class Program { private static IMqttClient client; static void Main(string[] args) { Console.WriteLine("Hello World!"); client = new MqttClientService().MqttClientStart("127.0.0.1", 1883, "cyw", "123456", "cyw"); client.ConnectedAsync += Client_ConnectedAsync; client.ApplicationMessageReceivedAsync += Client_ApplicationMessageReceivedAsync; client.DisconnectedAsync += Client_DisconnectedAsync; Console.ReadLine(); } private static Task Client_DisconnectedAsync(MqttClientDisconnectedEventArgs arg) { Console.WriteLine("Client_DisconnectedAsync"); return Task.CompletedTask; } private static Task Client_ApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs arg) { Console.WriteLine($"ApplicationMessageReceivedAsync:客戶端ID=【{arg.ClientId}】接收到消息。 Topic主題=【{arg.ApplicationMessage.Topic}】 消息=【{Encoding.UTF8.GetString(arg.ApplicationMessage.Payload)}】 qos等級=【{arg.ApplicationMessage.QualityOfServiceLevel}】"); return Task.CompletedTask; } private static System.Threading.Tasks.Task Client_ConnectedAsync(MqttClientConnectedEventArgs arg) { client.SubscribeAsync("test", MqttQualityOfServiceLevel.AtLeastOnce); Console.WriteLine("Client_ConnectedAsync"); return Task.CompletedTask; } } }
三、發送消息
引用的MQTTnet最新4.1.0版本,新增MqttClientService類,實例化client。然後連接到服務端後向指定topic發送消息。
using MQTTnet; using MQTTnet.Client; using MQTTnet.Protocol; using System; using System.Collections.Generic; using System.Text; using System.Threading.Tasks; namespace MQTTPublish { public class MqttClientService { public static IMqttClient _mqttClient; public IMqttClient MqttClientStart(string ip, int port, string username, string pwd, string clientId) { var optionsBuilder = new MqttClientOptionsBuilder() .WithTcpServer(ip, port) // 要訪問的mqtt服務端的 ip 和 端口號 .WithCredentials(username, pwd) // 要訪問的mqtt服務端的用戶名和密碼 .WithClientId(clientId) // 設置客戶端id .WithCleanSession() .WithTls(new MqttClientOptionsBuilderTlsParameters { UseTls = false // 是否使用 tls加密 }); var clientOptions = optionsBuilder.Build(); _mqttClient = new MqttFactory().CreateMqttClient(); //_mqttClient.ConnectedAsync += _mqttClient_ConnectedAsync; // 客戶端連接成功事件 //_mqttClient.DisconnectedAsync += _mqttClient_DisconnectedAsync; // 客戶端連接關閉事件 //_mqttClient.ApplicationMessageReceivedAsync += _mqttClient_ApplicationMessageReceivedAsync; // 收到消息事件 _mqttClient.ConnectAsync(clientOptions); return _mqttClient; } } }
using MQTTnet; using MQTTnet.Client; using MQTTnet.Protocol; using MQTTnet.Server; using System; using System.Text; using System.Threading.Tasks; namespace MQTTPublish { internal class Program { private static IMqttClient client; static void Main(string[] args) { Console.WriteLine("Hello World!"); client = new MqttClientService().MqttClientStart("127.0.0.1", 1883, "cyw", "123456", "cywcyw"); client.ConnectedAsync += Client_ConnectedAsync; Console.ReadLine(); } private static System.Threading.Tasks.Task Client_ConnectedAsync(MqttClientConnectedEventArgs arg) { Console.WriteLine("Client_ConnectedAsync"); var message = new MqttApplicationMessage { Topic = "test", Payload = Encoding.Default.GetBytes("cyw-123456"), QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce, Retain = true // 服務端是否保留消息。true爲保留,如果有新的訂閱者連接,就會立馬收到該消息。 }; Console.WriteLine("PublishAsync topic:"+ message.Topic+ " Payload:"+ Encoding.UTF8.GetString(message.Payload)); client.PublishAsync(message); return Task.CompletedTask; } } }
四、測試
依次啓動服務端,訂閱消息客戶端和發送消息客戶端。
demo地址
MQTTDemo.rar