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.rhkb.cn/news/241459.html

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

相关文章

使用Python的pygame库实现下雪的效果

使用Python的pygame库实现下雪的效果 关于Python中pygame游戏模块的安装使用可见 https://blog.csdn.net/cnds123/article/details/119514520 先给出效果图&#xff1a; 源码如下&#xff1a; import pygame import random# 初始化pygame pygame.init()# 设置屏幕尺寸 width…

用Python优雅地写出数学表达式的LaTeX代码

用Python优雅地写出数学表达式的LaTeX代码 目录 Latexify LaTeX 安装方法 版本要求 使用方法 实例一 实例二 实例三 实例四 实例五 Latexify Latexify是一个Python库&#xff0c;它可以将数学表达式转换为LaTeX代码。通过使用latexify-py&#xff0c;可以将数学表达…

传统语音识别系统流程

文章目录 概述语音识别原理公式语音识别术语&#xff1a;分帧提取声学特征声学模型 概述 语音识别传统方法主要分两个阶段&#xff1a;训练和识别&#xff0c;训练阶段主要是生成声学模型和语言模型给识别阶段用。传统方法主要有五大模块组成&#xff0c;分别是特征提取&#…

java使用jsch处理软链接判断是否文件夹

前言 这一次主要是碰到一个问题。因为使用jsch去读取文件的时候&#xff0c;有一些文件它是使用软链接制作的一个映射。因为这里面有一个问题。如果它是软链接你就无法判断他到底是文件。还是文件夹&#xff1f;因为他没有提供可以直接读取的方法&#xff0c;用权限信息去判断…

vue2使用 element表格展开功能渲染子表格

默认样式 修改后 样式2 <el-table :data"needDataFollow" border style"width: 100%"><el-table-column align"center" label"序号" type"index" width"80" /><el-table-column align"cent…

64.Spring事件监听的核心机制是什么?

Spring事件监听的核心机制是什么? spring的事件监听有三个部分组成 事件(ApplicationEvent) 负责对应相应监听器 事件源发生某事件是特定事件监听器被触发的原因监听器(ApplicationListener) 对应于观察者模式中的观察者。监听器监听特定事件,并在内部定义了事件发生后的响应…

关于java中的Super详解

关于java中的Super详解 我们在上一篇文章中了解到了面向对象三大基本特征&#xff0c;继承&#xff0c;我们本篇文章中来了解一下Super&#x1f600;。 一、Super和this调用属性 this&#xff1a;当前类中使用。super&#xff1a;父类使用。 我们直接用代码来说明一下。 1…

如何使用JS逆向爬取网站数据

引言&#xff1a; JS逆向是指利用编程技术对网站上的JavaScript代码进行逆向分析&#xff0c;从而实现对网站数据的抓取和分析。这种技术在网络数据采集和分析中具有重要的应用价值&#xff0c;能够帮助程序员获取网站上的有用信息&#xff0c;并进行进一步的处理和分析。 基…

windows vscode jsoncpp cmake c++ 构建项目

jsoncpp的编译和使用推荐文章&#xff1a;jsoncpp的编译和使用 | 爱编程的大丙 (subingwen.cn)https://www.subingwen.cn/cpp/jsoncpp/从这个链接下载jsoncpp-master&#xff1a;https://github.com/open-source-parsers/jsoncpp 可以把这个文件夹名字改成jsoncpp&#xff0c;…

探索设计模式的魅力:“感受单例模式的力量与神秘” - 掌握编程的王牌技巧

在软件开发的赛场上&#xff0c;单例模式以其独特的魅力长期占据着重要的地位。作为设计模式中的一员&#xff0c;它在整个软件工程的棋盘上扮演着关键性角色。本文将带你深入探索单例模式的神秘面纱&#xff0c;从历史渊源到现代应用&#xff0c;从基础实现到高级技巧&#xf…

未来的NAS:连接您的数字生活

