mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-29 01:32:37 +08:00
Wrap COM exceptions, close client connection when server stops, process retained messages from server
This commit is contained in:
@@ -10,8 +10,11 @@
|
|||||||
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
|
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
|
||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<description>MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker).</description>
|
<description>MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker).</description>
|
||||||
<releaseNotes>* [Client] Fixed WebSocket sub protocol negotiation (Thanks to @JanEggers)
|
<releaseNotes>* [Core] Fixed library reference issues for .NET 4.6 (Thanks to @JanEggers).
|
||||||
|
* [Core] Several COM exceptions are now wrapped properly resulting in less warnings in the trace.
|
||||||
|
* [Client] Fixed WebSocket sub protocol negotiation for ASP.NET Core 2 servers (Thanks to @JanEggers).
|
||||||
|
* [Server] Client connections are now closed when the server is stopped (Thanks to @zhudanfei).
|
||||||
|
* [Server] Published messages from the server are now retained (if set) (Thanks to @ChristianRiedl). BREAKING CHANGE!
|
||||||
</releaseNotes>
|
</releaseNotes>
|
||||||
<copyright>Copyright Christian Kratky 2016-2017</copyright>
|
<copyright>Copyright Christian Kratky 2016-2017</copyright>
|
||||||
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
|
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
|
||||||
|
|||||||
@@ -19,9 +19,8 @@ namespace MQTTnet.Implementations
|
|||||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Stream SendStream => RawReceiveStream;
|
public Stream SendStream { get; private set; }
|
||||||
public Stream ReceiveStream => RawReceiveStream;
|
public Stream ReceiveStream { get; private set; }
|
||||||
public Stream RawReceiveStream { get; private set; }
|
|
||||||
|
|
||||||
public async Task ConnectAsync()
|
public async Task ConnectAsync()
|
||||||
{
|
{
|
||||||
@@ -32,7 +31,7 @@ namespace MQTTnet.Implementations
|
|||||||
}
|
}
|
||||||
|
|
||||||
_webSocket = new ClientWebSocket();
|
_webSocket = new ClientWebSocket();
|
||||||
|
|
||||||
if (_options.RequestHeaders != null)
|
if (_options.RequestHeaders != null)
|
||||||
{
|
{
|
||||||
foreach (var requestHeader in _options.RequestHeaders)
|
foreach (var requestHeader in _options.RequestHeaders)
|
||||||
@@ -64,13 +63,13 @@ namespace MQTTnet.Implementations
|
|||||||
}
|
}
|
||||||
|
|
||||||
await _webSocket.ConnectAsync(new Uri(uri), CancellationToken.None).ConfigureAwait(false);
|
await _webSocket.ConnectAsync(new Uri(uri), CancellationToken.None).ConfigureAwait(false);
|
||||||
RawReceiveStream = new WebSocketStream(_webSocket);
|
|
||||||
|
SendStream = new WebSocketStream(_webSocket);
|
||||||
|
ReceiveStream = SendStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DisconnectAsync()
|
public async Task DisconnectAsync()
|
||||||
{
|
{
|
||||||
RawReceiveStream = null;
|
|
||||||
|
|
||||||
if (_webSocket == null)
|
if (_webSocket == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -80,6 +79,8 @@ namespace MQTTnet.Implementations
|
|||||||
{
|
{
|
||||||
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).ConfigureAwait(false);
|
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_webSocket = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ namespace MQTTnet.Implementations
|
|||||||
var response = await _webSocket.ReceiveAsync(new ArraySegment<byte>(buffer, currentOffset, count), cancellationToken).ConfigureAwait(false);
|
var response = await _webSocket.ReceiveAsync(new ArraySegment<byte>(buffer, currentOffset, count), cancellationToken).ConfigureAwait(false);
|
||||||
currentOffset += response.Count;
|
currentOffset += response.Count;
|
||||||
count -= response.Count;
|
count -= response.Count;
|
||||||
|
|
||||||
if (response.MessageType == WebSocketMessageType.Close)
|
if (response.MessageType == WebSocketMessageType.Close)
|
||||||
{
|
{
|
||||||
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, cancellationToken).ConfigureAwait(false);
|
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, cancellationToken).ConfigureAwait(false);
|
||||||
|
|||||||
@@ -85,9 +85,7 @@ namespace MQTTnet
|
|||||||
clientSessionsManager,
|
clientSessionsManager,
|
||||||
_serviceProvider.GetRequiredService<MqttClientSubscriptionsManager>(),
|
_serviceProvider.GetRequiredService<MqttClientSubscriptionsManager>(),
|
||||||
_serviceProvider.GetRequiredService<ILogger<MqttClientSession>>(),
|
_serviceProvider.GetRequiredService<ILogger<MqttClientSession>>(),
|
||||||
_serviceProvider.GetRequiredService<ILogger<MqttClientPendingMessagesQueue>>(),
|
_serviceProvider.GetRequiredService<ILogger<MqttClientPendingMessagesQueue>>());
|
||||||
_serviceProvider.GetRequiredService<IMqttClientRetainedMessageManager>()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IMqttClient CreateMqttClient()
|
public IMqttClient CreateMqttClient()
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MQTTnet.Core.Channel;
|
using MQTTnet.Core.Channel;
|
||||||
@@ -14,10 +15,12 @@ namespace MQTTnet.Core.Adapter
|
|||||||
{
|
{
|
||||||
public class MqttChannelCommunicationAdapter : IMqttCommunicationAdapter
|
public class MqttChannelCommunicationAdapter : IMqttCommunicationAdapter
|
||||||
{
|
{
|
||||||
|
private const uint ErrorOperationAborted = 0x800703E3;
|
||||||
|
|
||||||
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
|
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
|
||||||
private readonly ILogger<MqttChannelCommunicationAdapter> _logger;
|
private readonly ILogger<MqttChannelCommunicationAdapter> _logger;
|
||||||
private readonly IMqttCommunicationChannel _channel;
|
private readonly IMqttCommunicationChannel _channel;
|
||||||
|
|
||||||
public MqttChannelCommunicationAdapter(IMqttCommunicationChannel channel, IMqttPacketSerializer serializer, ILogger<MqttChannelCommunicationAdapter> logger)
|
public MqttChannelCommunicationAdapter(IMqttCommunicationChannel channel, IMqttPacketSerializer serializer, ILogger<MqttChannelCommunicationAdapter> logger)
|
||||||
{
|
{
|
||||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
@@ -29,166 +32,87 @@ namespace MQTTnet.Core.Adapter
|
|||||||
|
|
||||||
public async Task ConnectAsync(TimeSpan timeout)
|
public async Task ConnectAsync(TimeSpan timeout)
|
||||||
{
|
{
|
||||||
try
|
await ExecuteAndWrapExceptionAsync(() => _channel.ConnectAsync().TimeoutAfter(timeout));
|
||||||
{
|
|
||||||
await _channel.ConnectAsync().TimeoutAfter(timeout).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
catch (TaskCanceledException)
|
|
||||||
{
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
catch (OperationCanceledException)
|
|
||||||
{
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
catch (MqttCommunicationTimedOutException)
|
|
||||||
{
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
catch (MqttCommunicationException)
|
|
||||||
{
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
throw new MqttCommunicationException(exception);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DisconnectAsync(TimeSpan timeout)
|
public async Task DisconnectAsync(TimeSpan timeout)
|
||||||
{
|
{
|
||||||
try
|
await ExecuteAndWrapExceptionAsync(() => _channel.DisconnectAsync().TimeoutAfter(timeout));
|
||||||
{
|
|
||||||
await _channel.DisconnectAsync().TimeoutAfter(timeout).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
catch (TaskCanceledException)
|
|
||||||
{
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
catch (OperationCanceledException)
|
|
||||||
{
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
catch (MqttCommunicationTimedOutException)
|
|
||||||
{
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
catch (MqttCommunicationException)
|
|
||||||
{
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
throw new MqttCommunicationException(exception);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SendPacketsAsync(TimeSpan timeout, CancellationToken cancellationToken, IEnumerable<MqttBasePacket> packets)
|
public async Task SendPacketsAsync(TimeSpan timeout, CancellationToken cancellationToken, IEnumerable<MqttBasePacket> packets)
|
||||||
{
|
{
|
||||||
try
|
await ExecuteAndWrapExceptionAsync(async () =>
|
||||||
{
|
{
|
||||||
await _semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
|
await _semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
try
|
||||||
foreach (var packet in packets)
|
|
||||||
{
|
{
|
||||||
if (packet == null)
|
foreach (var packet in packets)
|
||||||
{
|
{
|
||||||
continue;
|
if (packet == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("TX >>> {0} [Timeout={1}]", packet, timeout);
|
||||||
|
|
||||||
|
var writeBuffer = PacketSerializer.Serialize(packet);
|
||||||
|
await _channel.SendStream.WriteAsync(writeBuffer, 0, writeBuffer.Length, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogInformation("TX >>> {0} [Timeout={1}]", packet, timeout);
|
if (timeout > TimeSpan.Zero)
|
||||||
|
{
|
||||||
var writeBuffer = PacketSerializer.Serialize(packet);
|
await _channel.SendStream.FlushAsync(cancellationToken).TimeoutAfter(timeout).ConfigureAwait(false);
|
||||||
await _channel.SendStream.WriteAsync(writeBuffer, 0, writeBuffer.Length, cancellationToken).ConfigureAwait(false);
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await _channel.SendStream.FlushAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
if (timeout > TimeSpan.Zero)
|
|
||||||
{
|
{
|
||||||
await _channel.SendStream.FlushAsync(cancellationToken).TimeoutAfter(timeout).ConfigureAwait(false);
|
_semaphore.Release();
|
||||||
}
|
}
|
||||||
else
|
});
|
||||||
{
|
|
||||||
await _channel.SendStream.FlushAsync(cancellationToken).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (TaskCanceledException)
|
|
||||||
{
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
catch (OperationCanceledException)
|
|
||||||
{
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
catch (MqttCommunicationTimedOutException)
|
|
||||||
{
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
catch (MqttCommunicationException)
|
|
||||||
{
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
throw new MqttCommunicationException(exception);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_semaphore.Release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<MqttBasePacket> ReceivePacketAsync(TimeSpan timeout, CancellationToken cancellationToken)
|
public async Task<MqttBasePacket> ReceivePacketAsync(TimeSpan timeout, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
ReceivedMqttPacket receivedMqttPacket = null;
|
MqttBasePacket packet = null;
|
||||||
try
|
await ExecuteAndWrapExceptionAsync(async () =>
|
||||||
{
|
{
|
||||||
if (timeout > TimeSpan.Zero)
|
ReceivedMqttPacket receivedMqttPacket = null;
|
||||||
|
try
|
||||||
{
|
{
|
||||||
receivedMqttPacket = await ReceiveAsync(_channel.ReceiveStream, cancellationToken).TimeoutAfter(timeout).ConfigureAwait(false);
|
if (timeout > TimeSpan.Zero)
|
||||||
}
|
{
|
||||||
else
|
receivedMqttPacket = await ReceiveAsync(_channel.ReceiveStream, cancellationToken).TimeoutAfter(timeout).ConfigureAwait(false);
|
||||||
{
|
}
|
||||||
receivedMqttPacket = await ReceiveAsync(_channel.ReceiveStream, cancellationToken).ConfigureAwait(false);
|
else
|
||||||
}
|
{
|
||||||
|
receivedMqttPacket = await ReceiveAsync(_channel.ReceiveStream, cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
if (receivedMqttPacket == null || cancellationToken.IsCancellationRequested)
|
if (receivedMqttPacket == null || cancellationToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
throw new TaskCanceledException();
|
throw new TaskCanceledException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var packet = PacketSerializer.Deserialize(receivedMqttPacket);
|
packet = PacketSerializer.Deserialize(receivedMqttPacket);
|
||||||
if (packet == null)
|
if (packet == null)
|
||||||
{
|
{
|
||||||
throw new MqttProtocolViolationException("Received malformed packet.");
|
throw new MqttProtocolViolationException("Received malformed packet.");
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogInformation("RX <<< {0}", packet);
|
_logger.LogInformation("RX <<< {0}", packet);
|
||||||
return packet;
|
}
|
||||||
}
|
finally
|
||||||
catch (TaskCanceledException)
|
{
|
||||||
{
|
receivedMqttPacket?.Dispose();
|
||||||
throw;
|
}
|
||||||
}
|
});
|
||||||
catch (OperationCanceledException)
|
|
||||||
{
|
return packet;
|
||||||
throw;
|
|
||||||
}
|
|
||||||
catch (MqttCommunicationTimedOutException)
|
|
||||||
{
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
catch (MqttCommunicationException)
|
|
||||||
{
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
throw new MqttCommunicationException(exception);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
receivedMqttPacket?.Dispose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<ReceivedMqttPacket> ReceiveAsync(Stream stream, CancellationToken cancellationToken)
|
private static async Task<ReceivedMqttPacket> ReceiveAsync(Stream stream, CancellationToken cancellationToken)
|
||||||
@@ -215,5 +139,42 @@ namespace MQTTnet.Core.Adapter
|
|||||||
|
|
||||||
return new ReceivedMqttPacket(header, new MemoryStream(body, 0, body.Length));
|
return new ReceivedMqttPacket(header, new MemoryStream(body, 0, body.Length));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static async Task ExecuteAndWrapExceptionAsync(Func<Task> action)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await action().ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (TaskCanceledException)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
catch (MqttCommunicationTimedOutException)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
catch (MqttCommunicationException)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
catch (COMException comException)
|
||||||
|
{
|
||||||
|
if ((uint)comException.HResult == ErrorOperationAborted)
|
||||||
|
{
|
||||||
|
throw new OperationCanceledException();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new MqttCommunicationException(comException);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
throw new MqttCommunicationException(exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,7 @@ namespace MQTTnet.Core.Server
|
|||||||
event EventHandler<MqttClientDisconnectedEventArgs> ClientDisconnected;
|
event EventHandler<MqttClientDisconnectedEventArgs> ClientDisconnected;
|
||||||
event EventHandler<MqttServerStartedEventArgs> Started;
|
event EventHandler<MqttServerStartedEventArgs> Started;
|
||||||
|
|
||||||
IList<ConnectedMqttClient> GetConnectedClients();
|
Task<IList<ConnectedMqttClient>> GetConnectedClientsAsync();
|
||||||
void Publish(IEnumerable<MqttApplicationMessage> applicationMessages);
|
|
||||||
|
|
||||||
Task StartAsync();
|
Task StartAsync();
|
||||||
Task StopAsync();
|
Task StopAsync();
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ namespace MQTTnet.Core.Server
|
|||||||
_pendingPublishPackets.Add(packet, CancellationToken.None);
|
_pendingPublishPackets.Add(packet, CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
_session.Stop();
|
await _session.StopAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ namespace MQTTnet.Core.Server
|
|||||||
{
|
{
|
||||||
private readonly HashSet<ushort> _unacknowledgedPublishPackets = new HashSet<ushort>();
|
private readonly HashSet<ushort> _unacknowledgedPublishPackets = new HashSet<ushort>();
|
||||||
|
|
||||||
private readonly IMqttClientRetainedMessageManager _clientRetainedMessageManager;
|
|
||||||
private readonly MqttClientSubscriptionsManager _subscriptionsManager;
|
private readonly MqttClientSubscriptionsManager _subscriptionsManager;
|
||||||
private readonly MqttClientSessionsManager _sessionsManager;
|
private readonly MqttClientSessionsManager _sessionsManager;
|
||||||
private readonly MqttClientPendingMessagesQueue _pendingMessagesQueue;
|
private readonly MqttClientPendingMessagesQueue _pendingMessagesQueue;
|
||||||
@@ -34,10 +33,8 @@ namespace MQTTnet.Core.Server
|
|||||||
MqttClientSessionsManager sessionsManager,
|
MqttClientSessionsManager sessionsManager,
|
||||||
MqttClientSubscriptionsManager subscriptionsManager,
|
MqttClientSubscriptionsManager subscriptionsManager,
|
||||||
ILogger<MqttClientSession> logger,
|
ILogger<MqttClientSession> logger,
|
||||||
ILogger<MqttClientPendingMessagesQueue> messageQueueLogger,
|
ILogger<MqttClientPendingMessagesQueue> messageQueueLogger)
|
||||||
IMqttClientRetainedMessageManager clientRetainedMessageManager)
|
|
||||||
{
|
{
|
||||||
_clientRetainedMessageManager = clientRetainedMessageManager ?? throw new ArgumentNullException(nameof(clientRetainedMessageManager));
|
|
||||||
_sessionsManager = sessionsManager ?? throw new ArgumentNullException(nameof(sessionsManager));
|
_sessionsManager = sessionsManager ?? throw new ArgumentNullException(nameof(sessionsManager));
|
||||||
_subscriptionsManager = subscriptionsManager ?? throw new ArgumentNullException(nameof(subscriptionsManager));
|
_subscriptionsManager = subscriptionsManager ?? throw new ArgumentNullException(nameof(subscriptionsManager));
|
||||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
@@ -82,7 +79,7 @@ namespace MQTTnet.Core.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Stop()
|
public async Task StopAsync()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -90,17 +87,21 @@ namespace MQTTnet.Core.Server
|
|||||||
_cancellationTokenSource?.Dispose();
|
_cancellationTokenSource?.Dispose();
|
||||||
_cancellationTokenSource = null;
|
_cancellationTokenSource = null;
|
||||||
|
|
||||||
_adapter = null;
|
if (_adapter != null)
|
||||||
|
{
|
||||||
|
await _adapter.DisconnectAsync(_options.DefaultCommunicationTimeout).ConfigureAwait(false);
|
||||||
|
_adapter = null;
|
||||||
|
}
|
||||||
|
|
||||||
_logger.LogInformation("Client '{0}': Disconnected.", ClientId);
|
_logger.LogInformation("Client '{0}': Session stopped.", ClientId);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
var willMessage = _willMessage;
|
var willMessage = _willMessage;
|
||||||
if (willMessage != null)
|
if (willMessage != null)
|
||||||
{
|
{
|
||||||
_willMessage = null; //clear willmessage so it is send just once
|
_willMessage = null; //clear willmessage so it is send just once
|
||||||
_sessionsManager.DispatchApplicationMessage(this, willMessage);
|
await _sessionsManager.DispatchApplicationMessageAsync(this, willMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -133,12 +134,12 @@ namespace MQTTnet.Core.Server
|
|||||||
catch (MqttCommunicationException exception)
|
catch (MqttCommunicationException exception)
|
||||||
{
|
{
|
||||||
_logger.LogWarning(new EventId(), exception, "Client '{0}': Communication exception while processing client packets.", ClientId);
|
_logger.LogWarning(new EventId(), exception, "Client '{0}': Communication exception while processing client packets.", ClientId);
|
||||||
Stop();
|
await StopAsync();
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
_logger.LogError(new EventId(), exception, "Client '{0}': Unhandled exception while processing client packets.", ClientId);
|
_logger.LogError(new EventId(), exception, "Client '{0}': Unhandled exception while processing client packets.", ClientId);
|
||||||
Stop();
|
await StopAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,14 +183,11 @@ namespace MQTTnet.Core.Server
|
|||||||
|
|
||||||
if (packet is MqttDisconnectPacket || packet is MqttConnectPacket)
|
if (packet is MqttDisconnectPacket || packet is MqttConnectPacket)
|
||||||
{
|
{
|
||||||
Stop();
|
return StopAsync();
|
||||||
return Task.FromResult(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogWarning("Client '{0}': Received not supported packet ({1}). Closing connection.", ClientId, packet);
|
_logger.LogWarning("Client '{0}': Received not supported packet ({1}). Closing connection.", ClientId, packet);
|
||||||
Stop();
|
return StopAsync();
|
||||||
|
|
||||||
return Task.FromResult(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task HandleIncomingSubscribePacketAsync(IMqttCommunicationAdapter adapter, MqttSubscribePacket subscribePacket, CancellationToken cancellationToken)
|
private async Task HandleIncomingSubscribePacketAsync(IMqttCommunicationAdapter adapter, MqttSubscribePacket subscribePacket, CancellationToken cancellationToken)
|
||||||
@@ -202,13 +200,13 @@ namespace MQTTnet.Core.Server
|
|||||||
if (subscribeResult.CloseConnection)
|
if (subscribeResult.CloseConnection)
|
||||||
{
|
{
|
||||||
await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new MqttDisconnectPacket()).ConfigureAwait(false);
|
await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new MqttDisconnectPacket()).ConfigureAwait(false);
|
||||||
Stop();
|
await StopAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task EnqueueSubscribedRetainedMessagesAsync(MqttSubscribePacket subscribePacket)
|
private async Task EnqueueSubscribedRetainedMessagesAsync(MqttSubscribePacket subscribePacket)
|
||||||
{
|
{
|
||||||
var retainedMessages = await _clientRetainedMessageManager.GetSubscribedMessagesAsync(subscribePacket).ConfigureAwait(false);
|
var retainedMessages = await _sessionsManager.GetRetainedMessagesAsync(subscribePacket).ConfigureAwait(false);
|
||||||
foreach (var publishPacket in retainedMessages)
|
foreach (var publishPacket in retainedMessages)
|
||||||
{
|
{
|
||||||
EnqueuePublishPacket(publishPacket.ToPublishPacket());
|
EnqueuePublishPacket(publishPacket.ToPublishPacket());
|
||||||
@@ -219,29 +217,16 @@ namespace MQTTnet.Core.Server
|
|||||||
{
|
{
|
||||||
var applicationMessage = publishPacket.ToApplicationMessage();
|
var applicationMessage = publishPacket.ToApplicationMessage();
|
||||||
|
|
||||||
var interceptorContext = new MqttApplicationMessageInterceptorContext
|
|
||||||
{
|
|
||||||
ApplicationMessage = applicationMessage
|
|
||||||
};
|
|
||||||
|
|
||||||
_options.ApplicationMessageInterceptor?.Invoke(interceptorContext);
|
|
||||||
applicationMessage = interceptorContext.ApplicationMessage;
|
|
||||||
|
|
||||||
if (applicationMessage.Retain)
|
|
||||||
{
|
|
||||||
await _clientRetainedMessageManager.HandleMessageAsync(ClientId, applicationMessage).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (applicationMessage.QualityOfServiceLevel)
|
switch (applicationMessage.QualityOfServiceLevel)
|
||||||
{
|
{
|
||||||
case MqttQualityOfServiceLevel.AtMostOnce:
|
case MqttQualityOfServiceLevel.AtMostOnce:
|
||||||
{
|
{
|
||||||
_sessionsManager.DispatchApplicationMessage(this, applicationMessage);
|
await _sessionsManager.DispatchApplicationMessageAsync(this, applicationMessage);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case MqttQualityOfServiceLevel.AtLeastOnce:
|
case MqttQualityOfServiceLevel.AtLeastOnce:
|
||||||
{
|
{
|
||||||
_sessionsManager.DispatchApplicationMessage(this, applicationMessage);
|
await _sessionsManager.DispatchApplicationMessageAsync(this, applicationMessage);
|
||||||
|
|
||||||
await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken,
|
await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken,
|
||||||
new MqttPubAckPacket { PacketIdentifier = publishPacket.PacketIdentifier });
|
new MqttPubAckPacket { PacketIdentifier = publishPacket.PacketIdentifier });
|
||||||
@@ -256,7 +241,7 @@ namespace MQTTnet.Core.Server
|
|||||||
_unacknowledgedPublishPackets.Add(publishPacket.PacketIdentifier);
|
_unacknowledgedPublishPackets.Add(publishPacket.PacketIdentifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
_sessionsManager.DispatchApplicationMessage(this, applicationMessage);
|
await _sessionsManager.DispatchApplicationMessageAsync(this, applicationMessage);
|
||||||
|
|
||||||
await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken,
|
await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken,
|
||||||
new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier }).ConfigureAwait(false);
|
new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier }).ConfigureAwait(false);
|
||||||
|
|||||||
@@ -16,19 +16,24 @@ namespace MQTTnet.Core.Server
|
|||||||
{
|
{
|
||||||
public sealed class MqttClientSessionsManager
|
public sealed class MqttClientSessionsManager
|
||||||
{
|
{
|
||||||
private readonly Dictionary<string, MqttClientSession> _clientSessions = new Dictionary<string, MqttClientSession>();
|
private readonly Dictionary<string, MqttClientSession> _sessions = new Dictionary<string, MqttClientSession>();
|
||||||
|
private readonly SemaphoreSlim _sessionsSemaphore = new SemaphoreSlim(1, 1);
|
||||||
|
|
||||||
|
private readonly MqttServerOptions _options;
|
||||||
private readonly ILogger<MqttClientSessionsManager> _logger;
|
private readonly ILogger<MqttClientSessionsManager> _logger;
|
||||||
private readonly IMqttClientSesssionFactory _clientSesssionFactory;
|
private readonly IMqttClientSesssionFactory _clientSesssionFactory;
|
||||||
private readonly MqttServerOptions _options;
|
private readonly IMqttClientRetainedMessageManager _clientRetainedMessageManager;
|
||||||
|
|
||||||
public MqttClientSessionsManager(
|
public MqttClientSessionsManager(
|
||||||
IOptions<MqttServerOptions> options,
|
IOptions<MqttServerOptions> options,
|
||||||
ILogger<MqttClientSessionsManager> logger,
|
ILogger<MqttClientSessionsManager> logger,
|
||||||
IMqttClientSesssionFactory clientSesssionFactory)
|
IMqttClientSesssionFactory clientSesssionFactory,
|
||||||
|
IMqttClientRetainedMessageManager clientRetainedMessageManager)
|
||||||
{
|
{
|
||||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
_options = options.Value ?? throw new ArgumentNullException(nameof(options));
|
_options = options.Value ?? throw new ArgumentNullException(nameof(options));
|
||||||
_clientSesssionFactory = clientSesssionFactory ?? throw new ArgumentNullException(nameof(clientSesssionFactory));
|
_clientSesssionFactory = clientSesssionFactory ?? throw new ArgumentNullException(nameof(clientSesssionFactory));
|
||||||
|
_clientRetainedMessageManager = clientRetainedMessageManager ?? throw new ArgumentNullException(nameof(clientRetainedMessageManager));
|
||||||
}
|
}
|
||||||
|
|
||||||
public event EventHandler<MqttClientConnectedEventArgs> ClientConnected;
|
public event EventHandler<MqttClientConnectedEventArgs> ClientConnected;
|
||||||
@@ -61,7 +66,7 @@ namespace MQTTnet.Core.Server
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var clientSession = GetOrCreateClientSession(connectPacket);
|
var clientSession = await GetOrCreateClientSessionAsync(connectPacket);
|
||||||
|
|
||||||
await clientAdapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new MqttConnAckPacket
|
await clientAdapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new MqttConnAckPacket
|
||||||
{
|
{
|
||||||
@@ -92,7 +97,7 @@ namespace MQTTnet.Core.Server
|
|||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
//ignored
|
// ignored
|
||||||
}
|
}
|
||||||
|
|
||||||
ClientDisconnected?.Invoke(this, new MqttClientDisconnectedEventArgs(new ConnectedMqttClient
|
ClientDisconnected?.Invoke(this, new MqttClientDisconnectedEventArgs(new ConnectedMqttClient
|
||||||
@@ -103,30 +108,58 @@ namespace MQTTnet.Core.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Clear()
|
public async Task StopAsync()
|
||||||
{
|
{
|
||||||
lock (_clientSessions)
|
await _sessionsSemaphore.WaitAsync().ConfigureAwait(false);
|
||||||
|
try
|
||||||
{
|
{
|
||||||
_clientSessions.Clear();
|
foreach (var session in _sessions)
|
||||||
|
{
|
||||||
|
await session.Value.StopAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
_sessions.Clear();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_sessionsSemaphore.Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IList<ConnectedMqttClient> GetConnectedClients()
|
public async Task<IList<ConnectedMqttClient>> GetConnectedClientsAsync()
|
||||||
{
|
{
|
||||||
lock (_clientSessions)
|
await _sessionsSemaphore.WaitAsync().ConfigureAwait(false);
|
||||||
|
try
|
||||||
{
|
{
|
||||||
return _clientSessions.Where(s => s.Value.IsConnected).Select(s => new ConnectedMqttClient
|
return _sessions.Where(s => s.Value.IsConnected).Select(s => new ConnectedMqttClient
|
||||||
{
|
{
|
||||||
ClientId = s.Value.ClientId,
|
ClientId = s.Value.ClientId,
|
||||||
ProtocolVersion = s.Value.ProtocolVersion ?? MqttProtocolVersion.V311
|
ProtocolVersion = s.Value.ProtocolVersion ?? MqttProtocolVersion.V311
|
||||||
}).ToList();
|
}).ToList();
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_sessionsSemaphore.Release();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DispatchApplicationMessage(MqttClientSession senderClientSession, MqttApplicationMessage applicationMessage)
|
public async Task DispatchApplicationMessageAsync(MqttClientSession senderClientSession, MqttApplicationMessage applicationMessage)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
var interceptorContext = new MqttApplicationMessageInterceptorContext
|
||||||
|
{
|
||||||
|
ApplicationMessage = applicationMessage
|
||||||
|
};
|
||||||
|
|
||||||
|
_options.ApplicationMessageInterceptor?.Invoke(interceptorContext);
|
||||||
|
applicationMessage = interceptorContext.ApplicationMessage;
|
||||||
|
|
||||||
|
if (applicationMessage.Retain)
|
||||||
|
{
|
||||||
|
await _clientRetainedMessageManager.HandleMessageAsync(senderClientSession?.ClientId, applicationMessage).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
var eventArgs = new MqttApplicationMessageReceivedEventArgs(senderClientSession?.ClientId, applicationMessage);
|
var eventArgs = new MqttApplicationMessageReceivedEventArgs(senderClientSession?.ClientId, applicationMessage);
|
||||||
ApplicationMessageReceived?.Invoke(this, eventArgs);
|
ApplicationMessageReceived?.Invoke(this, eventArgs);
|
||||||
}
|
}
|
||||||
@@ -135,15 +168,20 @@ namespace MQTTnet.Core.Server
|
|||||||
_logger.LogError(new EventId(), exception, "Error while processing application message");
|
_logger.LogError(new EventId(), exception, "Error while processing application message");
|
||||||
}
|
}
|
||||||
|
|
||||||
lock (_clientSessions)
|
lock (_sessions)
|
||||||
{
|
{
|
||||||
foreach (var clientSession in _clientSessions.Values.ToList())
|
foreach (var clientSession in _sessions.Values.ToList())
|
||||||
{
|
{
|
||||||
clientSession.EnqueuePublishPacket(applicationMessage.ToPublishPacket());
|
clientSession.EnqueuePublishPacket(applicationMessage.ToPublishPacket());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task<List<MqttApplicationMessage>> GetRetainedMessagesAsync(MqttSubscribePacket subscribePacket)
|
||||||
|
{
|
||||||
|
return _clientRetainedMessageManager.GetSubscribedMessagesAsync(subscribePacket);
|
||||||
|
}
|
||||||
|
|
||||||
private MqttConnectReturnCode ValidateConnection(MqttConnectPacket connectPacket)
|
private MqttConnectReturnCode ValidateConnection(MqttConnectPacket connectPacket)
|
||||||
{
|
{
|
||||||
if (_options.ConnectionValidator != null)
|
if (_options.ConnectionValidator != null)
|
||||||
@@ -154,17 +192,18 @@ namespace MQTTnet.Core.Server
|
|||||||
return MqttConnectReturnCode.ConnectionAccepted;
|
return MqttConnectReturnCode.ConnectionAccepted;
|
||||||
}
|
}
|
||||||
|
|
||||||
private GetOrCreateClientSessionResult GetOrCreateClientSession(MqttConnectPacket connectPacket)
|
private async Task<GetOrCreateClientSessionResult> GetOrCreateClientSessionAsync(MqttConnectPacket connectPacket)
|
||||||
{
|
{
|
||||||
lock (_clientSessions)
|
await _sessionsSemaphore.WaitAsync().ConfigureAwait(false);
|
||||||
|
try
|
||||||
{
|
{
|
||||||
var isSessionPresent = _clientSessions.TryGetValue(connectPacket.ClientId, out var clientSession);
|
var isSessionPresent = _sessions.TryGetValue(connectPacket.ClientId, out var clientSession);
|
||||||
if (isSessionPresent)
|
if (isSessionPresent)
|
||||||
{
|
{
|
||||||
if (connectPacket.CleanSession)
|
if (connectPacket.CleanSession)
|
||||||
{
|
{
|
||||||
_clientSessions.Remove(connectPacket.ClientId);
|
_sessions.Remove(connectPacket.ClientId);
|
||||||
clientSession.Stop();
|
await clientSession.StopAsync();
|
||||||
clientSession = null;
|
clientSession = null;
|
||||||
|
|
||||||
_logger.LogTrace("Stopped existing session of client '{0}'.", connectPacket.ClientId);
|
_logger.LogTrace("Stopped existing session of client '{0}'.", connectPacket.ClientId);
|
||||||
@@ -181,13 +220,17 @@ namespace MQTTnet.Core.Server
|
|||||||
isExistingSession = false;
|
isExistingSession = false;
|
||||||
|
|
||||||
clientSession = _clientSesssionFactory.CreateClientSession(connectPacket.ClientId, this);
|
clientSession = _clientSesssionFactory.CreateClientSession(connectPacket.ClientId, this);
|
||||||
_clientSessions[connectPacket.ClientId] = clientSession;
|
_sessions[connectPacket.ClientId] = clientSession;
|
||||||
|
|
||||||
_logger.LogTrace("Created a new session for client '{0}'.", connectPacket.ClientId);
|
_logger.LogTrace("Created a new session for client '{0}'.", connectPacket.ClientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new GetOrCreateClientSessionResult { IsExistingSession = isExistingSession, Session = clientSession };
|
return new GetOrCreateClientSessionResult { IsExistingSession = isExistingSession, Session = clientSession };
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_sessionsSemaphore.Release();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -20,12 +20,11 @@ namespace MQTTnet.Core.Server
|
|||||||
private CancellationTokenSource _cancellationTokenSource;
|
private CancellationTokenSource _cancellationTokenSource;
|
||||||
|
|
||||||
public MqttServer(
|
public MqttServer(
|
||||||
IOptions<MqttServerOptions> options,
|
IOptions<MqttServerOptions> options,
|
||||||
IEnumerable<IMqttServerAdapter> adapters,
|
IEnumerable<IMqttServerAdapter> adapters,
|
||||||
ILogger<MqttServer> logger,
|
ILogger<MqttServer> logger,
|
||||||
MqttClientSessionsManager clientSessionsManager,
|
MqttClientSessionsManager clientSessionsManager,
|
||||||
IMqttClientRetainedMessageManager clientRetainedMessageManager
|
IMqttClientRetainedMessageManager clientRetainedMessageManager)
|
||||||
)
|
|
||||||
{
|
{
|
||||||
_options = options.Value ?? throw new ArgumentNullException(nameof(options));
|
_options = options.Value ?? throw new ArgumentNullException(nameof(options));
|
||||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
@@ -35,7 +34,7 @@ namespace MQTTnet.Core.Server
|
|||||||
if (adapters == null)
|
if (adapters == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(adapters));
|
throw new ArgumentNullException(nameof(adapters));
|
||||||
}
|
}
|
||||||
|
|
||||||
_adapters = adapters.ToList();
|
_adapters = adapters.ToList();
|
||||||
|
|
||||||
@@ -44,9 +43,9 @@ namespace MQTTnet.Core.Server
|
|||||||
_clientSessionsManager.ClientDisconnected += OnClientDisconnected;
|
_clientSessionsManager.ClientDisconnected += OnClientDisconnected;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IList<ConnectedMqttClient> GetConnectedClients()
|
public Task<IList<ConnectedMqttClient>> GetConnectedClientsAsync()
|
||||||
{
|
{
|
||||||
return _clientSessionsManager.GetConnectedClients();
|
return _clientSessionsManager.GetConnectedClientsAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public event EventHandler<MqttServerStartedEventArgs> Started;
|
public event EventHandler<MqttServerStartedEventArgs> Started;
|
||||||
@@ -54,7 +53,7 @@ namespace MQTTnet.Core.Server
|
|||||||
public event EventHandler<MqttClientDisconnectedEventArgs> ClientDisconnected;
|
public event EventHandler<MqttClientDisconnectedEventArgs> ClientDisconnected;
|
||||||
public event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;
|
public event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;
|
||||||
|
|
||||||
public void Publish(IEnumerable<MqttApplicationMessage> applicationMessages)
|
public async Task PublishAsync(IEnumerable<MqttApplicationMessage> applicationMessages)
|
||||||
{
|
{
|
||||||
if (applicationMessages == null) throw new ArgumentNullException(nameof(applicationMessages));
|
if (applicationMessages == null) throw new ArgumentNullException(nameof(applicationMessages));
|
||||||
|
|
||||||
@@ -65,23 +64,10 @@ namespace MQTTnet.Core.Server
|
|||||||
|
|
||||||
foreach (var applicationMessage in applicationMessages)
|
foreach (var applicationMessage in applicationMessages)
|
||||||
{
|
{
|
||||||
var interceptorContext = new MqttApplicationMessageInterceptorContext
|
await _clientSessionsManager.DispatchApplicationMessageAsync(null, applicationMessage);
|
||||||
{
|
|
||||||
ApplicationMessage = applicationMessage
|
|
||||||
};
|
|
||||||
|
|
||||||
_options.ApplicationMessageInterceptor?.Invoke(interceptorContext);
|
|
||||||
|
|
||||||
_clientSessionsManager.DispatchApplicationMessage(null, interceptorContext.ApplicationMessage);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task PublishAsync(IEnumerable<MqttApplicationMessage> applicationMessages)
|
|
||||||
{
|
|
||||||
Publish(applicationMessages);
|
|
||||||
return Task.FromResult(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task StartAsync()
|
public async Task StartAsync()
|
||||||
{
|
{
|
||||||
if (_cancellationTokenSource != null) throw new InvalidOperationException("The MQTT server is already started.");
|
if (_cancellationTokenSource != null) throw new InvalidOperationException("The MQTT server is already started.");
|
||||||
@@ -113,7 +99,7 @@ namespace MQTTnet.Core.Server
|
|||||||
await adapter.StopAsync();
|
await adapter.StopAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
_clientSessionsManager.Clear();
|
await _clientSessionsManager.StopAsync();
|
||||||
|
|
||||||
_logger.LogInformation("Stopped.");
|
_logger.LogInformation("Stopped.");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user