.net MQTT 发布订阅实例
1.下载安装 mosquitto-2.1.2-install-windows-x64.exe
https://mosquitto.org/files/binary/win64/mosquitto-2.1.2-install-windows-x64.exe
在安装目录下 如D:\Program Files\Mosquitto 找到mosquitto.conf 文件打开改成
listener 1883 0.0.0.0 allow_anonymous true log_dest file C:\mosquitto\log\mosquitto.log log_type all
保存后重启服务 Mosquitto Broker 即可.
2.安装MQTTnet包
<PackageReference Include="MQTTnet" Version="5.2.0.1603" />
3. 发布事件相关代码
using GL.SerialPort; using GL.UIResource.Infrastructure; using GL.UIResource.UiControls; using GL.UIResource.Utils; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using MQTTnet; using MQTTnet.Formatter; using MQTTnet.Protocol; using Newtonsoft.Json; using System.Diagnostics; using System.IO.Ports; using Task = System.Threading.Tasks.Task; namespace GL.USBWinform { public partial class MainFrm : Form { #region prop属性 public string sComName = ""; public IPort port; public PortSet? portSet; private string[] portNames; private List<int> baudRates; private List<int> dataBits; private Array parityCheck; private Array stopBit; private readonly ILogger<MainFrm> _logger; private IMqttClient? _client; private MqttClientOptions? mqttClientOptions; private string _broker = "127.0.0.1"; private readonly int _mqttPort = 1883; private readonly string _deviceId = "device-01"; // 主题设计:设备上报数据 private string DataTopic => $"{_deviceId}/data"; #endregion #region ctor构造函数 public MainFrm(IPort _port, ILogger<MainFrm> logger) { InitializeComponent(); port = _port; _logger = logger; var builder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json") .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT")}.json", true, true); var configuration = builder.Build(); _broker = configuration.GetSection("IPAddress").Value!; portNames = SerialPortHelper.GetPortNames(); baudRates = SerialPortHelper.GetStandardBaudRates(); dataBits = SerialPortHelper.GetDataBits(); parityCheck = Enum.GetValues(typeof(Parity)); stopBit = Enum.GetValues(typeof(StopBits)).Cast<StopBits>().Where(sb => sb != StopBits.None).ToArray(); } private void MainFrm_Load(object sender, EventArgs e) { initializeData(); string filePath = Path.Combine(System.Windows.Forms.Application.StartupPath, "SerialPortInfo.json"); if (File.Exists(filePath)) { portSet = JsonConvert.DeserializeObject<PortSet>(File.ReadAllText(filePath)); } if (portSet != null) { int index = Array.IndexOf(portNames, portSet.PortName); cmbComName.SelectedIndex = index; cmbBaudRate.SelectedIndex = Array.IndexOf(baudRates.ToArray(), portSet.BaudRate); cmbDataBits.SelectedIndex = Array.IndexOf(dataBits.ToArray(), portSet.DataBits); cmbParityCheck.SelectedIndex = Array.IndexOf(parityCheck.Cast<object>().ToArray(), portSet.ParityCheck); cmbStopBit.SelectedIndex = Array.IndexOf(stopBit.Cast<object>().ToArray(), portSet.StopBit); } } #endregion #region MQTT相关 /// <summary> /// 初始化数据 /// </summary> private void initializeData() { cmbComName.Items.Clear(); cmbBaudRate.Items.Clear(); cmbDataBits.Items.Clear(); cmbStopBit.Items.Clear(); cmbParityCheck.Items.Clear(); cmbComName.Items.AddRange(portNames); cmbBaudRate.Items.AddRange(baudRates.Cast<object>().ToArray()); cmbBaudRate.SelectedIndex = 10; cmbDataBits.Items.AddRange(dataBits.Cast<object>().ToArray()); cmbDataBits.SelectedIndex = 3; cmbParityCheck.Items.AddRange(parityCheck.Cast<object>().ToArray()); cmbParityCheck.SelectedIndex = 0; cmbStopBit.Items.AddRange(stopBit.Cast<object>().ToArray()); cmbStopBit.SelectedIndex = 0; //初始化Mqtt服务 var factory = new MqttClientFactory(); _client = factory.CreateMqttClient(); mqttClientOptions = new MqttClientOptionsBuilder() .WithTcpServer(_broker, _mqttPort) .WithProtocolVersion(MqttProtocolVersion.V311) // 👈 添加这一行 .WithClientId($"pub-{_deviceId}-{Guid.NewGuid():N}") .WithCleanSession() //.WithTimeout(TimeSpan.FromSeconds(10)) // 加个超时,避免长时间挂起 .Build(); } /// <summary> /// 开始连接MQTT /// </summary> /// <returns></returns> private Task StartConnMqtt() { try { if (_client != null) { _client.DisconnectedAsync += async e => { Debug.WriteLine($"[MQTT] 断开原因: {e.Reason}, 异常: {e.Exception?.Message}"); // 避免无限重连风暴 await Task.Delay(TimeSpan.FromSeconds(5)); try { var result = await _client.ConnectAsync(mqttClientOptions); var msg = $"[MQTT] 重连结果: {result.ResultCode}"; Debug.WriteLine(msg); AddLog(msg); } catch (Exception ex) { var msg = $"[MQTT] 重连失败: {ex.GetType().Name}: {ex.Message}"; var msg2 = $"[MQTT] 内部异常: {ex.InnerException?.Message}"; _logger.LogError(msg); _logger.LogError(msg2); } }; // 用 await 或显式 catch,把异常暴露出来 _ = ConnectWithLogAsync(); } } catch (Exception ex) { var msg = $"[MQTT] 初始化异常: {ex}"; _logger.LogError(msg); } return Task.CompletedTask; } /// <summary> /// 断开MQTT连接 /// </summary> /// <returns></returns> public async Task DisposeAsync() { if (_client == null) return; try { if (_client.IsConnected) { var options = new MqttClientDisconnectOptionsBuilder() .WithReason(MqttClientDisconnectOptionsReason.NormalDisconnection) .Build(); await _client.DisconnectAsync(options); } } catch (Exception ex) { _logger.LogError($"断开异常: {ex.Message}"); } finally { _client.Dispose(); _client = null; } } // 新增辅助方法 private async Task ConnectWithLogAsync() { try { if (_client != null && !_client.IsConnected) { var result = await _client.ConnectAsync(mqttClientOptions); var msg = $"[MQTT] 首次连接结果: {result.ResultCode}"; AddLog(msg); } } catch (Exception ex) { var msg = $"[MQTT] 首次连接失败: {ex.GetType().Name}: {ex.Message}"; var msg2 = $"[MQTT] InnerException: {ex.InnerException?.GetType().Name}: {ex.InnerException?.Message}"; var msg3 = $"[MQTT] 堆栈: {ex.StackTrace}"; _logger.LogError(msg); _logger.LogError(msg2); _logger.LogError(msg3); } } /// <summary> /// MQTT发布事件 /// </summary> /// <param name="jsonData">json数据</param> /// <returns></returns> private async Task PublishAsync(string jsonData) { var msg = new MqttApplicationMessageBuilder() .WithTopic(DataTopic) //设置消息发布的主题 .WithPayload(jsonData) //设置消息的负载(内容) .WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce) //设置 QoS 等级为 1(至少一次) .WithRetainFlag(false) // 保留消息,新订阅者能立刻知道状态 .Build(); if (_client != null) { await _client.PublishAsync(msg); AddLog(jsonData); } } #endregion /// <summary> /// 添加日志信息 /// </summary> /// <param name="msg"></param> private void AddLog(string msg) { CommonUtil.BeginInvoke(lbMsg, new Action(() => { lbMsg.AppendText(msg + Environment.NewLine); })); } private void btnSet_Click(object sender, EventArgs e) { sComName = cmbComName.Text; string sBaudRate = cmbBaudRate.Text; string sDataBits = cmbDataBits.Text; string sParityCheck = cmbParityCheck.Text; string sStopBit = cmbStopBit.Text; if (string.IsNullOrWhiteSpace(sComName)) { cmbComName.Focus(); GLMessageBox.Show("请选择串口名称", MessageType.Info); return; } if (string.IsNullOrWhiteSpace(sBaudRate)) { cmbBaudRate.Focus(); GLMessageBox.Show("请选择波特率", MessageType.Info); return; } if (string.IsNullOrWhiteSpace(sDataBits)) { cmbDataBits.Focus(); GLMessageBox.Show("请选择数据位", MessageType.Info); return; } if (string.IsNullOrWhiteSpace(sParityCheck)) { cmbParityCheck.Focus(); GLMessageBox.Show("请选择奇偶校验", MessageType.Info); return; } if (string.IsNullOrWhiteSpace(sStopBit)) { cmbStopBit.Focus(); GLMessageBox.Show("请选择停止位", MessageType.Info); return; } var value = (StopBits)Enum.Parse(typeof(StopBits), sStopBit); int iStopBit = (int)value; var checkValue = (Parity)Enum.Parse(typeof(Parity), sParityCheck); int iParityCheck = (int)checkValue; if (portSet == null) portSet = new PortSet(); portSet.PortName = sComName; portSet.BaudRate = Convert.ToInt32(sBaudRate); portSet.DataBits = Convert.ToInt32(sDataBits); portSet.ParityCheck = checkValue; portSet.StopBit = value; port.setPort(portSet); string filePath = Path.Combine(Application.StartupPath, "SerialPortInfo.json"); string json = JsonConvert.SerializeObject(portSet); File.WriteAllText(filePath, json); AddLog("串口配置成功"); } private void btnStart_Click(object sender, EventArgs e) { var dic = SerialPortHelper.GetAllCp210xDevices(); if (dic.Count > 0) { if (portSet != null) { port.setPort(portSet); } port.EngineReceive -= Port_EngineReceive; port.ClosePort(); var IsOpen = port.OpenPort(); var logMsg = IsOpen ? "串口连接成功" : "串口连接失败"; AddLog(logMsg); StartConnMqtt(); port.EngineReceive += Port_EngineReceive; } else { GLMessageBox.Show("未发现可用USB设备", MessageType.Info); } } private void Port_EngineReceive(byte[] bytes, string str, string command) { try { var result = Ack8Decoder.Decode(bytes); // Debug.WriteLine("解码后的原始数据: " + result.ActualValue); var sResult = new PortResult { DeviceNum = result.DeviceNum, ActualValue = result.ActualValue }; var portResult = new PortResult<PortResult>() { Code = 200, Data = sResult, Msg = "解码成功" }; var jsonData = JsonConvert.SerializeObject(portResult); _ = Task.Run(async () => await PublishAsync(jsonData)); } catch (Exception ex) { var errorMsg = new PortResult<object>() { Code = -1, Data = null, Msg = $"解码失败:{ex.Message}" }; var jsonData = JsonConvert.SerializeObject(errorMsg); _logger.LogError(ex, "处理引擎数据时发生错误"); } } private void btnClearLog_Click(object sender, EventArgs e) { this.lbMsg.Clear(); } private void MainFrm_FormClosed(object sender, FormClosedEventArgs e) { port.ClosePort(); _ = DisposeAsync(); } private void cmbComName_MouseEnter(object sender, EventArgs e) { portNames = SerialPortHelper.GetPortNames(); if(portNames.Length>0) { cmbComName.Items.Clear(); cmbComName.Items.AddRange(portNames); } } } }
4.订阅事件项代码
private IMqttClient _client; private string _broker = "localhost";//localhost private readonly int _mqttPort = 1883; private MqttClientOptions mqttClientOptions; private readonly ILogger<FrmTest> _logger; public FrmTest(IPort _port, ILogger<FrmTest> logger) { InitializeComponent(); _logger=logger; port = _port; portSet = new PortSet(); var builder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json") .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT")}.json", true, true); var configuration = builder.Build(); _broker = configuration.GetSection("IPAddress").Value!; //初始化MqttClient工厂 var factory = new MqttClientFactory(); _client = factory.CreateMqttClient(); mqttClientOptions = new MqttClientOptionsBuilder() .WithTcpServer(_broker, _mqttPort) .WithClientId($"sub-{Guid.NewGuid():N}") .WithCleanSession() .WithProtocolVersion(MqttProtocolVersion.V311) // 👈 添加这一行 .Build(); _client.DisconnectedAsync += async e => { _logger.LogInformation($"[MQTT] 断开原因: {e.Reason}, 异常: {e.Exception?.Message}"); // 避免无限重连风暴 await Task.Delay(TimeSpan.FromSeconds(5)); try { var result = await _client.ConnectAsync(mqttClientOptions); await SubscribeAsync(); // 重连后重新订阅 _logger.LogInformation($"[MQTT] 重连结果: {result.ResultCode}"); } catch (Exception ex) { _logger.LogError($"[MQTT] 重连失败: {ex.GetType().Name}: {ex.Message}"); _logger.LogError($"[MQTT] 内部异常: {ex.InnerException?.Message}"); } }; // 用 await 或显式 catch,把异常暴露出来 _ = ConnectWithLogAsync(); // 收到消息的回调 _client.ApplicationMessageReceivedAsync += e => { string topic = e.ApplicationMessage.Topic; string payload = e.ApplicationMessage.ConvertPayloadToString(); Debug.WriteLine($"[收到] {topic} -> {payload}"); var result = JsonConvert.DeserializeObject<PortResult<PortResult>>(payload); if (result != null) { if (result.Code == 200) { if (result.Data != null) { if (lbMsg.InvokeRequired) { lbMsg.Invoke(new Action(() => { lbMsg.Text = $"当前设备编号{result.Data.DeviceNum}实测物理值:{result.Data.ActualValue}"; })); } else { lbMsg.Text = $"当前设备编号{result.Data.DeviceNum}实测物理值:{result.Data.ActualValue}"; } } } } if (lbMsg.InvokeRequired) { lbMsg.Invoke(new Action(() => { lbMsg.Text += payload; })); } else { lbMsg.Text += payload; } return Task.CompletedTask; }; } // 新增辅助方法 private async Task ConnectWithLogAsync() { try { if (_client != null) { var result = await _client.ConnectAsync(mqttClientOptions); await SubscribeAsync(); _logger.LogInformation($"[MQTT] 首次连接结果: {result.ResultCode}"); } } catch (Exception ex) { _logger.LogError($"[MQTT] 首次连接失败: {ex.GetType().Name}: {ex.Message}"); _logger.LogError($"[MQTT] InnerException: {ex.InnerException?.GetType().Name}: {ex.InnerException?.Message}"); _logger.LogError($"[MQTT] 堆栈: {ex.StackTrace}"); } } // MqttClient订阅事件 private async Task SubscribeAsync() { // 订阅所有设备的数据(通配符 +) await _client.SubscribeAsync(new MqttTopicFilterBuilder() .WithTopic("+/data") .WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce) .Build()); // 订阅所有设备的状态 await _client.SubscribeAsync(new MqttTopicFilterBuilder() .WithTopic("devices/+/status") .WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce) .Build()); }
浙公网安备 33010602011771号