Fix and improve session takeover process handling.

This commit is contained in:
Christian
2022-09-14 14:15:35 +02:00
parent c009cbef76
commit d4dad4860a
6 changed files with 59 additions and 45 deletions
+19 -1
View File
@@ -98,7 +98,7 @@ namespace MQTTnet.Server
try
{
Task.Run(() => SendPacketsLoop(cancellationToken), cancellationToken).RunInBackground(_logger);
_ = Task.Factory.StartNew(() => SendPacketsLoop(cancellationToken), cancellationToken, TaskCreationOptions.PreferFairness, TaskScheduler.Default).ConfigureAwait(false);
IsRunning = true;
@@ -391,7 +391,20 @@ namespace MQTTnet.Server
{
return;
}
// Check for cancellation again because receive packet might block some time.
if (cancellationToken.IsCancellationRequested)
{
return;
}
// The TCP connection of this client may be still open but the client has already been taken over by
// a new TCP connection. So we must exit here to make sure to no longer process any message.
if (IsTakenOver || !IsRunning)
{
return;
}
var processPacket = true;
if (_eventContainer.InterceptingInboundPacketEvent.HasHandlers)
@@ -493,6 +506,11 @@ namespace MQTTnet.Server
return;
}
if (IsTakenOver || !IsRunning)
{
return;
}
try
{
await SendPacketAsync(packetBusItem.Packet, cancellationToken).ConfigureAwait(false);
@@ -23,7 +23,6 @@ namespace MQTTnet.Server
{
readonly Dictionary<string, MqttClient> _clients = new Dictionary<string, MqttClient>(4096);
readonly AsyncLock _createConnectionSyncRoot = new AsyncLock();
readonly MqttServerEventContainer _eventContainer;
readonly MqttNetSourceLogger _logger;
readonly MqttServerOptions _options;
@@ -39,6 +38,8 @@ namespace MQTTnet.Server
readonly object _sessionsManagementLock = new object();
readonly HashSet<MqttSession> _subscriberSessions = new HashSet<MqttSession>();
readonly SemaphoreSlim _createConnectionSyncRoot = new SemaphoreSlim(1, 1);
public MqttClientSessionsManager(
MqttServerOptions options,
MqttRetainedMessagesManager retainedMessagesManager,
@@ -75,6 +76,8 @@ namespace MQTTnet.Server
public async Task DeleteSessionAsync(string clientId)
{
_logger.Verbose("Deleting session for client '{0}'.", clientId);
MqttClient connection;
lock (_clients)
@@ -193,7 +196,7 @@ namespace MQTTnet.Server
public void Dispose()
{
_createConnectionSyncRoot?.Dispose();
_createConnectionSyncRoot.Dispose();
lock (_sessionsManagementLock)
{
@@ -444,7 +447,7 @@ namespace MQTTnet.Server
IMqttChannelAdapter channelAdapter,
ValidatingConnectionEventArgs validatingConnectionEventArgs)
{
MqttClient connection;
MqttClient client;
bool sessionShouldPersist;
@@ -470,8 +473,9 @@ namespace MQTTnet.Server
sessionShouldPersist = !connectPacket.CleanSession;
}
using (await _createConnectionSyncRoot.WaitAsync(CancellationToken.None).ConfigureAwait(false))
await _createConnectionSyncRoot.WaitAsync(CancellationToken.None).ConfigureAwait(false);
try
{
MqttSession session;
lock (_sessionsManagementLock)
@@ -484,7 +488,7 @@ namespace MQTTnet.Server
{
if (connectPacket.CleanSession)
{
_logger.Verbose("Deleting existing session of client '{0}'.", connectPacket.ClientId);
_logger.Verbose("Deleting existing session of client '{0}' due to clean start.", connectPacket.ClientId);
session = CreateSession(connectPacket.ClientId, validatingConnectionEventArgs.SessionItems, sessionShouldPersist);
}
else
@@ -507,40 +511,44 @@ namespace MQTTnet.Server
await _eventContainer.PreparingSessionEvent.InvokeAsync(preparingSessionEventArgs).ConfigureAwait(false);
}
MqttClient existing;
MqttClient existingClient;
lock (_clients)
{
_clients.TryGetValue(connectPacket.ClientId, out existing);
connection = CreateConnection(connectPacket, channelAdapter, session);
_clients.TryGetValue(connectPacket.ClientId, out existingClient);
client = CreateClient(connectPacket, channelAdapter, session);
_clients[connectPacket.ClientId] = connection;
_clients[connectPacket.ClientId] = client;
}
if (existing != null)
if (existingClient != null)
{
existing.IsTakenOver = true;
await existing.StopAsync(MqttDisconnectReasonCode.SessionTakenOver).ConfigureAwait(false);
existingClient.IsTakenOver = true;
await existingClient.StopAsync(MqttDisconnectReasonCode.SessionTakenOver).ConfigureAwait(false);
if (_eventContainer.ClientConnectedEvent.HasHandlers)
{
var eventArgs = new ClientDisconnectedEventArgs(existing.Id, MqttClientDisconnectType.Takeover, existing.Endpoint, existing.Session.Items);
var eventArgs = new ClientDisconnectedEventArgs(existingClient.Id, MqttClientDisconnectType.Takeover, existingClient.Endpoint, existingClient.Session.Items);
await _eventContainer.ClientDisconnectedEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
}
}
}
finally
{
_createConnectionSyncRoot.Release();
}
return connection;
return client;
}
MqttClient CreateConnection(MqttConnectPacket connectPacket, IMqttChannelAdapter channelAdapter, MqttSession session)
MqttClient CreateClient(MqttConnectPacket connectPacket, IMqttChannelAdapter channelAdapter, MqttSession session)
{
return new MqttClient(connectPacket, channelAdapter, session, _options, _eventContainer, this, _rootLogger);
}
MqttSession CreateSession(string clientId, IDictionary sessionItems, bool isPersistent)
{
_logger.Verbose("Created a new session for client '{0}'.", clientId);
_logger.Verbose("Created new session for client '{0}'.", clientId);
return new MqttSession(clientId, isPersistent, sessionItems, _options, _eventContainer, _retainedMessagesManager, this);
}