mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-19 11:03:07 +08:00
Fixed a race condition in client and server.
This commit is contained in:
@@ -16,6 +16,8 @@ namespace MQTTnet.Server
|
||||
private readonly Func<Task> _timeoutCallback;
|
||||
private readonly IMqttNetLogger _logger;
|
||||
|
||||
private Task _workerTask;
|
||||
|
||||
public MqttClientKeepAliveMonitor(string clientId, Func<Task> timeoutCallback, IMqttNetLogger logger)
|
||||
{
|
||||
_clientId = clientId;
|
||||
@@ -34,7 +36,15 @@ namespace MQTTnet.Server
|
||||
return;
|
||||
}
|
||||
|
||||
Task.Run(async () => await RunAsync(keepAlivePeriod, cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false);
|
||||
_workerTask = Task.Run(() => RunAsync(keepAlivePeriod, cancellationToken).ConfigureAwait(false), cancellationToken);
|
||||
}
|
||||
|
||||
public void WaitForCompletion()
|
||||
{
|
||||
if (_workerTask != null)
|
||||
{
|
||||
Task.WaitAll(_workerTask);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RunAsync(int keepAlivePeriod, CancellationToken cancellationToken)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Adapter;
|
||||
@@ -18,6 +19,8 @@ namespace MQTTnet.Server
|
||||
private readonly MqttClientSession _clientSession;
|
||||
private readonly IMqttNetLogger _logger;
|
||||
|
||||
private Task _workerTask;
|
||||
|
||||
public MqttClientPendingMessagesQueue(IMqttServerOptions options, MqttClientSession clientSession, IMqttNetLogger logger)
|
||||
{
|
||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
@@ -36,7 +39,15 @@ namespace MQTTnet.Server
|
||||
return;
|
||||
}
|
||||
|
||||
Task.Run(async () => await SendQueuedPacketsAsync(adapter, cancellationToken), cancellationToken).ConfigureAwait(false);
|
||||
_workerTask = Task.Run(() => SendQueuedPacketsAsync(adapter, cancellationToken), cancellationToken);
|
||||
}
|
||||
|
||||
public void WaitForCompletion()
|
||||
{
|
||||
if (_workerTask != null)
|
||||
{
|
||||
Task.WaitAll(_workerTask);
|
||||
}
|
||||
}
|
||||
|
||||
public void Enqueue(MqttBasePacket packet)
|
||||
@@ -55,7 +66,7 @@ namespace MQTTnet.Server
|
||||
{
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
await SendQueuedPacketAsync(adapter, cancellationToken);
|
||||
await SendNextQueuedPacketAsync(adapter, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
@@ -67,7 +78,7 @@ namespace MQTTnet.Server
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SendQueuedPacketAsync(IMqttChannelAdapter adapter, CancellationToken cancellationToken)
|
||||
private async Task SendNextQueuedPacketAsync(IMqttChannelAdapter adapter, CancellationToken cancellationToken)
|
||||
{
|
||||
MqttBasePacket packet = null;
|
||||
try
|
||||
@@ -78,6 +89,11 @@ namespace MQTTnet.Server
|
||||
throw new InvalidOperationException(); // should not happen
|
||||
}
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new[] { packet }).ConfigureAwait(false);
|
||||
|
||||
_logger.Trace<MqttClientPendingMessagesQueue>("Enqueued packet sent (ClientId: {0}).", _clientSession.ClientId);
|
||||
@@ -110,7 +126,10 @@ namespace MQTTnet.Server
|
||||
}
|
||||
}
|
||||
|
||||
await _clientSession.StopAsync();
|
||||
if (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
await _clientSession.StopAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -96,14 +96,14 @@ namespace MQTTnet.Server
|
||||
}
|
||||
|
||||
_cancellationTokenSource?.Cancel(false);
|
||||
|
||||
PendingMessagesQueue.WaitForCompletion();
|
||||
KeepAliveMonitor.WaitForCompletion();
|
||||
|
||||
_cancellationTokenSource?.Dispose();
|
||||
_cancellationTokenSource = null;
|
||||
|
||||
if (_adapter != null)
|
||||
{
|
||||
await _adapter.DisconnectAsync(_options.DefaultCommunicationTimeout).ConfigureAwait(false);
|
||||
_adapter = null;
|
||||
}
|
||||
_adapter = null;
|
||||
|
||||
_logger.Info<MqttClientSession>("Client '{0}': Session stopped.", ClientId);
|
||||
}
|
||||
|
||||
@@ -33,16 +33,18 @@ namespace MQTTnet.Server
|
||||
public Action<string, TopicFilter> ClientSubscribedTopicCallback { get; set; }
|
||||
public Action<string, string> ClientUnsubscribedTopicCallback { get; set; }
|
||||
public Action<string, MqttApplicationMessage> ApplicationMessageReceivedCallback { get; set; }
|
||||
|
||||
|
||||
public async Task RunSessionAsync(IMqttChannelAdapter clientAdapter, CancellationToken cancellationToken)
|
||||
{
|
||||
var clientId = string.Empty;
|
||||
MqttClientSession clientSession = null;
|
||||
try
|
||||
{
|
||||
if (!(await clientAdapter.ReceivePacketAsync(_options.DefaultCommunicationTimeout, cancellationToken).ConfigureAwait(false) is MqttConnectPacket connectPacket))
|
||||
if (!(await clientAdapter.ReceivePacketAsync(_options.DefaultCommunicationTimeout, cancellationToken)
|
||||
.ConfigureAwait(false) is MqttConnectPacket connectPacket))
|
||||
{
|
||||
throw new MqttProtocolViolationException("The first packet from a client must be a 'CONNECT' packet [MQTT-3.1.0-1].");
|
||||
throw new MqttProtocolViolationException(
|
||||
"The first packet from a client must be a 'CONNECT' packet [MQTT-3.1.0-1].");
|
||||
}
|
||||
|
||||
clientId = connectPacket.ClientId;
|
||||
@@ -53,10 +55,13 @@ namespace MQTTnet.Server
|
||||
var connectReturnCode = ValidateConnection(connectPacket);
|
||||
if (connectReturnCode != MqttConnectReturnCode.ConnectionAccepted)
|
||||
{
|
||||
await clientAdapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new[] { new MqttConnAckPacket
|
||||
await clientAdapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new[]
|
||||
{
|
||||
ConnectReturnCode = connectReturnCode
|
||||
}}).ConfigureAwait(false);
|
||||
new MqttConnAckPacket
|
||||
{
|
||||
ConnectReturnCode = connectReturnCode
|
||||
}
|
||||
}).ConfigureAwait(false);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -64,11 +69,14 @@ namespace MQTTnet.Server
|
||||
var result = await GetOrCreateClientSessionAsync(connectPacket).ConfigureAwait(false);
|
||||
clientSession = result.Session;
|
||||
|
||||
await clientAdapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new[] { new MqttConnAckPacket
|
||||
await clientAdapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new[]
|
||||
{
|
||||
ConnectReturnCode = connectReturnCode,
|
||||
IsSessionPresent = result.IsExistingSession
|
||||
}}).ConfigureAwait(false);
|
||||
new MqttConnAckPacket
|
||||
{
|
||||
ConnectReturnCode = connectReturnCode,
|
||||
IsSessionPresent = result.IsExistingSession
|
||||
}
|
||||
}).ConfigureAwait(false);
|
||||
|
||||
ClientConnectedCallback?.Invoke(new ConnectedMqttClient
|
||||
{
|
||||
@@ -78,6 +86,9 @@ namespace MQTTnet.Server
|
||||
|
||||
await clientSession.RunAsync(connectPacket, clientAdapter).ConfigureAwait(false);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_logger.Error<MqttClientSessionsManager>(exception, exception.Message);
|
||||
@@ -87,12 +98,13 @@ namespace MQTTnet.Server
|
||||
try
|
||||
{
|
||||
await clientAdapter.DisconnectAsync(_options.DefaultCommunicationTimeout).ConfigureAwait(false);
|
||||
clientAdapter.Dispose();
|
||||
}
|
||||
catch (Exception)
|
||||
catch (Exception exception)
|
||||
{
|
||||
// ignored
|
||||
_logger.Error<MqttClientSessionsManager>(exception, exception.Message);
|
||||
}
|
||||
|
||||
|
||||
ClientDisconnectedCallback?.Invoke(new ConnectedMqttClient
|
||||
{
|
||||
ClientId = clientId,
|
||||
@@ -308,7 +320,7 @@ namespace MQTTnet.Server
|
||||
ClientSubscribedTopicCallback = null;
|
||||
ClientUnsubscribedTopicCallback = null;
|
||||
ApplicationMessageReceivedCallback = null;
|
||||
|
||||
|
||||
_semaphore?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user