C#MQTT编程07--MQTT服务器和客户端(wpf版)

1、前言

上篇完成了winform版的mqtt服务器和客户端,实现了订阅和发布,效果666,长这样

 这节要做的wpf版,长这样,效果也是帅BBBB帅,wpf技术是cs程序软件的福音。

 wpf的基础知识和案例项目可以看我的另一个专栏系列文章,这里直接干搞,开发环境依然是vs2022,.netframework 4.8,mqttnet3.x。

WPF真入门教程

2、服务器搭建

1、创建项目方案

 2、添加包组件MQTTNET 

3、创建相关的目录及文件 

 

样式文件CommonStyle.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"><Style x:Key="ButtonStyle" TargetType="Button"><Setter Property="Background" Value="#3F85FF"></Setter><Setter Property="Foreground" Value="White"></Setter><Setter Property="FocusVisualStyle" Value="{x:Null}"></Setter><Setter Property="Margin" Value="5"></Setter><Setter Property="FontSize" Value="16"></Setter><!--模板的样式--><Setter Property="Template"><Setter.Value><!--Button单选按钮样式--><ControlTemplate TargetType="Button"><Grid ><Border Background="{TemplateBinding Background}" CornerRadius="5" ><TextBlock Margin="10 5 10 5" Text="{TemplateBinding Content}" FontSize="{TemplateBinding FontSize}" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock></Border></Grid><ControlTemplate.Triggers><!--鼠标放上去时的触发器--><Trigger Property="IsMouseOver" Value="True"><Setter Property="Background" Value="DarkOliveGreen" ></Setter></Trigger></ControlTemplate.Triggers> </ControlTemplate></Setter.Value></Setter></Style>
</ResourceDictionary>

4、设置UI布局界面 

 

<Window x:Class="MQTTNETServerWPF.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:local="clr-namespace:MQTTNETServerWPF.ViewModel"mc:Ignorable="d" Background="Transparent" WindowStartupLocation="CenterScreen"FontSize="13" FontFamily="Microsoft YaHei" FontWeight="ExtraLight" Foreground="#333"Title="MainWindow" Height="550" Width="890"><WindowChrome.WindowChrome><WindowChrome GlassFrameThickness="-1"/></WindowChrome.WindowChrome><Window.DataContext><local:MainWindowViewModel/></Window.DataContext><Grid ShowGridLines="true"  ><Grid.RowDefinitions><RowDefinition Height="70"/><RowDefinition/></Grid.RowDefinitions><TextBlock Grid.Row="0" FontWeight="Bold" Background="BlanchedAlmond"  Text="WPF版MQTT服务器程序" FontSize="25" VerticalAlignment="Center" Margin="6,20,0,0" Foreground="#666"   /><Grid Grid.Row="1"><Grid.ColumnDefinitions><ColumnDefinition Width="220"/><ColumnDefinition/></Grid.ColumnDefinitions><Border BorderBrush="#EEE" BorderThickness="0,0,1,0"/><!--左侧布局--><StackPanel Grid.Column="0" Margin="20" ><TextBlock Text="主机地址"/><TextBox Text="{Binding Server.ServerIP}" Height="30" VerticalContentAlignment="Center" Padding="5,0" Margin="0,10"  /><TextBlock Text="端口号" Margin="0,10,0,0"/><TextBox Text="{Binding Server.ServerPort}" Height="30" VerticalContentAlignment="Center" Padding="5,0" Margin="0,10"  /><TextBlock Text="连接账号" Margin="0,10,0,0"/><TextBox Text="{Binding Server.ServerName}"  Height="30" VerticalContentAlignment="Center" Padding="5,0" Margin="0,10"    /><TextBlock Text="连接密码" Margin="0,10,0,0"/><TextBox Text="{Binding Server.ServerPwd}" Height="30" VerticalContentAlignment="Center" Padding="5,0" Margin="0,10"  /><Button Content="启动服务" Margin="0,30,0,0" Height="30"  Command="{Binding StartCommand}" Style="{StaticResource ButtonStyle}" /><Button Content="停止服务" Margin="0,10" Height="30"  Command="{Binding StopCommand}"  Style="{StaticResource ButtonStyle}"/></StackPanel><!--右侧布局--><Grid Grid.Column="1"><Grid.RowDefinitions><RowDefinition Height="2*"/><RowDefinition Height="3*"/></Grid.RowDefinitions><Grid.ColumnDefinitions><ColumnDefinition/><ColumnDefinition/></Grid.ColumnDefinitions><GridSplitter VerticalAlignment="Bottom" HorizontalAlignment="Stretch" Height="4" Background="#F7F9FA" Grid.ColumnSpan="2" Margin="0,0,3,0"/><Grid Margin="20,20,10,15"><Grid.RowDefinitions><RowDefinition Height="30"/><RowDefinition/></Grid.RowDefinitions><TextBlock Text="在线Client列表"/><ListBox Grid.Row="1"   ItemsSource="{Binding ClientsList}"/></Grid><Grid Margin="10,20,20,15" Grid.Column="1"><Grid.RowDefinitions><RowDefinition Height="30"/><RowDefinition/></Grid.RowDefinitions><TextBlock Text="Topic主题列表"/><ListView Grid.Row="1"   ItemsSource="{Binding TopicsList}"></ListView></Grid><Grid Grid.Row="1" Grid.ColumnSpan="2" Margin="20,10,20,20"><Grid.RowDefinitions><RowDefinition Height="30"/><RowDefinition/></Grid.RowDefinitions><TextBlock Text="消息"/><TextBox  Grid.Row="1"   x:Name="txtRich" ToolTip="右键清理内容" Text="{Binding ConnectWords}" Height="200" Background="White" VerticalContentAlignment="Top" Padding="3,0" Margin="10,9,10,10"    ><!--添加一个右键菜单的功能,即清空--><TextBox.ContextMenu><ContextMenu><MenuItem x:Name="menuClear" Click="miClear_Click"  Header="清空内容"></MenuItem></ContextMenu></TextBox.ContextMenu></TextBox></Grid></Grid></Grid></Grid>
</Window>

