.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);
            }
        }
    }
}
View Code

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());
        }
View Code

 

posted on 2026-09-18 17:13  QFAAAAA  阅读(22)  评论(0)    收藏  举报

导航