You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于MQTT实现的类属性无法返回正确值问题求助

问题分析

你的问题核心是异步回调的时机不匹配:

  • getTopicValue方法仅完成了MQTT客户端的连接与订阅操作,执行完就返回了;
  • 而topicValue的赋值是在后续收到MQTT消息时,由ApplicationMessageReceivedAsync回调完成的。
    所以你在按钮事件里await getTopicValue()之后立刻读取topicValue,此时回调还没触发,自然拿到空字符串。
解决方案

根据你的需求(单次获取消息/持续接收消息),提供两种可行方案:

方案1:等待第一条消息到达后返回(适合单次获取场景)

修改ClassMQTT类,用TaskCompletionSource来等待消息回调触发,确保await getTopicValue()完成时已经拿到消息值:

using MQTTnet;
using MQTTnet.Client;
using System;
using System.Threading.Tasks;

namespace ClassMQTTTest
{
    public class ClassMQTT : IDisposable
    {
        private readonly string _broker;
        private readonly int _brokerPort;
        private readonly string _topic;
        private readonly string _clientId = Guid.NewGuid().ToString();
        private readonly MqttFactory _factory;
        private readonly IMqttClient _mqttClient;
        private readonly MqttClientOptions _connectionOptions;
        private TaskCompletionSource<string> _messageTcs;

        public string TopicValue { get; private set; }

        public ClassMQTT(string broker, int brokerPort, string topic)
        {
            _broker = broker;
            _brokerPort = brokerPort;
            _topic = topic;

            _factory = new MqttFactory();
            _mqttClient = _factory.CreateMqttClient();
            _connectionOptions = new MqttClientOptionsBuilder()
                .WithTcpServer(_broker, _brokerPort)
                .WithCredentials("", "")
                .WithClientId(_clientId)
                .WithCleanSession()
                .Build();

            // 提前注册回调,避免错过消息
            _mqttClient.ApplicationMessageReceivedAsync += OnMessageReceived;
        }

        private async Task OnMessageReceived(MqttApplicationMessageReceivedEventArgs e)
        {
            TopicValue = e.ApplicationMessage.ConvertPayloadToString();
            _messageTcs?.TrySetResult(TopicValue);
        }

        private async Task ConnectToMqtt()
        {
            var connectResult = await _mqttClient.ConnectAsync(_connectionOptions);
            if (connectResult.ResultCode != MqttClientConnectResultCode.Success)
            {
                throw new InvalidOperationException($"MQTT连接失败:{connectResult.ResultCode}");
            }
        }

        public async Task<string> GetTopicValue()
        {
            _messageTcs = new TaskCompletionSource<string>();
            
            await ConnectToMqtt();
            await _mqttClient.SubscribeAsync(_topic);

            // 等待第一条消息到达
            return await _messageTcs.Task;
        }

        public void Dispose()
        {
            _mqttClient.DisconnectAsync().Wait();
            _mqttClient.Dispose();
        }
    }
}

然后修改按钮事件:

private async void button1_Click(object sender, EventArgs e)
{
    using var testMQTT = new ClassMQTT("xxxxxx", 000000, "topicTest");
    string vTopic = await testMQTT.GetTopicValue();
    MessageBox.Show(vTopic);    
}

方案2:通过事件通知持续接收消息(适合实时更新场景)

如果需要持续接收该主题的消息并更新UI,可在类中定义事件,UI层订阅事件来获取消息:

修改ClassMQTT类:

using MQTTnet;
using MQTTnet.Client;
using System;
using System.Threading.Tasks;

namespace ClassMQTTTest
{
    public class ClassMQTT : IDisposable
    {
        private readonly string _broker;
        private readonly int _brokerPort;
        private readonly string _topic;
        private readonly string _clientId = Guid.NewGuid().ToString();
        private readonly MqttFactory _factory;
        private readonly IMqttClient _mqttClient;
        private readonly MqttClientOptions _connectionOptions;

        // 定义消息接收事件
        public event Action<string> MessageReceived;

        public ClassMQTT(string broker, int brokerPort, string topic)
        {
            _broker = broker;
            _brokerPort = brokerPort;
            _topic = topic;

            _factory = new MqttFactory();
            _mqttClient = _factory.CreateMqttClient();
            _connectionOptions = new MqttClientOptionsBuilder()
                .WithTcpServer(_broker, _brokerPort)
                .WithCredentials("", "")
                .WithClientId(_clientId)
                .WithCleanSession()
                .Build();

            _mqttClient.ApplicationMessageReceivedAsync += OnMessageReceived;
        }

        private async Task OnMessageReceived(MqttApplicationMessageReceivedEventArgs e)
        {
            var message = e.ApplicationMessage.ConvertPayloadToString();
            MessageReceived?.Invoke(message);
            await Task.CompletedTask;
        }

        private async Task ConnectToMqtt()
        {
            var connectResult = await _mqttClient.ConnectAsync(_connectionOptions);
            if (connectResult.ResultCode != MqttClientConnectResultCode.Success)
            {
                throw new InvalidOperationException($"MQTT连接失败:{connectResult.ResultCode}");
            }
        }

        public async Task StartListening()
        {
            await ConnectToMqtt();
            await _mqttClient.SubscribeAsync(_topic);
        }

        public void Dispose()
        {
            _mqttClient.DisconnectAsync().Wait();
            _mqttClient.Dispose();
        }
    }
}

修改按钮事件(注意UI更新需要Invoke):

private ClassMQTT _testMQTT;

private async void button1_Click(object sender, EventArgs e)
{
    _testMQTT = new ClassMQTT("xxxxxx", 000000, "topicTest");
    _testMQTT.MessageReceived += OnMqttMessageReceived;
    await _testMQTT.StartListening();
}

private void OnMqttMessageReceived(string message)
{
    // UI线程更新需要Invoke
    if (InvokeRequired)
    {
        Invoke(new Action<string>(OnMqttMessageReceived), message);
        return;
    }
    MessageBox.Show(message);
    // 或者更新文本框等控件
    // textBox1.Text = message;
}

// 窗体关闭时释放资源
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
    _testMQTT?.Dispose();
}
额外注意事项
  • 实现IDisposable接口,确保客户端断开连接并释放资源,避免内存泄漏;
  • 连接失败时抛出异常或返回状态,便于上层处理错误;
  • 提前注册消息回调,避免订阅后短时间内收到消息却错过的情况。

内容的提问来源于stack exchange,提问作者alexandrecrt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 04:21:05