5、视图模型,属性绑定和命令绑定

完整代码:

using MQTTnet.Client.Receiving;
using MQTTnet;
using MQTTnet.Server;
using MQTTNETServerWPF.Command;
using MQTTNETServerWPF.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Collections.ObjectModel;
using MQTTnet.Certificates;
using MQTTnet.Protocol;
using System.Runtime.Remoting.Messaging;namespace MQTTNETServerWPF.ViewModel
{public class MainWindowViewModel : ViewModelBase{private IMqttServer mqttserver;//mqtt服务器List<TopicItem> Topics = new List<TopicItem>();public MainWindowViewModel(){//创建服务器对象mqttserver = new MqttFactory().CreateMqttServer();mqttserver.ApplicationMessageReceivedHandler =new MqttApplicationMessageReceivedHandlerDelegate(new Action<MqttApplicationMessageReceivedEventArgs>(Server_ApplicationMessageReceived));//绑定消息接收事件mqttserver.ClientConnectedHandler =new MqttServerClientConnectedHandlerDelegate(new Action<MqttServerClientConnectedEventArgs>(Server_ClientConnected));//绑定客户端连接事件mqttserver.ClientDisconnectedHandler = new MqttServerClientDisconnectedHandlerDelegate(new Action<MqttServerClientDisconnectedEventArgs>(Server_ClientDisconnected));//绑定客户端断开事件mqttserver.ClientSubscribedTopicHandler = new MqttServerClientSubscribedHandlerDelegate(new Action<MqttServerClientSubscribedTopicEventArgs>(Server_ClientSubscribedTopic));//绑定客户端订阅主题事件mqttserver.ClientUnsubscribedTopicHandler = new MqttServerClientUnsubscribedTopicHandlerDelegate(new Action<MqttServerClientUnsubscribedTopicEventArgs>(Server_ClientUnsubscribedTopic));//绑定客户端退订主题事件mqttserver.StartedHandler = new MqttServerStartedHandlerDelegate(new Action<EventArgs>(Server_Started));//绑定服务端启动事件mqttserver.StoppedHandler = new MqttServerStoppedHandlerDelegate(new Action<EventArgs>(Server_Stopped));//绑定服务端停止事件}#region 方法/// 绑定消息接收事件/// </summary>/// <param name="e"></param>private void Server_ApplicationMessageReceived(MqttApplicationMessageReceivedEventArgs e){string msg = e.ApplicationMessage.ConvertPayloadToString();WriteLog(">>> 收到消息:" + msg + ",QoS =" + e.ApplicationMessage.QualityOfServiceLevel + ",客户端=" + e.ClientId + ",主题:" + e.ApplicationMessage.Topic);}/// <summary>/// 绑定客户端连接事件/// </summary>/// <param name="e"></param>private void Server_ClientConnected(MqttServerClientConnectedEventArgs e){Task.Run(() =>{App.Current.Dispatcher.Invoke(() =>{this.ClientsList.Add(e.ClientId);});WriteLog(">>> 客户端" + e.ClientId + "连接");});}/// <summary>/// 绑定客户端断开事件/// </summary>/// <param name="e"></param>private void Server_ClientDisconnected(MqttServerClientDisconnectedEventArgs e){Task.Run(() =>{App.Current.Dispatcher.Invoke(() =>{this.ClientsList.Remove(e.ClientId);});WriteLog(">>> 客户端" + e.ClientId + "断开");});}/// <summary>/// 绑定客户端订阅主题事件/// </summary>/// <param name="e"></param>private void Server_ClientSubscribedTopic(MqttServerClientSubscribedTopicEventArgs e){Task.Run(() =>{App.Current.Dispatcher.Invoke(() =>{var topic = Topics.FirstOrDefault(t => t.Topic == e.TopicFilter.Topic);if (topic == null){topic = new TopicItem { Topic = e.TopicFilter.Topic, Count = 0 };Topics.Add(topic);}if (!topic.Clients.Exists(c => c == e.ClientId)){topic.Clients.Add(e.ClientId);topic.Count++;}this.TopicsList.Clear();foreach (var item in this.Topics){this.TopicsList.Add($"{item.Topic}:{item.Count}");}});WriteLog(">>> 客户端" + e.ClientId + "订阅主题" + e.TopicFilter.Topic);});}/// <summary>/// 绑定客户端退订主题事件/// </summary>/// <param name="e"></param>private void Server_ClientUnsubscribedTopic(MqttServerClientUnsubscribedTopicEventArgs e){Task.Run(() =>{App.Current.Dispatcher.Invoke(() =>{var topic = Topics.FirstOrDefault(t => t.Topic == e.TopicFilter);if (topic != null){topic.Count--;topic.Clients.Remove(e.ClientId);}this.TopicsList.Clear();foreach (var item in this.Topics){this.TopicsList.Add($"{item.Topic}:{item.Count}");}});WriteLog(">>> 客户端" + e.ClientId + "退订主题" + e.TopicFilter);});}/// <summary>/// 绑定服务端启动事件/// </summary>/// <param name="e"></param>private void Server_Started(EventArgs e){WriteLog(">>> 服务端已启动!");}/// <summary>/// 绑定服务端停止事件/// </summary>/// <param name="e"></param>private void Server_Stopped(EventArgs e){WriteLog(">>> 服务端已停止!");}/// <summary>/// 显示日志/// </summary>/// <param name="message"></param>public void WriteLog(string message){Task.Run(() =>{App.Current.Dispatcher.Invoke(() =>{ConnectWords = message + "\r";});});}#endregion#region 属性private MqttServerModel server = new MqttServerModel("127.0.0.1", "1869", "boss", "1234");//服务器实体/// <summary>/// 当前服务器对象/// </summary>public MqttServerModel Server{get { return server; }set{server = value;OnPropertyChanged();}}private string connectWords = "";/// <summary>/// 连接状态/// </summary>public string ConnectWords{get { return connectWords; }set{connectWords = value;OnPropertyChanged();}}private ObservableCollection<string> clientsList = new ObservableCollection<string>();/// <summary>/// 客户列表/// </summary>public ObservableCollection<string> ClientsList{get { return clientsList; }set{clientsList = value;OnPropertyChanged();}}private ObservableCollection<string> topicsList = new ObservableCollection<string>();/// <summary>/// 主题列表/// </summary>public ObservableCollection<string> TopicsList{get { return topicsList; }set{topicsList = value;OnPropertyChanged();}}#endregion#region 命令/// <summary>/// 启动命令/// </summary>[Obsolete]public ICommand StartCommand{get{return new RelayCommand(async o =>{var optionBuilder = new MqttServerOptionsBuilder().WithDefaultEndpointBoundIPAddress(System.Net.IPAddress.Parse(Server.ServerIP)).WithDefaultEndpointPort(int.Parse(Server.ServerPort)).WithDefaultCommunicationTimeout(TimeSpan.FromMilliseconds(5000)).WithConnectionValidator(t =>{string un = "", pwd = "";un = Server.ServerName;pwd = Server.ServerPwd;if (t.Username != un || t.Password != pwd){t.ReturnCode = MqttConnectReturnCode.ConnectionRefusedBadUsernameOrPassword;}else{t.ReturnCode = MqttConnectReturnCode.ConnectionAccepted;}});var option = optionBuilder.Build();//启动await mqttserver.StartAsync(option);});}}/// <summary>/// 启动命令/// </summary>[Obsolete]public ICommand StopCommand{get{return new RelayCommand(async o =>{ if (server != null){await mqttserver.StopAsync();} });}}#endregion}
}

 注意一个地方,就是给文本框添加了一个右键“清空”的功能,看看是怎么样实现的?

 6、启动测试服务器

启动成功,服务器kokokokoko!!!!

 3、客户端创建

1、添加项目MQTTNETClientWPF

2、添加客户端的组件

 

3、创建相关的类文件及目录 

 

4、设计UI布局

 布局仔细 看下

<Window x:Class="MQTTNETClientWPF.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:local="clr-namespace:MQTTNETClientWPF.ViewModel"mc:Ignorable="d" Background="Transparent" WindowStartupLocation="CenterScreen"FontSize="13" FontFamily="Microsoft YaHei" FontWeight="ExtraLight" Foreground="#333"Title="MainWindow" Height="600" Width="850"><WindowChrome.WindowChrome><WindowChrome GlassFrameThickness="-1"/></WindowChrome.WindowChrome><Window.DataContext><local:MainWindowViewModel/></Window.DataContext><Grid  ShowGridLines="true" ><Grid.RowDefinitions><RowDefinition Height="70"/><RowDefinition/></Grid.RowDefinitions><TextBlock Grid.Row="0" FontWeight="Bold"  Text="WPF版MQTT客户端程序" FontSize="25" VerticalAlignment="Center" Margin="6,20,0,0" Foreground="#666" Background="BlanchedAlmond"  /><Grid Grid.Row="1"><Grid.ColumnDefinitions><ColumnDefinition Width="220"/><ColumnDefinition/></Grid.ColumnDefinitions><Border BorderBrush="#EEE" BorderThickness="0,0,1,0"/><StackPanel Margin="20"><TextBlock Text="主机地址"/><TextBox Text="{Binding Client.ServerIP}" Height="30" VerticalContentAlignment="Center" Padding="5,0" Margin="0,10" Name="tbHostAddr"/><TextBlock Text="端口号" Margin="0,5,0,0"/><TextBox  Text="{Binding Client.ServerPort}" Height="30" VerticalContentAlignment="Center" Padding="5,0" Margin="0,10"   Name="tbHostPort"/><TextBlock Text="连接账号" Margin="0,5,0,0"/><TextBox  Text="{Binding Client.ServerName}" Height="30" VerticalContentAlignment="Center" Padding="5,0" Margin="0,10" Name="tbUsername"/><TextBlock Text="连接密码" Margin="0,5,0,0"/><TextBox Text="{Binding Client.ServerPwd}" Height="30" VerticalContentAlignment="Center" Padding="5,0" Margin="0,10"  Name="tbPassword"/><TextBlock Text="客户端ID" Margin="0,5,0,0"/><TextBox Text="{Binding Client.ClientId}" Height="30" VerticalContentAlignment="Center" Padding="5,0" Margin="0,10"   Name="tbClientId"/><Button Content="连接" Margin="0,30,0,0" Height="30" Command="{Binding OpenCommand}"  Style="{StaticResource ButtonStyle}"/><Button Content="断开" Margin="0,10" Height="30"  Command="{Binding CloseCommand}" Style="{StaticResource ButtonStyle}"/></StackPanel><Grid Grid.Column="1" Margin="20,10"><Grid.RowDefinitions><RowDefinition Height="auto"/><RowDefinition Height="auto"/><RowDefinition/></Grid.RowDefinitions><Grid><Grid.RowDefinitions><RowDefinition Height="30"/><RowDefinition Height="50"/></Grid.RowDefinitions><Grid.ColumnDefinitions><ColumnDefinition Width="auto"/><ColumnDefinition/><ColumnDefinition Width="auto"/></Grid.ColumnDefinitions><Border Background="#F7F9FA" Grid.ColumnSpan="3"/><TextBlock Text="订阅" VerticalAlignment="Center" Margin="5,0"/><TextBlock Text="主题" Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="5,0"/><TextBox Text="{Binding Topic}" Grid.Row="1" Height="30" Padding="5,0" VerticalContentAlignment="Center" Grid.Column="1"  Name="tbTopic"/><Button Width="50" Grid.Row="1" Height="30" Content="订阅" Grid.Column="2" Margin="5,10,0,10"   Command="{Binding SubscriteCommand}"  Style="{StaticResource ButtonStyle}"  HorizontalAlignment="Left"/></Grid><Grid Grid.Row="1" Margin="0,20"><Grid.RowDefinitions><RowDefinition Height="30"/><RowDefinition Height="50"/><RowDefinition Height="30"/><RowDefinition/></Grid.RowDefinitions><Grid.ColumnDefinitions><ColumnDefinition Width="auto"/><ColumnDefinition/><ColumnDefinition Width="auto"/></Grid.ColumnDefinitions><Border Background="#F7F9FA" Grid.ColumnSpan="3"/><TextBlock Text="发布" VerticalAlignment="Center" Margin="5,0"/><TextBlock Text="主题" Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="5,0"/><TextBox Text="{Binding Topic}" Grid.Row="1" Height="30" Padding="5,0" VerticalContentAlignment="Center" Grid.Column="1"   Name="tbPubTopic"/><TextBlock Text="内容" Grid.Row="2" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="5,0"/><TextBox Text="{Binding Pubmsg}"  Grid.Row="2" Height="30" Padding="5,0" VerticalContentAlignment="Center" Grid.Column="1"  Name="tbContent"/><Button Width="50" Grid.Row="2" Height="30"  Content="发布" Grid.Column="2" Margin="5,0,5,0"  Command="{Binding PublishCommand}"  Style="{StaticResource ButtonStyle}"  VerticalAlignment="Top"/></Grid><Grid Grid.Row="2" Margin="0,10,0,0"><Grid.RowDefinitions><RowDefinition Height="30"/><RowDefinition/></Grid.RowDefinitions><Border Background="#F7F9FA" Grid.ColumnSpan="3"/><TextBlock Text="消息" VerticalAlignment="Center" Margin="5,0"/><TextBox  Grid.Row="1"  x:Name="txtRich" ToolTip="右键清理内容" Text="{Binding ConnectWords}" Height="200"  Background="White" VerticalContentAlignment="Top"  Padding="3,0" Margin="10,9,75,10"  ><!--添加一个右键菜单的功能,即清空--><TextBox.ContextMenu><ContextMenu><MenuItem x:Name="menuClear" Click="miClear_Click"  Header="清空内容"></MenuItem></ContextMenu></TextBox.ContextMenu></TextBox></Grid></Grid></Grid></Grid>
</Window>

5、视图模型viewmodel

模型类的属性绑定和命令绑定,数据驱动控件,即Mvvm渲染方法

using MQTTnet.Client.Options;
using MQTTnet.Client;
using MQTTnet.Extensions.ManagedClient;
using MQTTNETClientWPF.Command;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using MQTTNETClientWPF.Model;
using MQTTnet;namespace MQTTNETClientWPF.ViewModel
{public class MainWindowViewModel : ViewModelBase{private IManagedMqttClient mqttClient; //mqtt客户端public MainWindowViewModel(){var factory = new MqttFactory();mqttClient = factory.CreateManagedMqttClient();//创建客户端对象//绑定断开事件mqttClient.UseDisconnectedHandler(async ee =>{WriteLog(DateTime.Now.ToString() + "与服务器之间的连接断开了,正在尝试重新连接");// 等待 5s 时间await Task.Delay(TimeSpan.FromSeconds(5));try{mqttClient.UseConnectedHandler(cc =>{WriteLog(">>> 连接到服务成功!");});}catch (Exception ex){Console.WriteLine($"重新连接服务器失败:{ex}");}});//绑定接收事件mqttClient.UseApplicationMessageReceivedHandler(aa =>{try{string msg = aa.ApplicationMessage.ConvertPayloadToString();WriteLog(">>> 消息:" + msg + ",QoS =" + aa.ApplicationMessage.QualityOfServiceLevel + ",客户端=" + aa.ClientId + ",主题:" + aa.ApplicationMessage.Topic);}catch (Exception ex){WriteLog($"+ 消息 = " + ex.Message);} });//绑定连接事件mqttClient.UseConnectedHandler(ee =>{WriteLog(">>> 连接到服务");});}/// <summary>/// 显示日志/// </summary>/// <param name="message"></param> public void WriteLog(string message){Task.Run(() =>{App.Current.Dispatcher.Invoke(() =>{ConnectWords = message + "\r";});});}#region 属性private MqttClientModel client = new MqttClientModel("127.0.0.1", "1869", "boss", "1234", "c1");//服务器实体/// <summary>/// 连接对象/// </summary>public MqttClientModel Client{get { return client; }set{client = value;OnPropertyChanged();}}private string connectWords = "";/// <summary>/// 连接状态/// </summary>public string ConnectWords{get { return connectWords; }set{connectWords = value;OnPropertyChanged();}}private string topic = "shanghai";/// <summary>/// 主题/// </summary>public string Topic{get { return topic; }set{topic = value;OnPropertyChanged();}}private string pubmsg = "0103";/// <summary>/// 发布/// </summary>public string Pubmsg{get { return pubmsg; }set{pubmsg = value;OnPropertyChanged();}}#endregion#region 命令/// <summary>/// 连接命令/// </summary> public ICommand OpenCommand{get{return new RelayCommand(async o =>{var mqttClientOptions = new MqttClientOptionsBuilder().WithClientId(this.Client.ClientId).WithTcpServer(this.Client.ServerIP, int.Parse(this.Client.ServerPort)).WithCredentials(this.Client.ServerName, this.Client.ServerPwd);var options = new ManagedMqttClientOptionsBuilder().WithAutoReconnectDelay(TimeSpan.FromSeconds(5)).WithClientOptions(mqttClientOptions.Build()).Build();//开启var t = mqttClientOptions;await mqttClient.StartAsync(options);});}}/// <summary>/// 断开命令/// </summary> public ICommand CloseCommand{get{return new RelayCommand(async o =>{if (mqttClient != null){if (mqttClient.IsStarted){await mqttClient.StopAsync();}mqttClient.Dispose();}});}}/// <summary>/// 订阅命令/// </summary> [Obsolete]public ICommand SubscriteCommand{get{return new RelayCommand(async o =>{if (string.IsNullOrWhiteSpace(this.Topic)){WriteLog(">>> 请输入主题");return;}//在 MQTT 中有三种 QoS 级别: //At most once(0) 最多一次//At least once(1) 至少一次//Exactly once(2) 恰好一次//await mqttClient.SubscribeAsync(new TopicFilterBuilder().WithTopic(this.tbTopic.Text).WithAtMostOnceQoS().Build());//最多一次, QoS 级别0await mqttClient.SubscribeAsync(new TopicFilterBuilder().WithTopic(this.Topic).WithAtLeastOnceQoS().Build());//恰好一次, QoS 级别1 WriteLog($">>> 成功订阅 {this.Topic}");});}}/// <summary>/// 发布命令/// </summary> public ICommand PublishCommand{get{return new RelayCommand(async o =>{if (string.IsNullOrWhiteSpace(this.Topic)){WriteLog(">>> 请输入主题");return;}var result = await mqttClient.PublishAsync(this.Topic,this.Pubmsg,MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce);//恰好一次, QoS 级别1 WriteLog($">>> 主题:{this.Topic},消息:{this.Pubmsg},结果: {result.ReasonCode}");});}}#endregion}
}

6、启动客户端

比较屌

4、测试mqtt

 

1、启动服务器,客户端连接成功

 2、测试订阅

3、测试发布

 再启动一个客户端程序,有人不知道如何启动,看下面

c1发布一个消息,看看c1,c2有没有收到,很明显都收到了

 服务器显示有关信息,完全good

基于mqttnet实现的wpf版通信,完美实现,效果飞起来了,颜值高,效果好,帅B得上了飞机。

5、完整代码打包下载 

链接:https://pan.baidu.com/s/1sfQnGEEcsRTBKUSDOdCeTA 
提取码:z2hj 

讲解不易,分析不易,原创不易,整理不易,伙伴们动动你的金手指,你的支持是我最大的动力。

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

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

相关文章

汽车科技迎来新纪元!奔驰、宝马、谷歌、亚马逊、高通的下一代布局!

不久的将来&#xff0c;你将能够直接通过汽车支付。为了减少支付服务的复杂性&#xff0c;高通正在与摩根大通合作设计一款车载钱包&#xff0c;简化支付体验。这将使驾驶员能够直接通过汽车支付流媒体内容、电动车充电、停车或得来速餐食物。 下一代智能车会是什么样子&#…

测试 ASP.NET Core 中间件

正常情况下&#xff0c;中间件会在主程序入口统一进行实例化&#xff0c;这样如果想单独测试某一个中间件就很不方便&#xff0c;为了能测试单个中间件&#xff0c;可以使用 TestServer 单独测试。 这样便可以&#xff1a; 实例化只包含需要测试的组件的应用管道。发送自定义请…

202408读书笔记|《半小时漫画红楼梦12》——鲜花着锦,烈火烹油,不是东风压了西风,就是西风压了东风

202408读书笔记|《半小时漫画红楼梦12》——鲜花着锦&#xff0c;烈火烹油&#xff0c;不是东风压了西风&#xff0c;就是西风压了东风 1. 关系图谱绘制2. 摘录3. 人物关系 1. 关系图谱绘制 https://blog.csdn.net/qq_40985985/article/details/127822673https://blog.csdn.ne…

代码随想录二刷 | 二叉树| 删除二叉搜索树中的节点

代码随想录二刷 &#xff5c; 二叉树&#xff5c; 删除二叉搜索树中的节点 题目描述解题思路代码实现 题目描述 450.删除二叉搜索树中的节点 给定一个二叉搜索树的根节点 root 和一个值 key&#xff0c;删除二叉搜索树中的 key 对应的节点&#xff0c;并保证二叉搜索树的性质…

小蓝的漆房*

题目 import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);int t sc.nextInt();sc.nextLine();int[] res new int[t];for(int i0;i<t;i) {int ans Integer.MAX_VALUE;int n sc.nextInt();int k…

C语言基础语法跟练 day4

41、牛牛有一个半径为 r 的球&#xff0c;他想知道这个球的体积。 #include <stdio.h> #include<math.h> int main() {float r;scanf("%f",&r);float v (4.0/3)*3.14*pow(r,3);printf("%.2f",v);return 0; } 42、小乐乐比较懒惰&#x…

MySQL运维篇(二)主从复制

一、概述 主从复制是指将主数据库的 DDL 和 DML 操作通过 二进制日志 传到从库服务器中&#xff0c;然后在从库上对这些日志重新执行&#xff08;也叫重做&#xff09;&#xff0c;从而使得从库和主库的数据保持同步。 MySQL 支持一台主库同时向多台从库进行复制&#xff0c; 从…

3d渲染农场有什么好处?为什么选择渲染农场?

3D渲染农场是专门用于3D图形制作的计算机集群&#xff0c;通常用来帮助电影、动画或视觉效果的渲染&#xff0c;它有什么好处为什么要选择它呢&#xff1f;这次我们一起来看看吧&#xff01; 1、时间效率 渲染农场能显著加快渲染速度&#xff0c;将原本需要数小时或数天的作业缩…

【车载HMI开发工具--EB GUIDE 与 Unity 合作提供一体化的沉浸式 HMI 设计开发工具链】【转载】

随着车载高性能计算平台的日益普及以及显示器尺寸和数量的不断增加&#xff0c;沉浸式车载人机交互界面&#xff08;HMI&#xff09;的需求也在持续增长。为了将实时 3D 技术带入车载 HMI 领域&#xff0c;Unity 与 Elektrobit (EB)展开了合作&#xff0c;EB 是推进 HMI 功能安…

【Linux系统编程】程序地址空间

进程地址空间 进程地址空间是指每个进程在计算机内存中所占用的地址空间。地址空间是指能被访问的内存地址范围&#xff0c;它由若干个连续的内存块组成。每个进程都有自己的地址空间&#xff0c;这意味着每个进程都有自己的内存地址范围&#xff0c;不会与其他进程冲突。进程地…

CTF伪随机数爆破

要了解伪随机数的爆破首先你的先知道什么是PHP种子&#xff0c; 借用在rand()函数中&#xff0c;我们可以通过设置随机数种子来影响随机数的生成。例如&#xff0c;在rand()函数中加入了随机数种子编码后&#xff0c;每次运行程序将会生成同样的随机整数序列。这个就是伪随机数…

Redis实战之-分布式锁-redission

一、分布式锁-redission功能介绍 基于setnx实现的分布式锁存在下面的问题&#xff1a; 重入问题&#xff1a;重入问题是指 获得锁的线程可以再次进入到相同的锁的代码块中&#xff0c;可重入锁的意义在于防止死锁&#xff0c;比如HashTable这样的代码中&#xff0c;他的方法都…