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.mzph.cn/news/634289.shtml

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

相关文章

Parade Series - RTSP - Web

Cron Startup Scripts └─ lt-init.cmd├─ lt-server.cmd│ └─ lt-rtsp-proxy-server.cmd│ └─ lt-rtsp-proxy-agent.cmd└─ lt-push.cmd└─ lt-rtsp-cam-daemon.cmd└─ lt-rtsp-cam-worker.cmdlt-init.cmd :: :: PLEASE DO NOT EDIT THIS FILE :: ECHO OFF…

Qt —— 编译Qt5版本QFTP库,并实现连接服务、获取列表、上传、下载、删除文件等操作(附源码、附基于Qt5编译好的QFTP库)

示例效果1 示例效果2 介绍 QFTP是Qt4的库,Qt5改用了QNetworkAccessManager来代替。但是Qt5提供的QNetworkAccessManager仅支持FTP的上传和下载,所以只能将QFTP库编译为Qt5的库来进行调用。 QFTP在Github的下载地址:https://github.com/qt/qtftp 客户端源码生成的release结果…

IDEA项目启动报错之Command too long

使用IDEA最新的版本2023-3月份社区版本&#xff0c;启动之前没问题的项目突然报错如下&#xff1a; Error running VipServiceApplication: Error running // VipServiceApplication.Command line is too long. Shorten the command line via // JAR manifest or via a // clas…

运放【之噪声】

电流噪声和电压噪声 我们一般评估噪声&#xff0c;还看对输出端噪声电压的贡献&#xff0c;因为电流乘以电阻等于电压&#xff0c;因此&#xff0c;最终的噪声大小还跟电路中电阻的取值有很大关系。显然&#xff0c;电阻越大&#xff0c;那么噪声电压就越大。反之电阻越小&…

设计模式——建造者模式(Builder Pattern)

概述 建造者模式是较为复杂的创建型模式&#xff0c;它将客户端与包含多个组成部分&#xff08;或部件&#xff09;的复杂对象的创建过程分离&#xff0c;客户端无须知道复杂对象的内部组成部分与装配方式&#xff0c;只需要知道所需建造者的类型即可。它关注如何一步一步创建一…

09 STM32 - PWM

9.1 PWM简介 脉冲宽度调制(Pulse Width Modulation,简称PWM)&#xff0c;是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。简单一点&#xff0c;就是对脉冲宽度的控制。 9.2 PWM波原理 如下图所示&#xff0c;使用定时器定时&#xff0c;从0开始&#x…

Ubuntu系统Git的安装配置及使用笔记(更新中)

Ubuntu下Git的下载及配置 (1)、下载git 打开终端命令窗口,输入&#xff1a;sudo apt-get install git 提示&#xff1a;sudo命令是用来以其他身份来执行命令&#xff0c;预设的身份为root,使用sudo时必须先输入密码 (2)、可以使用命令git --version查看git的版本号 (3)、设置…

项目中常用的ahoos

项目中常用的ahoos如下 useMount、useUnmountuseRequestuseBooleanuseInfiniteScrolluseLocalStorageStateuseInViewportuseUpdateEffectuseLayoutEffectuseMapuseThrottleFnuseDebounceFn

Java内置锁:深度解析StampedLock并发类

内容摘要 StampedLock类是一种高性能的读写锁&#xff0c;它通过引入乐观读和写锁的优化机制&#xff0c;提高了多线程环境下的并发性能&#xff0c;他支持三种访问模式&#xff1a;悲观读、写和乐观读&#xff0c;可以根据不同的业务场景选择适合的锁策略&#xff0c;相比传统…

Qt/QML编程之路:OpenGL的示例(39)

Qt编程之后,会发现有版本问题,有时候一个示例不同的版本下可能会跑不同,有些Qt5跑不同Qt6已经完善,可以跑通。 我就看到有个关于OpenGL的示例: 这个示例是演示怎么基于OpenGL编程的,但是调试时却发现glViewXXX等gl打头的函数说找不到reference,或者什么link不上之类的错…

