参考:https://blog.youkuaiyun.com/yangyong1250/article/details/129403616?spm=1001.2014.3001.5502
IDE环境:vs2022
mqtt服务端:emqx5.0
应用场景:我需要从mqtt建立订阅,对一个topic不停的消费取出下面物联网设备发到mqtt的byte[]消息,然后转存,或者根据通信协议解析消息内容。物联网终端设备可能很多,而免费版的emqx他是不能存储消息的,也不支持对接其他数据库持久化,那么我就要考虑快速的消费所有物联网设备传输过来的消息,可能一个单向订阅不能满足需求。经过查资料,5.0提供了共享订阅机制,可以让多个订阅使用共享订阅的方式从同一个topic主题中取出消息,这种机制目前来看可以满足我的需求。
重点在于订阅的主题前缀是 $share/tahmshare/, 这个前缀后面 跟目标topic就组成了共享订阅。
1、新建一个mqtttest名称的windows桌面程序工程,并从nuget安装mqttnet

2、新建了一个MqttNetHelper类,代码如下, 因为涉及我自己配置的隐私,有些变量清空了,请自行根据注释配置
using MQTTnet;
using MQTTnet.Client;
using MQTTnet.Protocol;
using System.Text;
namespace mqtttest
{
public class MqttNetHelper
{
public IMqttClient mqttClient;
public bool mqttIsConnected=false;
public string serverip = "127.0.0.1"; //填写你的服务端IP
public int serverport = 1883;
public int KeepAliveSecond = 60; //心跳间隔
public string useridstr = ""; //自己在emqx网页管理端新建的账户
public string passstr = ""; //密码
//消费端我是采用共享订阅$share/tahmshare/为共享订阅前缀,JQTAHMCSM才是真正的topic
public string topicStr = @"$share/tahmshare/JQTAHMCSM";
//用来存储消息
public Dictionary<string,string> topicDic=new Dictionary<string, string>();
public void MqttConnectAsync()
{
try
{
mqttIsConnected = false;
var mqttFactory = new MqttFactory();
//使用Build构建
var mqttClientOptions = new MqttClientOptionsBuilder()
.WithTcpServer(serverip, serverport)
.WithProtocolVersion(MQTTnet.Formatter.MqttProtocolVersion.V500)
.WithClientId(Guid.NewGuid().ToString())
.WithCleanSession(false)
.WithKeepAlivePeriod(TimeSpan.FromSeconds(KeepAliveSecond))
.WithCredentials(useridstr, passstr)
.WithTimeout(TimeSpan.FromSeconds(KeepAliveSecond))
//.WithWillTopic(topicStr)
.Build();
mqttClient = mqttFactory.CreateMqttClient();
//与3.1对比,事件订阅名称和接口已经变化
mqttClient.DisconnectedAsync += MqttClient_DisconnectedAsync;
mqttClient.ConnectedAsync += MqttClient_ConnectedAsync;
mqttClient.ApplicationMessageReceivedAsync += MqttClient_ApplicationMessageReceivedAsync;
Task task = mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
task.Wait();
mqttIsConnected = mqttClient.IsConnected;
}
catch (Exception ex)
{
Console.WriteLine($"Mqtt客户端尝试连接出错:{ex.Message}");
}
}
public Task MqttClient_ApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs arg)
{
string text = ToHexString(arg.ApplicationMessage.Payload);
string Topic = arg.ApplicationMessage.Topic;
string QoS = arg.ApplicationMessage.QualityOfServiceLevel.ToString();
string Retained = arg.ApplicationMessage.Retain.ToString();
topicDic.Add(Topic, text);
return Task.CompletedTask;
}
public Task MqttClient_ConnectedAsync(MqttClientConnectedEventArgs arg)
{
Console.WriteLine($"Mqtt客户端连接成功."); // 这里是随便写的,可以写到自己的日志机制
return Task.CompletedTask;
}
public Task MqttClient_DisconnectedAsync(MqttClientDisconnectedEventArgs arg)
{
Console.WriteLine($"Mqtt客户端连接断开");// 这里是随便写的,可以写到自己的日志机制
return Task.CompletedTask;
}
public static string ToHexString(byte[] bytes) // 0xae00cf => "AE00CF "
{
string hexString = string.Empty;
if (bytes != null)
{
StringBuilder strB = new StringBuilder();
for (int i = 0; i < bytes.Length; i++)
{
strB.Append(bytes[i].ToString("X2"));
}
hexString = strB.ToString();
}
return hexString;
}
}
}
3、form1中新建一个按钮,用来启动连接和产生mqtt订阅 ,新建一个richtextbox控件用来展示收到的消息。 新建一个定时器,用来定时从帮助类的存储消息的变量中topicDic 取出消息,追加到 tb_mqttMsg
MqttNetHelper mymqtt = new MqttNetHelper();
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
mymqtt.MqttConnectAsync();
//mymqtt.ApplicationMessageReceivedAsync += MqttClient_ApplicationMessageReceivedAsync;
string payload = "hello,from pascalming!";
string mqttTopic = "/topic/pascalming/v1";
var applicationMessage = new MqttApplicationMessageBuilder()
.WithTopic(mqttTopic)
.WithPayload(payload)
.Build();
Task<MqttClientSubscribeResult> task = mymqtt.mqttClient.SubscribeAsync(mymqtt.topicStr);
task.Wait();
}
private void timer1_Tick(object sender, EventArgs e)
{
if (mymqtt.mqttIsConnected)
{
foreach (var dic in mymqtt.topicDic)
{
tb_mqttMsg.AppendText(dic.Key + "-" + dic.Value + "\r\n");
mymqtt.topicDic.Remove(dic.Key);
}
}
}
4、效果如下
