MQTTnet
MQTTnet 4.X 版本
MqttService
public class MqttService : IHostedService, IDisposable { private readonly IManagedMqttClient _mqttClient; private readonly ILogger<MqttService> _logger; private readonly AppSettings _options; public DateTime? LastConnectedTime { get; private set; } public DateTime? LastDisconnectedTime { get; private set; } public int DisconnectCount { get; private set; } int retryCount = 3; private readonly ConcurrentDictionary<string, Action<string>> _messageHandlers = new(); // 事件:当收到消息时触发,供 SignalR 或其他服务消费 public event Func<string, string, Task>? MessageReceived; // 2. 构造函数只负责赋值和事件绑定,不执行连接操作 public MqttService( IManagedMqttClient mqttClient, ILogger<MqttService> logger, IOptions<AppSettings> options) { _mqttClient = mqttClient ?? throw new ArgumentNullException(nameof(mqttClient)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _options = options.Value ?? throw new ArgumentException(nameof(options)); //// 如果需要监听变化 //_options.OnChange(settings => //{ // // 处理配置变化 // Console.WriteLine("Config changed!"); //}); // 3. 绑定事件处理程序 _mqttClient.ConnectedAsync += e => { _logger.LogInformation("MQTT 4.x 已成功连接并建立心跳"); return Task.CompletedTask; }; _mqttClient.DisconnectedAsync += e => { _logger.LogWarning($"MQTT 断开连接,原因: {e.Reason},异常: {e.Exception?.Message}"); LastDisconnectedTime = DateTime.UtcNow; DisconnectCount++; _logger.LogWarning($"第{DisconnectCount}次断开,原因: {e.Reason}"); // 根据断开原因采取不同策略 if (e.Reason == MqttClientDisconnectReason.NormalDisconnection) { _logger.LogInformation("正常断开,等待5秒后重连..."); //await Task.Delay(5000); } else { // 异常断开,使用指数退避策略 int delay = Math.Min(30000, 1000 * (int)Math.Pow(2, retryCount)); _logger.LogInformation($"异常断开,{delay / 1000}秒后重连..."); //await Task.Delay(delay); } return Task.CompletedTask; }; _mqttClient.ApplicationMessageReceivedAsync += e => { try { // 安全检查:防止 ApplicationMessage 为 null if (e?.ApplicationMessage == null) { _logger.LogWarning("收到空的 MQTT 消息事件。"); return Task.CompletedTask; } // 使用结构化日志替代字符串插值,提升性能并支持日志查询 // 注意:ConvertPayloadToString 可能涉及编码转换,确保其不会抛出异常 string payload = e.ApplicationMessage.ConvertPayloadToString() ?? string.Empty; _logger.LogInformation($"收到消息 - Topic: {e.ApplicationMessage.Topic}, Payload: {payload}"); // 在这里可以添加业务逻辑,例如发布内部事件 // 如果有异步操作,请使用 await return Task.CompletedTask; } catch (Exception ex) { // 记录异常,防止未处理异常导致 MQTT 客户端断开或应用崩溃 _logger.LogError(ex, "处理 MQTT 消息时发生错误。"); return Task.CompletedTask; } }; //// 启动托管客户端 //Task.Run(async () => { await StartAsync(CancellationToken.None); }); } private Task _mqttClient_DisconnectedAsync(MQTTnet.Client.MqttClientDisconnectedEventArgs arg) { throw new NotImplementedException(); } //protected override async Task ExecuteAsync(CancellationToken stoppingToken) //{ // var config = _options.Mqtt; // if (string.IsNullOrEmpty(config.ServerUrl)) // { // _logger.LogError("MQTT ServerUrl is not configured in AppSettings."); // return; // } // // 1. 配置连接选项 // var options = new ManagedMqttClientOptionsBuilder() // .WithAutoReconnectDelay(TimeSpan.FromSeconds(5)) // .WithClientOptions(new MqttClientOptionsBuilder() // .WithClientId(config.ClientId) // .WithTcpServer(config.ServerUrl) // .WithCredentials(config.Username, config.Password) // .WithCleanSession(false) // 改为false以保持会话状态ithCleanSession() // .WithKeepAlivePeriod(TimeSpan.FromSeconds(30)) // 心跳保活 // .Build()) // .Build(); // // 2. 启动客户端(建立连接) // await _mqttClient.StartAsync(options); // // 3. 订阅主题 // await _mqttClient.SubscribeAsync("scada/#"); // // 4. 保持服务运行,直到应用停止 // await Task.Delay(Timeout.Infinite, stoppingToken); //} // 4. IHostedService.StartAsync: 应用启动时自动调用 public async Task StartAsync(CancellationToken cancellationToken) { if (_mqttClient.IsStarted) { _logger.LogInformation("MQTT Client is already started."); return; } var config = _options.Mqtt; if (string.IsNullOrEmpty(config.ServerUrl)) { _logger.LogError("MQTT ServerUrl is not configured in AppSettings."); return; } //NormalDisconnection 表示连接被正常关闭,但客户端配置了自动重连 //确保 config.ClientId 在您的配置中是唯一的,如果多个客户端使用相同的ClientId连接同一服务器,后连接的客户端会踢掉先连接的 //增加待处理消息队列大小, 队列满时策略 // 强制使用 InterNetwork (IPv4)WithTcpServer(new MqttClientTcpOptions{AddressFamily = System.Net.Sockets.AddressFamily.InterNetwork}) // 构建 Managed Client 选项 var options = new ManagedMqttClientOptionsBuilder() .WithAutoReconnectDelay(TimeSpan.FromSeconds(5)) // 断线重连延迟 .WithClientOptions(new MqttClientOptionsBuilder() //.WithMaxPendingMessages(1000) // 增加待处理消息队列大小 //.WithPendingMessagesOverflowStrategy(PendingMessagesOverflowStrategy.DropOldest) // 队列满时策略 .WithClientId(config.ClientId) .WithTcpServer(config.ServerUrl) // 确保这里只传域名和端口,不带协议头,不要包含 mqtt:// 前缀。 .WithCredentials(config.Username, config.Password) .WithCleanSession(false) // 改为false以保持会话状态ithCleanSession() .WithKeepAlivePeriod(TimeSpan.FromSeconds(30)) // 心跳保活 .Build()) .Build(); try { _logger.LogInformation("Starting MQTT Client..."); await _mqttClient.StartAsync(options); _logger.LogInformation("MQTT Client Start command issued."); } catch (Exception ex) { _logger.LogError(ex, "Failed to start MQTT Client."); } } // 5. IHostedService.StopAsync: 应用停止时自动调用 public async Task StopAsync(CancellationToken cancellationToken) { _logger.LogInformation("Stopping MQTT Client..."); if (_mqttClient.IsStarted) { await _mqttClient.StopAsync(); } _logger.LogInformation("MQTT Client stopped."); } // 6. 业务方法:发布消息 public async Task PublishAsync(string topicSuffix, string payload, MqttQualityOfServiceLevel qos = MqttQualityOfServiceLevel.AtMostOnce) { if (!_mqttClient.IsConnected) { _logger.LogWarning("MQTT client is not connected. Message will be queued if possible."); } var config = _options.Mqtt; var topic = $"{config.BaseTopic}/{topicSuffix}"; var message = new MqttApplicationMessageBuilder() .WithTopic(topic) .WithPayload(payload) .WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce) .Build(); try { // EnqueueAsync 是 ManagedClient 的核心优势:断网时缓存,联网后自动发送 await _mqttClient.EnqueueAsync(message); _logger.LogDebug("Enqueued message to {Topic}", topic); } catch (Exception ex) { _logger.LogError(ex, "Failed to enqueue message to {Topic}", topic); } } // 7. 业务方法:订阅主题 public async Task SubscribeAsync(string topicSuffix, MqttQualityOfServiceLevel qos = MqttQualityOfServiceLevel.AtMostOnce) { if (!_mqttClient.IsStarted) { _logger.LogInformation("MQTT Client is not already started."); } var config = _options.Mqtt; //var topic = $"{config.BaseTopic}/{topicSuffix}"; var topic = new MqttTopicFilterBuilder().WithTopic(topicSuffix).WithQualityOfServiceLevel(qos).Build(); try { await _mqttClient.SubscribeAsync(new[] { topic }); _logger.LogInformation("Subscribed to {Topic}", topic); } catch (Exception ex) { _logger.LogError(ex, "Failed to subscribe to {Topic}", topic); } } // 8. 重新加载配置后重启客户端(可选) public async Task RestartClientAsync() { _logger.LogInformation("Restarting MQTT Client..."); await StopAsync(CancellationToken.None); await StartAsync(CancellationToken.None); } public void Dispose() { _mqttClient?.Dispose(); } // 添加网络连通性检查 public async Task<bool> CheckServerReachable() { try { using var tcpClient = new TcpClient(); var uri = new Uri(_options.Mqtt.ServerUrl); await tcpClient.ConnectAsync(uri.Host, uri.Port > 0 ? uri.Port : 1883); return tcpClient.Connected; } catch { return false; } } private Task OnConnectedAsync(MqttClientConnectedEventArgs arg) { _logger.LogInformation("✅ MQTT Connected to broker"); // 重连后自动恢复订阅(示例) return SubscribeAsync("devices/+/telemetry"); } private Task OnDisconnectedAsync(MqttClientDisconnectedEventArgs arg) { _logger.LogWarning("❌ MQTT Disconnected: {Reason}", arg.Reason); return Task.CompletedTask; } private async Task OnMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs arg) { var topic = arg.ApplicationMessage.Topic; var payload = Encoding.UTF8.GetString(arg.ApplicationMessage.PayloadSegment); _logger.LogDebug("Received [{Topic}]: {Payload}", topic, payload); // 触发事件供外部消费 if (MessageReceived != null) await MessageReceived.Invoke(topic, payload); } //public override async Task StopAsync(CancellationToken cancellationToken) //{ // if (_mqttClient.IsConnected) // await _mqttClient.DisconnectAsync(cancellationToken: cancellationToken); // _mqttClient.Dispose(); // await base.StopAsync(cancellationToken); //} //protected override async Task ExecuteAsync(CancellationToken stoppingToken) //{ // while (!stoppingToken.IsCancellationRequested) // { // try // { // if (!_mqttClient.IsConnected) // { // await _mqttClient.ConnectAsync(_options, stoppingToken); // } // await Task.Delay(5000, stoppingToken); // 健康检查间隔 // } // catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested) // { // break; // } // catch (Exception ex) // { // _logger.LogError(ex, "MQTT connection loop error"); // await Task.Delay(5000, stoppingToken); // } // } //} }
ConfigService
public class ConfigService : IConfigService { private readonly AppSettings _options; private readonly IConfiguration _configuration; // 注入 IOptionsSnapshot 确保每次请求都能读取到最新的配置文件值 public ConfigService(IOptionsSnapshot<AppSettings> options, IConfiguration configuration) { _options = options.Value; _configuration = configuration; } public AppSettings GetAppSettings() => _options; public string? GetSingleValue(string key) => _configuration[key]; }
Program.cs
// A. 注册 MqttFactory 为单例 (工厂模式) builder.Services.AddSingleton<MqttFactory>(); // B. 注册 IManagedMqttClient 为单例 // 使用工厂创建客户端,确保整个应用生命周期中只有一个 MQTT 连接实例 builder.Services.AddSingleton<IManagedMqttClient>(sp => { var factory = sp.GetRequiredService<MqttFactory>(); return factory.CreateManagedMqttClient(); }); // C. 注册后台服务 MqttService 为 Hosted Service(负责启动/停止连接) // AddHostedService 会自动在应用启动时调用 StartAsync,停止时调用 StopAsync builder.Services.AddHostedService<MqttService>(); //// D. (可选) 如果 Controller 需要注入 IMqttService 接口 //// 将接口映射到具体的 MqttService 实例, 注册其他使用 MQTT 的服务(如 Controller 依赖的服务), ///IManagedMqttClient 为单例,使用工厂创建客户端代替 //builder.Services.AddSingleton<IMqttService>(sp => sp.GetRequiredService<MqttService>()); // ==========================================
// 1. 注册配置选项
builder.Services.Configure<AppSettings>(builder.Configuration.GetSection("AppSettings"));
AppSettings
public class AppSettings { public MqttConfig Mqtt { get; set; } = new(); } public class MqttConfig { public string ServerUrl { get; set; } = ""; public string ClientId { get; set; } = ""; public string Username { get; set; } = ""; public string Password { get; set; } = ""; public string BaseTopic { get; set; } = ""; }
SystemController
[Route("api/[controller]")] [ApiController] public class SystemController : ControllerBase { //private readonly IMqttService _mqttService; private readonly IConfigService _configService; private readonly ILogger<SystemController> _logger; private readonly IManagedMqttClient _mqttClient; //public SystemController(IMqttService mqttService, IConfigService configService, ILogger<SystemController> logger) //{ // _mqttService = mqttService; // _configService = configService; // _logger = logger; //} public SystemController(IManagedMqttClient mqttClient, IConfigService configService, ILogger<SystemController> logger) { _mqttClient = mqttClient; _configService = configService; _logger = logger; } [HttpPost("start-mqtt")] public IActionResult StartMqtt([FromQuery] string topic) { if (!_mqttClient.IsConnected) { return BadRequest("MQTT Client is not connected."); } _mqttClient.SubscribeAsync(topic); return Ok(new ApiResponseDto { Success = true, Message = $"已成功订阅 Topic: {topic}" }); } [HttpGet("config")] public IActionResult GetConfig([FromQuery] string? key = null) { if (string.IsNullOrWhiteSpace(key)) { // 返回所有嵌套配置 return Ok(new ApiResponseDto { Success = true, Data = _configService.GetAppSettings() }); } // 返回单个节点值 return Ok(new ApiResponseDto { Success = true, Data = _configService.GetSingleValue(key) }); } }
=======
5.X版本有点问题
#region 手动自动重连和断线重连 /// <summary> /// MQTT 后台服务,自动连接、重连、心跳,并可通过 API 手动启停或发布消息。 /// </summary> public class MqttClientService : BackgroundService, IMqttClientService { private readonly ILogger<MqttClientService> _logger; private readonly IOptionsMonitor<MqttSettings> _settings; private IMqttClient? _mqttClient; private MqttClientOptions? _clientOptions; private volatile bool _connected; private readonly SemaphoreSlim _connectLock = new(1, 1); // 用于手动控制的标志(Start/Stop API) private volatile bool _manualStop; private readonly TaskCompletionSource _readyTcs = new(); public bool IsConnected => _connected; public event Func<MqttApplicationMessageReceivedEventArgs, Task>? MessageReceived; public MqttClientService(ILogger<MqttClientService> logger, IOptionsMonitor<MqttSettings> settings) { _logger = logger; _settings = settings; } /// <summary> /// BackgroundService 主循环,持续维护连接直到应用程序关闭。 /// </summary> protected override async Task ExecuteAsync(CancellationToken stoppingToken) { // 等待手动 Start(如果外部 API 调用 StartAsync 再开始连接,也可直接开始) // 此处设计为自动开始,若需手动启动可注释下面一行,并在 StartAsync 中设置信号。 _readyTcs.TrySetResult(); while (!stoppingToken.IsCancellationRequested) { try { // 如果手动停止或退出令牌触发,则断开并等待 if (_manualStop || stoppingToken.IsCancellationRequested) { await DisconnectAsync(); await Task.Delay(1000, stoppingToken); continue; } if (_connected) { // 已连接,仅等待断开信号 await Task.Delay(1000, stoppingToken); continue; } // 未连接则尝试连接 await ConnectAsync(stoppingToken); } catch (OperationCanceledException opc) when (stoppingToken.IsCancellationRequested) { // 正常关闭 _logger.LogInformation($"MqttClientService normal stopped.正常关闭。{opc.Message}"); } catch (Exception ex) { _logger.LogError(ex, "MQTT 服务执行异常,将在 5 秒后重试"); await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken); } } } /// <summary> /// 建立 MQTT 连接,使用最新的配置。 /// </summary> private async Task ConnectAsync(CancellationToken cancellationToken) { await _connectLock.WaitAsync(cancellationToken); try { if (_connected || _mqttClient?.IsConnected == true) return; var config = _settings.CurrentValue; _clientOptions = new MqttClientOptionsBuilder() .WithTcpServer(config.Broker, config.Port) .WithClientId(config.ClientId) .WithCredentials(config.Username, config.Password) .WithKeepAlivePeriod(TimeSpan.FromSeconds(config.KeepAlivePeriod)) .Build(); var factory = new MqttClientFactory(); _mqttClient = factory.CreateMqttClient(); _mqttClient.ConnectedAsync += OnConnected; _mqttClient.DisconnectedAsync += OnDisconnected; _mqttClient.ApplicationMessageReceivedAsync += OnMessageReceived; _logger.LogInformation("正在连接到 MQTT Broker: {Broker}:{Port} ...", config.Broker, config.Port); await _mqttClient.ConnectAsync(_clientOptions, cancellationToken); _connected = true; _logger.LogInformation("MQTT 连接成功"); } catch (Exception ex) { _logger.LogWarning(ex, "MQTT 连接失败,{Delay}秒后重试", _settings.CurrentValue.ReconnectDelaySeconds); CleanupClient(); await Task.Delay(TimeSpan.FromSeconds(_settings.CurrentValue.ReconnectDelaySeconds), cancellationToken); } finally { _connectLock.Release(); } } /// <summary> /// 断开连接并清理客户端资源。 /// </summary> private async Task DisconnectAsync() { if (_mqttClient == null) return; try { if (_mqttClient.IsConnected) { await _mqttClient.DisconnectAsync(MqttClientDisconnectOptionsReason.NormalDisconnection); } } catch (Exception ex) { _logger.LogWarning(ex, "MQTT 断开时异常"); } finally { CleanupClient(); _connected = false; } } private void CleanupClient() { if (_mqttClient == null) return; _mqttClient.ConnectedAsync -= OnConnected; _mqttClient.DisconnectedAsync -= OnDisconnected; _mqttClient.ApplicationMessageReceivedAsync -= OnMessageReceived; _mqttClient.Dispose(); _mqttClient = null; } private async Task OnConnected(MqttClientConnectedEventArgs args) { _connected = true; _logger.LogInformation("MQTT 连接事件触发"); // 连接成功后订阅默认主题 if (_mqttClient != null) { var topic = $"{_settings.CurrentValue.TopicPrefix}#"; await _mqttClient.SubscribeAsync(topic); _logger.LogInformation("已订阅主题: {Topic}", topic); } } private async Task OnDisconnected(MqttClientDisconnectedEventArgs args) { _connected = false; _logger.LogWarning("MQTT 断开连接,原因:{Reason}", args.Reason); // 自动重连由 ExecuteAsync 主循环处理,此处仅标记状态 } private async Task OnMessageReceived(MqttApplicationMessageReceivedEventArgs args) { var payload = Encoding.UTF8.GetString(args.ApplicationMessage.Payload); _logger.LogInformation("收到消息 [{Topic}]: {Payload}", args.ApplicationMessage.Topic, payload); if (MessageReceived != null) await MessageReceived.Invoke(args); } /// <summary> /// 手动启动 MQTT 服务(若在 ExecuteAsync 中设置了等待信号,可释放信号启动连接循环) /// </summary> public async Task StartAsync() { _manualStop = false; _readyTcs.TrySetResult(); _logger.LogInformation("MQTT 服务手动启动"); // 触发立即连接尝试 if (!_connected) { _ = ConnectAsync(CancellationToken.None); } await Task.CompletedTask; } /// <summary> /// 手动停止 MQTT 服务(断开连接,后台循环将不再自动重连) /// </summary> public async Task StopAsync() { _manualStop = true; await DisconnectAsync(); _logger.LogInformation("MQTT 服务手动停止"); } /// <summary> /// 发布消息到指定主题 /// </summary> public async Task PublishAsync(string topic, string payload, bool retain = false, MqttQualityOfServiceLevel qos = MqttQualityOfServiceLevel.AtLeastOnce) { if (!_connected || _mqttClient == null) throw new InvalidOperationException("MQTT 未连接"); var message = new MqttApplicationMessageBuilder() .WithTopic(topic) .WithPayload(payload) .WithQualityOfServiceLevel(qos) .WithRetainFlag(retain) .Build(); await _mqttClient.PublishAsync(message); _logger.LogInformation("已发布到 [{Topic}]: {Payload}", topic, payload); } /// <summary> /// 订阅指定主题(需已连接) /// </summary> public async Task SubscribeAsync(string topic, MqttQualityOfServiceLevel qos = MqttQualityOfServiceLevel.AtLeastOnce) { if (!_connected || _mqttClient == null) throw new InvalidOperationException("MQTT 未连接,无法订阅主题"); var options = new MqttClientSubscribeOptionsBuilder() .WithTopicFilter(topic, qos) .Build(); await _mqttClient.SubscribeAsync(options); _logger.LogInformation("已订阅主题: {Topic} (QoS: {Qos})", topic, qos); } public override async Task StopAsync(CancellationToken cancellationToken) { _manualStop = true; await DisconnectAsync(); await base.StopAsync(cancellationToken); } public override void Dispose() { _mqttClient?.Dispose(); _connectLock.Dispose(); base.Dispose(); } } #endregion #region 手动自动重连 //public class MqttClientService : IMqttClientService, IDisposable //{ // private readonly ILogger<MqttClientService> _logger; // private readonly IOptionsMonitor<MqttSettings> _settings; // private IMqttClient? _mqttClient; // private MqttClientOptions? _options; // private CancellationTokenSource? _reconnectCts; // private volatile bool _intentionalStop; // public bool IsConnected => _mqttClient?.IsConnected ?? false; // public event Func<MqttApplicationMessageReceivedEventArgs, Task>? MessageReceived; // public MqttClientService(ILogger<MqttClientService> logger, IOptionsMonitor<MqttSettings> settings) // { // _logger = logger; // _settings = settings; // } // public async Task StartAsync() // { // if (_mqttClient?.IsConnected == true) return; // _intentionalStop = false; // var factory = new MqttClientFactory(); // _mqttClient = factory.CreateMqttClient(); // var currentSettings = _settings.CurrentValue; // _options = new MqttClientOptionsBuilder() // .WithTcpServer(currentSettings.Broker, currentSettings.Port) // .WithClientId(currentSettings.ClientId) // .WithCredentials(currentSettings.Username, currentSettings.Password) // .WithKeepAlivePeriod(TimeSpan.FromSeconds(currentSettings.KeepAlivePeriod)) // .Build(); // _mqttClient.ConnectedAsync += OnConnected; // _mqttClient.DisconnectedAsync += OnDisconnected; // _mqttClient.ApplicationMessageReceivedAsync += OnMessageReceived; // await ConnectWithRetry(); // } // private async Task ConnectWithRetry() // { // while (!_intentionalStop) // { // try // { // await _mqttClient!.ConnectAsync(_options!, CancellationToken.None); // _logger.LogInformation("MQTT 已连接"); // return; // } // catch (Exception ex) // { // _logger.LogWarning(ex, "MQTT 连接失败,{Delay}秒后重试", _settings.CurrentValue.ReconnectDelaySeconds); // await Task.Delay(TimeSpan.FromSeconds(_settings.CurrentValue.ReconnectDelaySeconds)); // } // } // } // private async Task OnConnected(MqttClientConnectedEventArgs args) // { // _logger.LogInformation("MQTT 连接成功事件触发"); // // 订阅默认主题(示例) // await _mqttClient!.SubscribeAsync($"{_settings.CurrentValue.TopicPrefix}#"); // } // private async Task OnDisconnected(MqttClientDisconnectedEventArgs args) // { // _logger.LogWarning("MQTT 断开连接,原因:{Reason}", args.Reason); // if (!_intentionalStop) // { // _ = Task.Run(async () => // { // await Task.Delay(1000); // await ConnectWithRetry(); // }); // } // } // private async Task OnMessageReceived(MqttApplicationMessageReceivedEventArgs args) // { // var payload = Encoding.UTF8.GetString(args.ApplicationMessage.Payload); // _logger.LogInformation("收到消息 [{Topic}]: {Payload}", args.ApplicationMessage.Topic, payload); // if (MessageReceived != null) // await MessageReceived.Invoke(args); // } // public async Task StopAsync() // { // _intentionalStop = true; // if (_mqttClient != null) // { // _mqttClient.ConnectedAsync -= OnConnected; // _mqttClient.DisconnectedAsync -= OnDisconnected; // _mqttClient.ApplicationMessageReceivedAsync -= OnMessageReceived; // if (_mqttClient.IsConnected) // await _mqttClient.DisconnectAsync(); // _mqttClient.Dispose(); // _mqttClient = null; // } // } // public async Task PublishAsync(string topic, string payload, bool retain = false) // { // if (_mqttClient?.IsConnected != true) throw new InvalidOperationException("MQTT 未连接"); // var message = new MqttApplicationMessageBuilder() // .WithTopic(topic) // .WithPayload(payload) // .WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce) // .WithRetainFlag(retain) // .Build(); // await _mqttClient.PublishAsync(message); // _logger.LogInformation("已发布到 [{Topic}]: {Payload}", topic, payload); // } // public void Dispose() // { // _mqttClient?.Dispose(); // _reconnectCts?.Cancel(); // } //} #endregion
//// 注册 MQTTnet 5.x 的核心工厂与托管客户端 builder.Services.AddSingleton<MqttClientFactory>(); builder.Services.AddSingleton<IMqttClient>(sp => { var factory = new MqttClientFactory(); return factory.CreateMqttClient(); }); builder.Services.AddHostedService<MqttClientService>();

浙公网安备 33010602011771号