FindMy技术与游戏手柄结合

游戏手柄作为游戏的重要配件&#xff0c;它极大地提升了玩家的游戏体验&#xff0c;推动了游戏市场的不断扩大。游戏手柄的触摸感应技术为游戏开发者提供了更多的创意空间&#xff0c;也为硬件制造商带来了新的商机。游戏手柄的个性化定制也为玩家社区的发展提供了动力&#xf…

红队打靶练习:BOB: 1.0.1

目录 信息收集 1、netdiscover 2、nmap 3、nikto 4、whatweb 目录探测 1、dirb 2、gobuster 3、dirsearch WEB 主页&#xff1a; robots.txt 其他页面 反弹shell 提权 系统信息收集 jc账户 本地提权 信息收集 1、netdiscover ┌──(root㉿ru)-[~/kali] └…

命令执行拼接符实例图解

命令执行常用到的5个拼接符&#xff0c;分别是逻辑与&#xff08;&&&#xff09;、逻辑或&#xff08;||&#xff09;、按位与&#xff08;&&#xff09;、按位或&#xff08;|&#xff09;和linux系统特有的分号&#xff08;;&#xff09; 1、&&&#xf…

运维平台介绍:视频智能运维平台的视频质量诊断分析和告警中心

目 录 一、视频智能运维平台介绍 &#xff08;一&#xff09;平台概述 &#xff08;二&#xff09;结构图 &#xff08;三&#xff09;功能介绍 1、运维监控 2、视频诊断 3、巡检管理 4、告警管理 5、资产管理 6、工单管理 7、运维…

【C++干货铺】C++11常用新特性 | 列表初始化 | STL中的变化

个人主页点击直达&#xff1a;小白不是程序媛 C系列专栏&#xff1a;C干货铺 代码仓库&#xff1a;Gitee 目录 C11简介 列表初始化 std::initializer_list std::initializer_list使用场景 decltype关键字 STL中的一些变化 新容器 array forward_list 容器中的一些新…

chrony介绍和安装

chrony介绍和安装 1.chrony&#xff08;时间同步服务&#xff09; 1.1 chrony介绍 Chrony 是一个用于时间同步的软件&#xff0c;它旨在提供高精度的系统时钟同步。Chrony 软件包括一个 NTP&#xff08;Network Time Protocol&#xff0c;网络时间协议&#xff09;服务器和客…

Python基础第二篇(Python基础语法)

文章目录 一、字面量二、注释三、变量四、数据类型五、数据类型转换六、标识符七、运算符八、字符串扩展内容&#xff08;1&#xff09;字符串定义&#xff08;2&#xff09;字符串拼接、&#xff08;3&#xff09;字符串格式化&#xff08;4&#xff09;字符串格式化的精度控制…

【算法与数据结构】Java实现查找与排序

文章目录 第一部分&#xff1a;查找算法二分查找插值查找分块查找哈希查找树表查找 第二部分&#xff1a;排序算法冒泡排序选择排序插入排序快速排序 总结 第一部分&#xff1a;查找算法 二分查找 也叫做折半查找&#xff0c;属于有序查找算法。 前提条件&#xff1a;数组数据…

TestNG注释

目录 TestNG注释列表 BeforeXXX和AfterXXX注释放在超类上时如何工作&#xff1f; 使用BeforeXXX和AfterXXX TestNG注释 TestNG是一个测试框架&#xff0c;旨在简化广泛的测试需求&#xff0c;从单元测试&#xff08;隔离测试一个类&#xff09;到集成测试&#xff08;测试由…

某马头条——day05

文章定时发布 实现方案对比 实现方案 延迟队列服务实现 按照文档进行项目的导入并准备数据库表导入对应实体类和nacos配置中心 乐观锁集成 redis集成和测试 成功集成通过测试 添加任务 ①&#xff1a;拷贝mybatis-plus生成的文件&#xff0c;mapper ②&#xff1a;创建task类…