未来的NAS&#xff1a;连接您的数字生活 引言 网络附加存储&#xff08;Network Attached Storage&#xff0c;简称NAS&#xff09;是一种通过网络连接的存储设备&#xff0c;用于集中存储和共享数据。传统的NAS设备通常包含一个或多个硬盘驱动器&#xff0c;可以通过局域网连…

【编码魔法师系列_构建型4】原型模式(Prototype Pattern)

&#x1f449;直达编码魔法师系列其他文章&#x1f448; 学会设计模式&#xff0c;你就可以像拥有魔法一样&#xff0c;在开发过程中解决一些复杂的问题。设计模式是由经验丰富的开发者们&#xff08;GoF&#xff09;凝聚出来的最佳实践&#xff0c;可以提高代码的可读性、可维…

基于网络爬虫的微博热点分析,包括文本分析和主题分析

基于Python的网络爬虫的微博热点分析是一项技术上具有挑战性的任务。我们使用requests库来获取微博热点数据&#xff0c;并使用pandas对数据进行处理和分析。为了更好地理解微博热点话题&#xff0c;我们采用LDA主题分析方法&#xff0c;结合jieba分词工具将文本分割成有意义的…

蓝桥杯备赛 | 洛谷做题打卡day5

蓝桥杯备赛 | 洛谷做题打卡day5 图论起航&#xff0c;一起来看看深&#xff08;广&#xff09;度优先吧 ~ 文章目录 蓝桥杯备赛 | 洛谷做题打卡day5图论起航&#xff0c;一起来看看深&#xff08;广&#xff09;度优先吧 ~【深基18.例3】查找文献题目描述 输入格式输出格式样例…

设计模式⑦ :简单化

文章目录 一、前言二、Facade 模式1. 介绍2. 应用3. 总结 三、Mediator 模式1. 介绍2. 应用3. 总结 一、前言 有时候不想动脑子&#xff0c;就懒得看源码又不像浪费时间所以会看看书&#xff0c;但是又记不住&#xff0c;所以决定开始写"抄书"系列。本系列大部分内容…

.NetCore Flurl.Http 4.0.0 以上管理客户端

参考原文地址&#xff1a;Managing Clients - Flurl 管理客户端 Flurl.Http 构建在堆栈之上System.Net.Http。如果您熟悉HttpClient&#xff0c;那么您可能听说过这个建议&#xff1a;不要为每个请求创建一个新客户端&#xff1b;重复使用它们&#xff0c;否则将面临后…

google网站流量怎么获取?

流量是一个综合性的指标&#xff0c;可以说做网站就是为了相关流量&#xff0c;一个网站流量都没有&#xff0c;那其实就跟摆饰品没什么区别 而想从谷歌这个搜索引擎里获取流量&#xff0c;一般都分为两种方式&#xff0c;一种是网站seo&#xff0c;另一种自然就是投广告&#…

软件测试阶段简介_单元测试、集成测试、配置项测试、系统测试

文章目录 前言一、软件测试“V”模型二、单元测试三、集成测试四、配置项测试五、系统测试总结 前言 一般来说&#xff0c;按照软件的研制阶段划分&#xff0c;软件测试可分为单元测试、集成测试、配置项测试、系统测试等。本文将对上述各测试阶段进行逐一介绍。 一、软件测试…

Halcon 一维测量

文章目录 算子矩形算子弧形算子移动到新的参考点 Halcon 案例测量保险丝的宽度&#xff08;边缘对测量&#xff09;使用助手进行测量 halcon 案例获取芯片引脚的个数平均宽度距离&#xff0c;连续两个边缘的距离&#xff08;measure_pos &#xff09;halcon 定位测量Halcon 测量…

HBase学习六:LSM树算法

1、简介 HBase是基于LSM树架构实现的,天生适合写多读少的应用场景。 LSM树本质上和B+树一样,是一种磁盘数据的索引结构。但和B+树不同的是,LSM树的索引对写入请求更友好。因为无论是何种写入请求,LSM树都会将写入操作处理为一次顺序写,而HDFS擅长的正是顺序写(且HDFS不…