基于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
相关产品推荐
相关产品推荐

