上一篇我们初步设计了MQTT服务端
这里我们设计客户端MQTT Client,接上一篇
新建Windows窗体FormMqttClient
窗体FormMqttClient设计如图:
窗体FormMqttClient设计器相关代码如下
文件FormMqttClient.Designer.cs
namespace CultureDemo
{
partial class FormMqttClient
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.rtxtMessage = new System.Windows.Forms.RichTextBox();
this.label8 = new System.Windows.Forms.Label();
this.btnPublish = new System.Windows.Forms.Button();
this.btnSubscribe = new System.Windows.Forms.Button();
this.txtContent = new System.Windows.Forms.TextBox();
this.label5 = new System.Windows.Forms.Label();
this.txtTopicPublish = new System.Windows.Forms.TextBox();
this.label6 = new System.Windows.Forms.Label();
this.txtTopic = new System.Windows.Forms.TextBox();
this.label7 = new System.Windows.Forms.Label();
this.btnDisconnect = new System.Windows.Forms.Button();
this.btnConnect = new System.Windows.Forms.Button();
this.txtClientId = new System.Windows.Forms.TextBox();
this.label4 = new System.Windows.Forms.Label();
this.txtUpwd = new System.Windows.Forms.TextBox();
this.label3 = new System.Windows.Forms.Label();
this.txtUserName = new System.Windows.Forms.TextBox();
this.label2 = new System.Windows.Forms.Label();
this.txtPort = new System.Windows.Forms.TextBox();
this.label1 = new System.Windows.Forms.Label();
this.txtIP = new System.Windows.Forms.TextBox();
this.lb = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// rtxtMessage
//
this.rtxtMessage.Location = new System.Drawing.Point(239, 37);
this.rtxtMessage.Name = "rtxtMessage";
this.rtxtMessage.Size = new System.Drawing.Size(623, 411);
this.rtxtMessage.TabIndex = 51;
this.rtxtMessage.Text = "";
//
// label8
//
this.label8.AutoSize = true;
this.label8.Location = new System.Drawing.Point(242, 18);
this.label8.Name = "label8";
this.label8.Size = new System.Drawing.Size(29, 12);
this.label8.TabIndex = 50;
this.label8.Text = "消息";
//
// btnPublish
//
this.btnPublish.Location = new System.Drawing.Point(117, 425);
this.btnPublish.Name = "btnPublish";
this.btnPublish.Size = new System.Drawing.Size(75, 23);
this.btnPublish.TabIndex = 49;
this.btnPublish.Text = "发布";
this.btnPublish.UseVisualStyleBackColor = true;
this.btnPublish.Click += new System.EventHandler(this.btnPublish_Click);
//
// btnSubscribe
//
this.btnSubscribe.Location = new System.Drawing.Point(117, 286);
this.btnSubscribe.Name = "btnSubscribe";
this.btnSubscribe.Size = new System.Drawing.Size(75, 23);
this.btnSubscribe.TabIndex = 48;
this.btnSubscribe.Text = "订阅";
this.btnSubscribe.UseVisualStyleBackColor = true;
this.btnSubscribe.Click += new System.EventHandler(this.btnSubscribe_Click);
//
// txtContent
//
this.txtContent.Location = new System.Drawing.Point(92, 377);
this.txtContent.Name = "txtContent";
this.txtContent.Size = new System.Drawing.Size(100, 21);
this.txtContent.TabIndex = 47;
//
// label5
//
this.label5.AutoSize = true;
this.label5.Location = new System.Drawing.Point(45, 380);
this.label5.Name = "label5";
this.label5.Size = new System.Drawing.Size(29, 12);
this.label5.TabIndex = 46;
this.label5.Text = "内容";
//
// txtTopicPublish
//
this.txtTopicPublish.Location = new System.Drawing.Point(92, 341);
this.txtTopicPublish.Name = "txtTopicPublish";
this.txtTopicPublish.Size = new System.Drawing.Size(100, 21);
this.txtTopicPublish.TabIndex = 45;
//
// label6
//
this.label6.AutoSize = true;
this.label6.Location = new System.Drawing.Point(45, 344);
this.label6.Name = "label6";
this.label6.Size = new System.Drawing.Size(29, 12);
this.label6.TabIndex = 44;
this.label6.Text = "主题";
//
// txtTopic
//
this.txtTopic.Location = new System.Drawing.Point(92, 248);
this.txtTopic.Name = "txtTopic";
this.txtTopic.Size = new System.Drawing.Size(100, 21);
this.txtTopic.TabIndex = 43;
//
// label7
//
this.label7.AutoSize = true;
this.label7.Location = new System.Drawing.Point(45, 251);
this.label7.Name = "label7";
this.label7.Size = new System.Drawing.Size(29, 12);
this.label7.TabIndex = 42;
this.label7.Text = "主题";
//
// btnDisconnect
//
this.btnDisconnect.Location = new System.Drawing.Point(130, 205);
this.btnDisconnect.Name = "btnDisconnect";
this.btnDisconnect.Size = new System.Drawing.Size(75, 23);
this.btnDisconnect.TabIndex = 41;
this.btnDisconnect.Text = "断开";
this.btnDisconnect.UseVisualStyleBackColor = true;
this.btnDisconnect.Click += new System.EventHandler(this.btnDisconnect_Click);
//
// btnConnect
//
this.btnConnect.Location = new System.Drawing.Point(36, 205);
this.btnConnect.Name = "btnConnect";
this.btnConnect.Size = new System.Drawing.Size(75, 23);
this.btnConnect.TabIndex = 40;
this.btnConnect.Text = "连接";
this.btnConnect.UseVisualStyleBackColor = true;
this.btnConnect.Click += new System.EventHandler(this.btnConnect_Click);
//
// txtClientId
//
this.txtClientId.Location = new System.Drawing.Point(67, 164);
this.txtClientId.Name = "txtClientId";
this.txtClientId.Size = new System.Drawing.Size(134, 21);
this.txtClientId.TabIndex = 39;
this.txtClientId.Text = "snake";
//
// label4
//
this.label4.AutoSize = true;
this.label4.Location = new System.Drawing.Point(20, 167);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(41, 12);
this.label4.TabIndex = 38;
this.label4.Text = "客户ID";
//
// txtUpwd
//
this.txtUpwd.Location = new System.Drawing.Point(67, 127);
this.txtUpwd.Name = "txtUpwd";
this.txtUpwd.Size = new System.Drawing.Size(134, 21);
this.txtUpwd.TabIndex = 37;
this.txtUpwd.Text = "1";
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(32, 130);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(29, 12);
this.label3.TabIndex = 36;
this.label3.Text = "密码";
//
// txtUserName
//
this.txtUserName.Location = new System.Drawing.Point(67, 100);
this.txtUserName.Name = "txtUserName";
this.txtUserName.Size = new System.Drawing.Size(134, 21);
this.txtUserName.TabIndex = 35;
this.txtUserName.Text = "a";
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(32, 103);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(29, 12);
this.label2.TabIndex = 34;
this.label2.Text = "账号";
//
// txtPort
//
this.txtPort.Location = new System.Drawing.Point(67, 64);
this.txtPort.Name = "txtPort";
this.txtPort.Size = new System.Drawing.Size(134, 21);
this.txtPort.TabIndex = 33;
this.txtPort.Text = "12345";
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(32, 67);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(29, 12);
this.label1.TabIndex = 32;
this.label1.Text = "端口";
//
// txtIP
//
this.txtIP.Location = new System.Drawing.Point(67, 22);
this.txtIP.Name = "txtIP";
this.txtIP.Size = new System.Drawing.Size(134, 21);
this.txtIP.TabIndex = 31;
this.txtIP.Text = "127.0.0.1";
//
// lb
//
this.lb.AutoSize = true;
this.lb.Location = new System.Drawing.Point(20, 25);
this.lb.Name = "lb";
this.lb.Size = new System.Drawing.Size(41, 12);
this.lb.TabIndex = 30;
this.lb.Text = "IP地址";
//
// FormMqttClient
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(874, 525);
this.Controls.Add(this.rtxtMessage);
this.Controls.Add(this.label8);
this.Controls.Add(this.btnPublish);
this.Controls.Add(this.btnSubscribe);
this.Controls.Add(this.txtContent);
this.Controls.Add(this.label5);
this.Controls.Add(this.txtTopicPublish);
this.Controls.Add(this.label6);
this.Controls.Add(this.txtTopic);
this.Controls.Add(this.label7);
this.Controls.Add(this.btnDisconnect);
this.Controls.Add(this.btnConnect);
this.Controls.Add(this.txtClientId);
this.Controls.Add(this.label4);
this.Controls.Add(this.txtUpwd);
this.Controls.Add(this.label3);
this.Controls.Add(this.txtUserName);
this.Controls.Add(this.label2);
this.Controls.Add(this.txtPort);
this.Controls.Add(this.label1);
this.Controls.Add(this.txtIP);
this.Controls.Add(this.lb);
this.Name = "FormMqttClient";
this.Text = "FormMqttClient";
this.Load += new System.EventHandler(this.FormMqttClient_Load);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.RichTextBox rtxtMessage;
private System.Windows.Forms.Label label8;
private System.Windows.Forms.Button btnPublish;
private System.Windows.Forms.Button btnSubscribe;
private System.Windows.Forms.TextBox txtContent;
private System.Windows.Forms.Label label5;
private System.Windows.Forms.TextBox txtTopicPublish;
private System.Windows.Forms.Label label6;
private System.Windows.Forms.TextBox txtTopic;
private System.Windows.Forms.Label label7;
private System.Windows.Forms.Button btnDisconnect;
private System.Windows.Forms.Button btnConnect;
private System.Windows.Forms.TextBox txtClientId;
private System.Windows.Forms.Label label4;
private System.Windows.Forms.TextBox txtUpwd;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.TextBox txtUserName;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.TextBox txtPort;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.TextBox txtIP;
private System.Windows.Forms.Label lb;
}
}
窗体FormMqttClient程序代码如下:客户端程序
文件FormMqttClient.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using MQTTnet;
using MQTTnet.Client;
namespace CultureDemo
{
public partial class FormMqttClient : Form
{
IMqttClient mqttClient;
public FormMqttClient()
{
InitializeComponent();
rtxtMessage.ReadOnly = true;
}
/// <summary>
/// 打印相关消息
/// </summary>
/// <param name="contents"></param>
private void DisplayMessage(string contents)
{
if (!this.IsHandleCreated)
{
return;
}
this.BeginInvoke(new Action(() =>
{
if (rtxtMessage.TextLength >= 40960)
{
rtxtMessage.Clear();
}
rtxtMessage.AppendText($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}->{contents}\n");
rtxtMessage.ScrollToCaret();
}));
}
private void FormMqttClient_Load(object sender, EventArgs e)
{
mqttClient = new MqttFactory().CreateMqttClient();
mqttClient.ConnectedAsync += MqttClient_ConnectedAsync;
mqttClient.DisconnectedAsync += MqttClient_DisconnectedAsync;
mqttClient.ApplicationMessageReceivedAsync += MqttClient_ApplicationMessageReceivedAsync;
}
private Task MqttClient_ApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs e)
{
return Task.Run(new Action(() =>
{
string msg = e.ApplicationMessage.ConvertPayloadToString();
DisplayMessage($"接收消息:{msg}\nQoS={e.ApplicationMessage.QualityOfServiceLevel}\n客户端={e.ClientId}\n主题:{e.ApplicationMessage.Topic}");
}));
}
private Task MqttClient_DisconnectedAsync(MqttClientDisconnectedEventArgs e)
{
return Task.Run(new Action(async () =>
{
DisplayMessage($"已断开到MQTT服务端的连接.尝试重新连接");
try
{
await Task.Delay(3000);
//MqttClientOptions options = new MqttClientOptions();
//await mqttClient.ConnectAsync(options);
await mqttClient.ReconnectAsync();
}
catch (Exception ex)
{
DisplayMessage($"重新连接服务器失败:{ex.Message}");
}
}));
}
private Task MqttClient_ConnectedAsync(MqttClientConnectedEventArgs e)
{
return Task.Run(new Action(() =>
{
DisplayMessage($"连接到MQTT服务端成功.{e.ConnectResult.AssignedClientIdentifier}");
}));
}
private async void btnConnect_Click(object sender, EventArgs e)
{
try
{
MqttClientOptionsBuilder mqttClientOptionBuild = new MqttClientOptionsBuilder()
.WithClientId(this.txtClientId.Text)
.WithTcpServer(this.txtIP.Text, int.Parse(this.txtPort.Text))
.WithCredentials(this.txtUserName.Text, this.txtUpwd.Text);
MqttClientOptions options = mqttClientOptionBuild.Build();
//开启
await mqttClient.ConnectAsync(options);
}
catch (Exception ex)
{
DisplayMessage($"连接服务端时出现异常:{ex.Message}");
}
}
private async void btnDisconnect_Click(object sender, EventArgs e)
{
if (mqttClient != null)
{
if (mqttClient.IsConnected)
{
await mqttClient.DisconnectAsync();
}
mqttClient.Dispose();
}
}
private async void btnSubscribe_Click(object sender, EventArgs e)
{
if (string.IsNullOrWhiteSpace(this.txtTopic.Text))
{
DisplayMessage(">>> 请输入主题");
return;
}
//在 MQTT 中有三种 QoS 级别: QuanlityOfService
//At most once(0) 最多一次
//At least once(1) 至少一次
//Exactly once(2) 恰好一次
MqttClientSubscribeOptionsBuilder subscribeOptionsBuilder = new MqttClientSubscribeOptionsBuilder();
MqttClientSubscribeOptions subscribeOption = subscribeOptionsBuilder.WithTopicFilter(this.txtTopic.Text).Build();
await mqttClient.SubscribeAsync(subscribeOption);
DisplayMessage($">>> 成功订阅主题【{txtTopic.Text}】");
}
private async void btnPublish_Click(object sender, EventArgs e)
{
if (string.IsNullOrWhiteSpace(this.txtTopicPublish.Text))
{
DisplayMessage(">>> 请输入主题");
return;
}
MqttClientPublishResult result = await mqttClient.PublishStringAsync(
this.txtTopicPublish.Text,
this.txtContent.Text,
MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce);//恰好一次, QoS 级别1
DisplayMessage($"发布消息--主题:{this.txtTopicPublish.Text},消息:{this.txtContent.Text},结果: {result.ReasonCode}");
}
}
}
为了一个应用程序可以作为客户端和服务端,我们在Program.cs增加切换开关
选择作为MQTT服务端和MQTT客户端
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace CultureDemo
{
static class Program
{
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
DialogResult dialog = MessageBox.Show("请选择程序作为MQTT服务端还是客户端,选择\"是\"作为服务端,选择\"否\"作为客户端", "是否MQTT服务端", MessageBoxButtons.YesNo);
if (dialog == DialogResult.Yes)
{
Application.Run(new FormMqttServer());
}
else
{
Application.Run(new FormMqttClient());
}
}
}
}