mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-11-06 06:52:21 +08:00
Improve internal session/connection management and connection takeover.
This commit is contained in:
@@ -42,8 +42,6 @@ namespace MQTTnet.Server
|
|||||||
long _receivedApplicationMessagesCount;
|
long _receivedApplicationMessagesCount;
|
||||||
long _sentApplicationMessagesCount;
|
long _sentApplicationMessagesCount;
|
||||||
|
|
||||||
volatile bool _isTakeover;
|
|
||||||
|
|
||||||
public MqttClientConnection(MqttConnectPacket connectPacket,
|
public MqttClientConnection(MqttConnectPacket connectPacket,
|
||||||
IMqttChannelAdapter channelAdapter,
|
IMqttChannelAdapter channelAdapter,
|
||||||
MqttClientSession session,
|
MqttClientSession session,
|
||||||
@@ -74,6 +72,8 @@ namespace MQTTnet.Server
|
|||||||
|
|
||||||
public MqttConnectPacket ConnectPacket { get; }
|
public MqttConnectPacket ConnectPacket { get; }
|
||||||
|
|
||||||
|
public bool IsTakeOver { get; set; }
|
||||||
|
|
||||||
public string ClientId => ConnectPacket.ClientId;
|
public string ClientId => ConnectPacket.ClientId;
|
||||||
|
|
||||||
public bool IsReadingPacket => _channelAdapter.IsReadingPacket;
|
public bool IsReadingPacket => _channelAdapter.IsReadingPacket;
|
||||||
@@ -82,19 +82,30 @@ namespace MQTTnet.Server
|
|||||||
|
|
||||||
public MqttClientSession Session { get; }
|
public MqttClientSession Session { get; }
|
||||||
|
|
||||||
public Task StopAsync(bool isTakeover = false)
|
public async Task StopAsync()
|
||||||
{
|
{
|
||||||
_isTakeover = isTakeover;
|
if (IsTakeOver)
|
||||||
var task = _packageReceiverTask;
|
{
|
||||||
|
// Is is very important to send the DISCONNECT packet here BEFORE cancelling the
|
||||||
|
// token because the entire connection is closed (disposed) as soon as the cancellation
|
||||||
|
// token is cancelled. To there is no chance that the DISCONNECT packet will ever arrive
|
||||||
|
// at the client!
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _channelAdapter.SendPacketAsync(new MqttDisconnectPacket
|
||||||
|
{
|
||||||
|
ReasonCode = MqttDisconnectReasonCode.SessionTakenOver
|
||||||
|
}, _serverOptions.DefaultCommunicationTimeout, CancellationToken.None).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
_logger.Error(exception, "Client '{0}': Error while sending DISCONNECT packet after takeover.", ClientId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
StopInternal();
|
StopInternal();
|
||||||
|
|
||||||
if (task != null)
|
await _packageReceiverTask;
|
||||||
{
|
|
||||||
return task;
|
|
||||||
}
|
|
||||||
|
|
||||||
return PlatformAbstractionLayer.CompletedTask;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResetStatistics()
|
public void ResetStatistics()
|
||||||
@@ -129,11 +140,11 @@ namespace MQTTnet.Server
|
|||||||
|
|
||||||
public Task RunAsync()
|
public Task RunAsync()
|
||||||
{
|
{
|
||||||
_packageReceiverTask = RunInternalAsync();
|
_packageReceiverTask = RunInternalAsync(_cancellationToken.Token);
|
||||||
return _packageReceiverTask;
|
return _packageReceiverTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task RunInternalAsync()
|
async Task RunInternalAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var disconnectType = MqttClientDisconnectType.NotClean;
|
var disconnectType = MqttClientDisconnectType.NotClean;
|
||||||
try
|
try
|
||||||
@@ -142,12 +153,10 @@ namespace MQTTnet.Server
|
|||||||
|
|
||||||
Session.WillMessage = ConnectPacket.WillMessage;
|
Session.WillMessage = ConnectPacket.WillMessage;
|
||||||
|
|
||||||
var cancellationToken = _cancellationToken.Token;
|
await SendAsync(_channelAdapter.PacketFormatterAdapter.DataConverter.CreateConnAckPacket(_connectionValidatorContext), cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
Task.Run(() => SendPendingPacketsAsync(cancellationToken), cancellationToken).Forget(_logger);
|
Task.Run(() => SendPendingPacketsAsync(cancellationToken), cancellationToken).Forget(_logger);
|
||||||
|
|
||||||
await SendAsync(_channelAdapter.PacketFormatterAdapter.DataConverter.CreateConnAckPacket(_connectionValidatorContext), cancellationToken).ConfigureAwait(false);
|
|
||||||
|
|
||||||
Session.IsCleanSession = false;
|
Session.IsCleanSession = false;
|
||||||
|
|
||||||
while (!cancellationToken.IsCancellationRequested)
|
while (!cancellationToken.IsCancellationRequested)
|
||||||
@@ -156,7 +165,7 @@ namespace MQTTnet.Server
|
|||||||
if (packet == null)
|
if (packet == null)
|
||||||
{
|
{
|
||||||
// The client has closed the connection gracefully.
|
// The client has closed the connection gracefully.
|
||||||
break;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Interlocked.Increment(ref _sentPacketsCount);
|
Interlocked.Increment(ref _sentPacketsCount);
|
||||||
@@ -209,7 +218,7 @@ namespace MQTTnet.Server
|
|||||||
disconnectType = MqttClientDisconnectType.Clean;
|
disconnectType = MqttClientDisconnectType.Clean;
|
||||||
|
|
||||||
StopInternal();
|
StopInternal();
|
||||||
break;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_packetDispatcher.Dispatch(packet);
|
_packetDispatcher.Dispatch(packet);
|
||||||
@@ -233,39 +242,21 @@ namespace MQTTnet.Server
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
if (_isTakeover)
|
if (IsTakeOver)
|
||||||
{
|
{
|
||||||
disconnectType = MqttClientDisconnectType.Takeover;
|
disconnectType = MqttClientDisconnectType.Takeover;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Session.WillMessage != null)
|
if (Session.WillMessage != null)
|
||||||
{
|
{
|
||||||
_sessionsManager.DispatchApplicationMessage(Session.WillMessage, this);
|
_sessionsManager.DispatchApplicationMessage(Session.WillMessage, this);
|
||||||
Session.WillMessage = null;
|
Session.WillMessage = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
_packetDispatcher.Reset();
|
_packetDispatcher.Cancel();
|
||||||
|
|
||||||
_packageReceiverTask = null;
|
|
||||||
|
|
||||||
if (_isTakeover)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Don't use SendAsync here _cancellationToken is already cancelled.
|
|
||||||
await _channelAdapter.SendPacketAsync(new MqttDisconnectPacket
|
|
||||||
{
|
|
||||||
ReasonCode = MqttDisconnectReasonCode.SessionTakenOver
|
|
||||||
}, TimeSpan.Zero, CancellationToken.None).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
_logger.Error(exception, "Client '{0}': Error while sending DISCONNECT packet after takeover.", ClientId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.Info("Client '{0}': Connection stopped.", ClientId);
|
_logger.Info("Client '{0}': Connection stopped.", ClientId);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _sessionsManager.CleanUpClient(ClientId, _channelAdapter, disconnectType);
|
await _sessionsManager.CleanUpClient(ClientId, _channelAdapter, disconnectType);
|
||||||
@@ -279,7 +270,7 @@ namespace MQTTnet.Server
|
|||||||
|
|
||||||
void StopInternal()
|
void StopInternal()
|
||||||
{
|
{
|
||||||
_cancellationToken.Cancel(false);
|
_cancellationToken.Cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task EnqueueSubscribedRetainedMessagesAsync(ICollection<MqttTopicFilter> topicFilters)
|
async Task EnqueueSubscribedRetainedMessagesAsync(ICollection<MqttTopicFilter> topicFilters)
|
||||||
@@ -454,7 +445,10 @@ namespace MQTTnet.Server
|
|||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
if (exception is MqttCommunicationTimedOutException)
|
if (exception is OperationCanceledException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
else if (exception is MqttCommunicationTimedOutException)
|
||||||
{
|
{
|
||||||
_logger.Warning(exception, "Sending publish packet failed: Timeout (ClientId: {0}).", ClientId);
|
_logger.Warning(exception, "Sending publish packet failed: Timeout (ClientId: {0}).", ClientId);
|
||||||
}
|
}
|
||||||
@@ -462,9 +456,6 @@ namespace MQTTnet.Server
|
|||||||
{
|
{
|
||||||
_logger.Warning(exception, "Sending publish packet failed: Communication exception (ClientId: {0}).", ClientId);
|
_logger.Warning(exception, "Sending publish packet failed: Communication exception (ClientId: {0}).", ClientId);
|
||||||
}
|
}
|
||||||
else if (exception is OperationCanceledException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_logger.Error(exception, "Sending publish packet failed (ClientId: {0}).", ClientId);
|
_logger.Error(exception, "Sending publish packet failed (ClientId: {0}).", ClientId);
|
||||||
@@ -477,10 +468,7 @@ namespace MQTTnet.Server
|
|||||||
Session.ApplicationMessagesQueue.Enqueue(queuedApplicationMessage);
|
Session.ApplicationMessagesQueue.Enqueue(queuedApplicationMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_cancellationToken.Token.IsCancellationRequested)
|
StopInternal();
|
||||||
{
|
|
||||||
await StopAsync().ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,9 +19,9 @@ namespace MQTTnet.Server
|
|||||||
{
|
{
|
||||||
readonly AsyncQueue<MqttEnqueuedApplicationMessage> _messageQueue = new AsyncQueue<MqttEnqueuedApplicationMessage>();
|
readonly AsyncQueue<MqttEnqueuedApplicationMessage> _messageQueue = new AsyncQueue<MqttEnqueuedApplicationMessage>();
|
||||||
|
|
||||||
readonly AsyncLock _createConnectionGate = new AsyncLock();
|
readonly object _createConnectionSyncRoot = new object();
|
||||||
readonly ConcurrentDictionary<string, MqttClientConnection> _connections = new ConcurrentDictionary<string, MqttClientConnection>();
|
readonly Dictionary<string, MqttClientConnection> _connections = new Dictionary<string, MqttClientConnection>();
|
||||||
readonly ConcurrentDictionary<string, MqttClientSession> _sessions = new ConcurrentDictionary<string, MqttClientSession>();
|
readonly Dictionary<string, MqttClientSession> _sessions = new Dictionary<string, MqttClientSession>();
|
||||||
|
|
||||||
readonly IDictionary<object, object> _serverSessionItems = new ConcurrentDictionary<object, object>();
|
readonly IDictionary<object, object> _serverSessionItems = new ConcurrentDictionary<object, object>();
|
||||||
|
|
||||||
@@ -56,9 +56,16 @@ namespace MQTTnet.Server
|
|||||||
Task.Run(() => TryProcessQueuedApplicationMessagesAsync(_cancellationToken), _cancellationToken).Forget(_logger);
|
Task.Run(() => TryProcessQueuedApplicationMessagesAsync(_cancellationToken), _cancellationToken).Forget(_logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task StopAsync()
|
public async Task CloseAllConnectionsAsync()
|
||||||
{
|
{
|
||||||
foreach (var connection in _connections.Values)
|
List<MqttClientConnection> connections;
|
||||||
|
lock (_connections)
|
||||||
|
{
|
||||||
|
connections = _connections.Values.ToList();
|
||||||
|
_connections.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var connection in connections)
|
||||||
{
|
{
|
||||||
await connection.StopAsync().ConfigureAwait(false);
|
await connection.StopAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
@@ -66,7 +73,10 @@ namespace MQTTnet.Server
|
|||||||
|
|
||||||
public List<MqttClientConnection> GetConnections()
|
public List<MqttClientConnection> GetConnections()
|
||||||
{
|
{
|
||||||
return _connections.Values.ToList();
|
lock (_connections)
|
||||||
|
{
|
||||||
|
return _connections.Values.ToList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task HandleClientConnectionAsync(IMqttChannelAdapter clientAdapter)
|
public Task HandleClientConnectionAsync(IMqttChannelAdapter clientAdapter)
|
||||||
@@ -80,16 +90,19 @@ namespace MQTTnet.Server
|
|||||||
{
|
{
|
||||||
var result = new List<IMqttClientStatus>();
|
var result = new List<IMqttClientStatus>();
|
||||||
|
|
||||||
foreach (var connection in _connections.Values)
|
lock (_connections)
|
||||||
{
|
{
|
||||||
var clientStatus = new MqttClientStatus(connection);
|
foreach (var connection in _connections.Values)
|
||||||
connection.FillStatus(clientStatus);
|
{
|
||||||
|
var clientStatus = new MqttClientStatus(connection);
|
||||||
|
connection.FillStatus(clientStatus);
|
||||||
|
|
||||||
var sessionStatus = new MqttSessionStatus(connection.Session, this);
|
var sessionStatus = new MqttSessionStatus(connection.Session, this);
|
||||||
connection.Session.FillStatus(sessionStatus);
|
connection.Session.FillStatus(sessionStatus);
|
||||||
clientStatus.Session = sessionStatus;
|
clientStatus.Session = sessionStatus;
|
||||||
|
|
||||||
result.Add(clientStatus);
|
result.Add(clientStatus);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Task.FromResult((IList<IMqttClientStatus>)result);
|
return Task.FromResult((IList<IMqttClientStatus>)result);
|
||||||
@@ -99,12 +112,15 @@ namespace MQTTnet.Server
|
|||||||
{
|
{
|
||||||
var result = new List<IMqttSessionStatus>();
|
var result = new List<IMqttSessionStatus>();
|
||||||
|
|
||||||
foreach (var session in _sessions.Values)
|
lock (_sessions)
|
||||||
{
|
{
|
||||||
var sessionStatus = new MqttSessionStatus(session, this);
|
foreach (var session in _sessions.Values)
|
||||||
session.FillStatus(sessionStatus);
|
{
|
||||||
|
var sessionStatus = new MqttSessionStatus(session, this);
|
||||||
|
session.FillStatus(sessionStatus);
|
||||||
|
|
||||||
result.Add(sessionStatus);
|
result.Add(sessionStatus);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Task.FromResult((IList<IMqttSessionStatus>)result);
|
return Task.FromResult((IList<IMqttSessionStatus>)result);
|
||||||
@@ -122,12 +138,7 @@ namespace MQTTnet.Server
|
|||||||
if (clientId == null) throw new ArgumentNullException(nameof(clientId));
|
if (clientId == null) throw new ArgumentNullException(nameof(clientId));
|
||||||
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
|
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
|
||||||
|
|
||||||
if (!_sessions.TryGetValue(clientId, out var session))
|
return GetSession(clientId).SubscribeAsync(topicFilters);
|
||||||
{
|
|
||||||
throw new InvalidOperationException($"Client session '{clientId}' is unknown.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return session.SubscribeAsync(topicFilters);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task UnsubscribeAsync(string clientId, IEnumerable<string> topicFilters)
|
public Task UnsubscribeAsync(string clientId, IEnumerable<string> topicFilters)
|
||||||
@@ -135,25 +146,27 @@ namespace MQTTnet.Server
|
|||||||
if (clientId == null) throw new ArgumentNullException(nameof(clientId));
|
if (clientId == null) throw new ArgumentNullException(nameof(clientId));
|
||||||
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
|
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
|
||||||
|
|
||||||
if (!_sessions.TryGetValue(clientId, out var session))
|
return GetSession(clientId).UnsubscribeAsync(topicFilters);
|
||||||
{
|
|
||||||
throw new InvalidOperationException($"Client session '{clientId}' is unknown.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return session.UnsubscribeAsync(topicFilters);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DeleteSessionAsync(string clientId)
|
public async Task DeleteSessionAsync(string clientId)
|
||||||
{
|
{
|
||||||
if (_connections.TryGetValue(clientId, out var connection))
|
MqttClientConnection connection;
|
||||||
|
lock (_connections)
|
||||||
|
{
|
||||||
|
_connections.TryGetValue(clientId, out connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_sessions)
|
||||||
|
{
|
||||||
|
_sessions.Remove(clientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connection != null)
|
||||||
{
|
{
|
||||||
await connection.StopAsync().ConfigureAwait(false);
|
await connection.StopAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_sessions.TryRemove(clientId, out _))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.Verbose("Session for client '{0}' deleted.", clientId);
|
_logger.Verbose("Session for client '{0}' deleted.", clientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,19 +241,22 @@ namespace MQTTnet.Server
|
|||||||
|
|
||||||
var deliveryCount = 0;
|
var deliveryCount = 0;
|
||||||
|
|
||||||
foreach (var clientSession in _sessions.Values)
|
lock (_sessions)
|
||||||
{
|
{
|
||||||
var isSubscribed = clientSession.EnqueueApplicationMessage(
|
foreach (var clientSession in _sessions.Values)
|
||||||
applicationMessage,
|
|
||||||
sender?.ClientId,
|
|
||||||
false);
|
|
||||||
|
|
||||||
if (isSubscribed)
|
|
||||||
{
|
{
|
||||||
deliveryCount++;
|
var isSubscribed = clientSession.EnqueueApplicationMessage(
|
||||||
|
applicationMessage,
|
||||||
|
sender?.ClientId,
|
||||||
|
false);
|
||||||
|
|
||||||
|
if (isSubscribed)
|
||||||
|
{
|
||||||
|
deliveryCount++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deliveryCount == 0)
|
if (deliveryCount == 0)
|
||||||
{
|
{
|
||||||
var undeliveredMessageInterceptor = _options.UndeliveredMessageInterceptor;
|
var undeliveredMessageInterceptor = _options.UndeliveredMessageInterceptor;
|
||||||
@@ -295,7 +311,7 @@ namespace MQTTnet.Server
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var connection = await CreateClientConnectionAsync(connectPacket, connectionValidatorContext, channelAdapter).ConfigureAwait(false);
|
var connection = CreateClientConnection(connectPacket, connectionValidatorContext, channelAdapter);
|
||||||
await _eventDispatcher.SafeNotifyClientConnectedAsync(connectPacket.ClientId).ConfigureAwait(false);
|
await _eventDispatcher.SafeNotifyClientConnectedAsync(connectPacket.ClientId).ConfigureAwait(false);
|
||||||
await connection.RunAsync().ConfigureAwait(false);
|
await connection.RunAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
@@ -315,7 +331,10 @@ namespace MQTTnet.Server
|
|||||||
// in case it is a takeover _connections already contains the new connection
|
// in case it is a takeover _connections already contains the new connection
|
||||||
if (disconnectType != MqttClientDisconnectType.Takeover)
|
if (disconnectType != MqttClientDisconnectType.Takeover)
|
||||||
{
|
{
|
||||||
_connections.TryRemove(clientId, out _);
|
lock (_connections)
|
||||||
|
{
|
||||||
|
_connections.Remove(clientId);
|
||||||
|
}
|
||||||
|
|
||||||
if (!_options.EnablePersistentSessions)
|
if (!_options.EnablePersistentSessions)
|
||||||
{
|
{
|
||||||
@@ -360,52 +379,52 @@ namespace MQTTnet.Server
|
|||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task<MqttClientConnection> CreateClientConnectionAsync(MqttConnectPacket connectPacket, MqttConnectionValidatorContext connectionValidatorContext, IMqttChannelAdapter channelAdapter)
|
MqttClientConnection CreateClientConnection(MqttConnectPacket connectPacket, MqttConnectionValidatorContext connectionValidatorContext, IMqttChannelAdapter channelAdapter)
|
||||||
{
|
{
|
||||||
MqttClientConnection connection;
|
lock (_createConnectionSyncRoot)
|
||||||
MqttClientConnection existingConnection = null;
|
|
||||||
|
|
||||||
using (await _createConnectionGate.WaitAsync(_cancellationToken).ConfigureAwait(false))
|
|
||||||
{
|
{
|
||||||
var session = _sessions.AddOrUpdate(
|
MqttClientSession session;
|
||||||
connectPacket.ClientId,
|
lock (_sessions)
|
||||||
key =>
|
{
|
||||||
|
if (!_sessions.TryGetValue(connectPacket.ClientId, out session))
|
||||||
{
|
{
|
||||||
_logger.Verbose("Created a new session for client '{0}'.", key);
|
_logger.Verbose("Created a new session for client '{0}'.", connectPacket.ClientId);
|
||||||
return new MqttClientSession(key, connectionValidatorContext.SessionItems, _eventDispatcher, _options, _retainedMessagesManager, _rootLogger);
|
session = CreateSession(connectPacket.ClientId, connectionValidatorContext);
|
||||||
},
|
}
|
||||||
(key, existingSession) =>
|
else
|
||||||
{
|
{
|
||||||
if (connectPacket.CleanSession)
|
if (connectPacket.CleanSession)
|
||||||
{
|
{
|
||||||
_logger.Verbose("Deleting existing session of client '{0}'.", connectPacket.ClientId);
|
_logger.Verbose("Deleting existing session of client '{0}'.", connectPacket.ClientId);
|
||||||
return new MqttClientSession(key, connectionValidatorContext.SessionItems, _eventDispatcher, _options, _retainedMessagesManager, _rootLogger);
|
session = CreateSession(connectPacket.ClientId, connectionValidatorContext);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Verbose("Reusing existing session of client '{0}'.", connectPacket.ClientId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_logger.Verbose("Reusing existing session of client '{0}'.", connectPacket.ClientId);
|
_sessions[connectPacket.ClientId] = session;
|
||||||
return existingSession;
|
}
|
||||||
});
|
|
||||||
|
|
||||||
connection = new MqttClientConnection(connectPacket, channelAdapter, session, connectionValidatorContext, _options, this, _retainedMessagesManager, _rootLogger);
|
MqttClientConnection existingConnection;
|
||||||
|
MqttClientConnection connection;
|
||||||
|
lock (_connections)
|
||||||
|
{
|
||||||
|
_connections.TryGetValue(connectPacket.ClientId, out existingConnection);
|
||||||
|
connection = CreateConnection(connectPacket, channelAdapter, session, connectionValidatorContext);
|
||||||
|
|
||||||
_connections.AddOrUpdate(
|
_connections[connectPacket.ClientId] = connection;
|
||||||
connectPacket.ClientId,
|
}
|
||||||
key => connection,
|
|
||||||
(key, tempExistingConnection) =>
|
if (existingConnection != null)
|
||||||
{
|
{
|
||||||
existingConnection = tempExistingConnection;
|
existingConnection.IsTakeOver = true;
|
||||||
return connection;
|
existingConnection.StopAsync().GetAwaiter().GetResult();
|
||||||
});
|
}
|
||||||
|
|
||||||
|
return connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disconnect the client outside of the lock so that new clients can still connect while a single
|
|
||||||
// one is being disconnected.
|
|
||||||
if (existingConnection != null)
|
|
||||||
{
|
|
||||||
await existingConnection.StopAsync(true).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return connection;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task<MqttApplicationMessageInterceptorContext> InterceptApplicationMessageAsync(MqttClientConnection senderConnection, MqttApplicationMessage applicationMessage)
|
async Task<MqttApplicationMessageInterceptorContext> InterceptApplicationMessageAsync(MqttClientConnection senderConnection, MqttApplicationMessage applicationMessage)
|
||||||
@@ -447,5 +466,42 @@ namespace MQTTnet.Server
|
|||||||
_logger.Error(exception, "Error while disconnecting client channel.");
|
_logger.Error(exception, "Error while disconnecting client channel.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MqttClientSession GetSession(string clientId)
|
||||||
|
{
|
||||||
|
lock (_sessions)
|
||||||
|
{
|
||||||
|
if (!_sessions.TryGetValue(clientId, out var session))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Client session '{clientId}' is unknown.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MqttClientConnection CreateConnection(MqttConnectPacket connectPacket, IMqttChannelAdapter channelAdapter, MqttClientSession session, MqttConnectionValidatorContext connectionValidatorContext)
|
||||||
|
{
|
||||||
|
return new MqttClientConnection(
|
||||||
|
connectPacket,
|
||||||
|
channelAdapter,
|
||||||
|
session,
|
||||||
|
connectionValidatorContext,
|
||||||
|
_options,
|
||||||
|
this,
|
||||||
|
_retainedMessagesManager,
|
||||||
|
_rootLogger);
|
||||||
|
}
|
||||||
|
|
||||||
|
MqttClientSession CreateSession(string clientId, MqttConnectionValidatorContext connectionValidatorContext)
|
||||||
|
{
|
||||||
|
return new MqttClientSession(
|
||||||
|
clientId,
|
||||||
|
connectionValidatorContext.SessionItems,
|
||||||
|
_eventDispatcher,
|
||||||
|
_options,
|
||||||
|
_retainedMessagesManager,
|
||||||
|
_rootLogger);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -186,10 +186,10 @@ namespace MQTTnet.Server
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await _clientSessionsManager.StopAsync().ConfigureAwait(false);
|
|
||||||
|
|
||||||
_cancellationTokenSource.Cancel(false);
|
_cancellationTokenSource.Cancel(false);
|
||||||
|
|
||||||
|
await _clientSessionsManager.CloseAllConnectionsAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
foreach (var adapter in _adapters)
|
foreach (var adapter in _adapters)
|
||||||
{
|
{
|
||||||
adapter.ClientHandler = null;
|
adapter.ClientHandler = null;
|
||||||
|
|||||||
Reference in New Issue
Block a user