Example #1
0
        Task SendAsync(MqttBasePacket packet, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();

            _sendTracker.Restart();

            return(_adapter.SendPacketAsync(packet, cancellationToken));
        }
Example #2
0
        private Task SendAsync(MqttBasePacket packet, CancellationToken cancellationToken)
        {
            if (cancellationToken.IsCancellationRequested)
            {
                return(Task.FromResult(0));
            }

            _sendTracker.Restart();

            return(_adapter.SendPacketAsync(packet, Options.CommunicationTimeout, cancellationToken));
        }
Example #3
0
        private async Task OnClientConnectedAsync(IMqttChannelAdapter adapter)
        {
            while (true)
            {
                var packet = await adapter.ReceivePacketAsync(Timeout.InfiniteTimeSpan, default);

                switch (packet)
                {
                case MqttConnectPacket connectPacket:
                    await adapter.SendPacketAsync(new MqttConnAckPacket
                    {
                        ReturnCode       = MqttConnectReturnCode.ConnectionAccepted,
                        ReasonCode       = MqttConnectReasonCode.Success,
                        IsSessionPresent = false
                    }, Timeout.InfiniteTimeSpan,
                                                  default);

                    break;

                case MqttDisconnectPacket disconnectPacket:
                    break;

                case MqttAuthPacket mqttAuthPacket:
                    break;

                case MqttConnAckPacket connAckPacket:
                    break;

                case MqttPublishPacket mqttPublishPacket:
                    break;

                case MqttSubscribePacket mqttSubscribePacket:
                    var ack = new MqttSubAckPacket
                    {
                        PacketIdentifier = mqttSubscribePacket.PacketIdentifier,
                        ReturnCodes      = new List <MqttSubscribeReturnCode> {
                            MqttSubscribeReturnCode.SuccessMaximumQoS0
                        }
                    };
                    ack.ReasonCodes.Add(MqttSubscribeReasonCode.GrantedQoS0);

                    await adapter.SendPacketAsync(ack, Timeout.InfiniteTimeSpan, default);

                    break;

                default:
                    break;
                }
            }
        }
        public async Task HandleClientConnectionAsync(IMqttChannelAdapter channelAdapter, CancellationToken cancellationToken)
        {
            try
            {
                MqttConnectPacket connectPacket;
                try
                {
                    using (var timeoutToken = new CancellationTokenSource(_options.DefaultCommunicationTimeout))
                    {
                        var firstPacket = await channelAdapter.ReceivePacketAsync(timeoutToken.Token).ConfigureAwait(false);

                        connectPacket = firstPacket as MqttConnectPacket;
                        if (connectPacket == null)
                        {
                            _logger.Warning(null,
                                            "Client '{0}': First received packet was no 'CONNECT' packet [MQTT-3.1.0-1].",
                                            channelAdapter.Endpoint);

                            return;
                        }
                    }
                }
                catch (OperationCanceledException)
                {
                    _logger.Warning(null, "Client '{0}': Connected but did not sent a CONNECT packet.", channelAdapter.Endpoint);
                    return;
                }
                catch (MqttCommunicationTimedOutException)
                {
                    _logger.Warning(null, "Client '{0}': Connected but did not sent a CONNECT packet.", channelAdapter.Endpoint);
                    return;
                }

                var connectionValidatorContext = await ValidateConnectionAsync(connectPacket, channelAdapter).ConfigureAwait(false);

                if (connectionValidatorContext.ReasonCode != MqttConnectReasonCode.Success)
                {
                    // Send failure response here without preparing a session. The result for a successful connect
                    // will be sent from the session itself.
                    var connAckPacket = channelAdapter.PacketFormatterAdapter.DataConverter.CreateConnAckPacket(connectionValidatorContext);
                    await channelAdapter.SendPacketAsync(connAckPacket, cancellationToken).ConfigureAwait(false);

                    return;
                }

                var connection = await CreateClientConnectionAsync(connectPacket, connectionValidatorContext, channelAdapter).ConfigureAwait(false);

                await _eventDispatcher.SafeNotifyClientConnectedAsync(connectPacket.ClientId).ConfigureAwait(false);

                await connection.RunAsync().ConfigureAwait(false);
            }
            catch (OperationCanceledException)
            {
            }
            catch (Exception exception)
            {
                _logger.Error(exception, exception.Message);
            }
        }
Example #5
0
        Task SendAsync(MqttBasePacket packet, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();

            _lastPacketSentTimestamp = DateTime.UtcNow;

            return(_adapter.SendPacketAsync(packet, cancellationToken));
        }
        private async Task SendAsync(MqttBasePacket packet)
        {
            await _channelAdapter.SendPacketAsync(packet, _serverOptions.DefaultCommunicationTimeout, _cancellationToken.Token).ConfigureAwait(false);

            Interlocked.Increment(ref _receivedPacketsCount);

            if (packet is MqttPublishPacket)
            {
                Interlocked.Increment(ref _receivedApplicationMessagesCount);
            }
        }
        private void HandleIncomingSubscribePacket(IMqttChannelAdapter adapter, MqttSubscribePacket subscribePacket, CancellationToken cancellationToken)
        {
            var subscribeResult = _subscriptionsManager.Subscribe(subscribePacket);

            adapter.SendPacketAsync(subscribeResult.ResponsePacket, cancellationToken).GetAwaiter().GetResult();

            if (subscribeResult.CloseConnection)
            {
                Stop(MqttClientDisconnectType.NotClean, true);
                return;
            }

            EnqueueSubscribedRetainedMessages(subscribePacket.TopicFilters);
        }
        private void HandleIncomingPublishPacketWithQoS1(
            IMqttChannelAdapter adapter,
            MqttPublishPacket publishPacket,
            CancellationToken cancellationToken)
        {
            _sessionsManager.EnqueueApplicationMessage(this, publishPacket);

            var response = new MqttPubAckPacket
            {
                PacketIdentifier = publishPacket.PacketIdentifier
            };

            adapter.SendPacketAsync(response, cancellationToken).GetAwaiter().GetResult();
        }
        private void HandleIncomingPublishPacketWithQoS2(
            IMqttChannelAdapter adapter,
            MqttPublishPacket publishPacket,
            CancellationToken cancellationToken)
        {
            // QoS 2 is implement as method "B" (4.3.3 QoS 2: Exactly once delivery)
            _sessionsManager.EnqueueApplicationMessage(this, publishPacket);

            var response = new MqttPubRecPacket
            {
                PacketIdentifier = publishPacket.PacketIdentifier
            };

            adapter.SendPacketAsync(response, cancellationToken).GetAwaiter().GetResult();
        }
Example #10
0
        public async Task StopAsync(MqttClientDisconnectReason reason)
        {
            Status            = MqttClientConnectionStatus.Finalizing;
            _disconnectReason = reason;

            if (reason == MqttClientDisconnectReason.SessionTakenOver || reason == MqttClientDisconnectReason.KeepAliveTimeout)
            {
                // 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
                {
                    var disconnectOptions = new MqttClientDisconnectOptions
                    {
                        ReasonCode   = reason,
                        ReasonString = reason.ToString()
                    };

                    var disconnectPacket = _channelAdapter.PacketFormatterAdapter.DataConverter.CreateDisconnectPacket(disconnectOptions);

                    using (var timeout = new CancellationTokenSource(_serverOptions.DefaultCommunicationTimeout))
                    {
                        await _channelAdapter.SendPacketAsync(disconnectPacket, timeout.Token).ConfigureAwait(false);
                    }
                }
                catch (Exception exception)
                {
                    _logger.Warning(exception, "Client '{0}': Error while sending DISCONNECT packet after takeover.", ClientId);
                }
            }

            StopInternal();

            await(_packageReceiverTask ?? PlatformAbstractionLayer.CompletedTask);
        }
Example #11
0
        public async Task SendAsync(MqttBasePacket packet, CancellationToken cancellationToken)
        {
            if (packet is null)
            {
                throw new ArgumentNullException(nameof(packet));
            }

            if (_adapter == null)
            {
                throw new InvalidOperationException("Low level MQTT client is not connected.");
            }

            try
            {
                await _adapter.SendPacketAsync(packet, _options.CommunicationTimeout, cancellationToken).ConfigureAwait(false);
            }
            catch (Exception)
            {
                await SafeDisconnect(cancellationToken).ConfigureAwait(false);

                throw;
            }
        }
Example #12
0
        private async Task RunSessionAsync(IMqttChannelAdapter clientAdapter, CancellationToken cancellationToken)
        {
            var clientId = string.Empty;

            try
            {
                var firstPacket = await clientAdapter.ReceivePacketAsync(_options.DefaultCommunicationTimeout, cancellationToken).ConfigureAwait(false);

                if (firstPacket == null)
                {
                    return;
                }

                if (!(firstPacket is MqttConnectPacket connectPacket))
                {
                    throw new MqttProtocolViolationException("The first packet from a client must be a 'CONNECT' packet [MQTT-3.1.0-1].");
                }

                clientId = connectPacket.ClientId;

                // Switch to the required protocol version before sending any response.
                clientAdapter.PacketSerializer.ProtocolVersion = connectPacket.ProtocolVersion;

                var connectReturnCode = ValidateConnection(connectPacket, clientAdapter);
                if (connectReturnCode != MqttConnectReturnCode.ConnectionAccepted)
                {
                    await clientAdapter.SendPacketAsync(
                        new MqttConnAckPacket
                    {
                        ConnectReturnCode = connectReturnCode
                    },
                        cancellationToken).ConfigureAwait(false);

                    return;
                }

                var result = PrepareClientSession(connectPacket);

                await clientAdapter.SendPacketAsync(
                    new MqttConnAckPacket
                {
                    ConnectReturnCode = connectReturnCode,
                    IsSessionPresent  = result.IsExistingSession
                },
                    cancellationToken).ConfigureAwait(false);

                _logger.Info("Client '{0}': Connected.", clientId);
                _eventDispatcher.OnClientConnected(clientId);

                await result.Session.RunAsync(connectPacket, clientAdapter).ConfigureAwait(false);
            }
            catch (OperationCanceledException)
            {
            }
            catch (Exception exception)
            {
                _logger.Error(exception, exception.Message);
            }
            finally
            {
                if (!_options.EnablePersistentSessions)
                {
                    DeleteSession(clientId);
                }
            }
        }
Example #13
0
 Task SendPacketAsync(MqttBasePacket packet, CancellationToken cancellationToken)
 {
     return(_channelAdapter.SendPacketAsync(packet, cancellationToken).ContinueWith(task => { Statistics.HandleSentPacket(packet); }, cancellationToken));
 }
        private void HandleIncomingUnsubscribePacket(IMqttChannelAdapter adapter, MqttUnsubscribePacket unsubscribePacket, CancellationToken cancellationToken)
        {
            var unsubscribeResult = _subscriptionsManager.Unsubscribe(unsubscribePacket);

            adapter.SendPacketAsync(unsubscribeResult, cancellationToken).GetAwaiter().GetResult();
        }
        private async Task HandleClientAsync(IMqttChannelAdapter channelAdapter, CancellationToken cancellationToken)
        {
            var    disconnectType = MqttClientDisconnectType.NotClean;
            string clientId       = null;

            try
            {
                var firstPacket = await channelAdapter.ReceivePacketAsync(_options.DefaultCommunicationTimeout, cancellationToken).ConfigureAwait(false);

                if (!(firstPacket is MqttConnectPacket connectPacket))
                {
                    _logger.Warning(null, "The first packet from client '{0}' was no 'CONNECT' packet [MQTT-3.1.0-1].", channelAdapter.Endpoint);
                    return;
                }

                clientId = connectPacket.ClientId;

                var validatorContext = await ValidateConnectionAsync(connectPacket, channelAdapter).ConfigureAwait(false);

                if (validatorContext.ReasonCode != MqttConnectReasonCode.Success)
                {
                    // Send failure response here without preparing a session. The result for a successful connect
                    // will be sent from the session itself.
                    var connAckPacket = channelAdapter.PacketFormatterAdapter.DataConverter.CreateConnAckPacket(validatorContext);
                    await channelAdapter.SendPacketAsync(connAckPacket, _options.DefaultCommunicationTimeout, cancellationToken).ConfigureAwait(false);

                    return;
                }

                var connection = await CreateConnectionAsync(channelAdapter, connectPacket).ConfigureAwait(false);

                await _eventDispatcher.HandleClientConnectedAsync(clientId).ConfigureAwait(false);

                disconnectType = await connection.RunAsync().ConfigureAwait(false);
            }
            catch (OperationCanceledException)
            {
            }
            catch (Exception exception)
            {
                _logger.Error(exception, exception.Message);
            }
            finally
            {
                if (clientId != null)
                {
                    _connections.TryRemove(clientId, out _);

                    if (!_options.EnablePersistentSessions)
                    {
                        await DeleteSessionAsync(clientId).ConfigureAwait(false);
                    }
                }

                await TryCleanupChannelAsync(channelAdapter).ConfigureAwait(false);

                if (clientId != null)
                {
                    await _eventDispatcher.TryHandleClientDisconnectedAsync(clientId, disconnectType).ConfigureAwait(false);
                }
            }
        }
        private void ProcessReceivedPacket(IMqttChannelAdapter adapter, MqttBasePacket packet, CancellationToken cancellationToken)
        {
            if (packet is MqttPublishPacket publishPacket)
            {
                HandleIncomingPublishPacket(adapter, publishPacket, cancellationToken);
                return;
            }

            if (packet is MqttPingReqPacket)
            {
                adapter.SendPacketAsync(new MqttPingRespPacket(), cancellationToken).GetAwaiter().GetResult();
                return;
            }

            if (packet is MqttPubRelPacket pubRelPacket)
            {
                var responsePacket = new MqttPubCompPacket
                {
                    PacketIdentifier = pubRelPacket.PacketIdentifier
                };

                adapter.SendPacketAsync(responsePacket, cancellationToken).GetAwaiter().GetResult();
                return;
            }

            if (packet is MqttPubRecPacket pubRecPacket)
            {
                var responsePacket = new MqttPubRelPacket
                {
                    PacketIdentifier = pubRecPacket.PacketIdentifier
                };

                adapter.SendPacketAsync(responsePacket, cancellationToken).GetAwaiter().GetResult();
                return;
            }

            if (packet is MqttPubAckPacket || packet is MqttPubCompPacket)
            {
                return;
            }

            if (packet is MqttSubscribePacket subscribePacket)
            {
                HandleIncomingSubscribePacket(adapter, subscribePacket, cancellationToken);
                return;
            }

            if (packet is MqttUnsubscribePacket unsubscribePacket)
            {
                HandleIncomingUnsubscribePacket(adapter, unsubscribePacket, cancellationToken);
                return;
            }

            if (packet is MqttDisconnectPacket)
            {
                Stop(MqttClientDisconnectType.Clean, true);
                return;
            }

            if (packet is MqttConnectPacket)
            {
                Stop(MqttClientDisconnectType.NotClean, true);
                return;
            }

            _logger.Warning(null, "Client '{0}': Received not supported packet ({1}). Closing connection.", ClientId, packet);
            Stop(MqttClientDisconnectType.NotClean, true);
        }
Example #17
0
        async Task RunInternalAsync()
        {
            var disconnectType = MqttClientDisconnectType.NotClean;

            try
            {
                _logger.Info("Client '{0}': Session started.", ClientId);

                _channelAdapter.ReadingPacketStartedCallback   = OnAdapterReadingPacketStarted;
                _channelAdapter.ReadingPacketCompletedCallback = OnAdapterReadingPacketCompleted;

                Session.WillMessage = ConnectPacket.WillMessage;

                Task.Run(() => SendPendingPacketsAsync(_cancellationToken.Token), _cancellationToken.Token).Forget(_logger);

                // TODO: Change to single thread in SessionManager. Or use SessionManager and stats from KeepAliveMonitor.
                _keepAliveMonitor.Start(ConnectPacket.KeepAlivePeriod, _cancellationToken.Token);

                await SendAsync(_channelAdapter.PacketFormatterAdapter.DataConverter.CreateConnAckPacket(_connectionValidatorContext)).ConfigureAwait(false);

                Session.IsCleanSession = false;

                while (!_cancellationToken.IsCancellationRequested)
                {
                    var packet = await _channelAdapter.ReceivePacketAsync(TimeSpan.Zero, _cancellationToken.Token).ConfigureAwait(false);

                    if (packet == null)
                    {
                        // The client has closed the connection gracefully.
                        break;
                    }

                    Interlocked.Increment(ref _sentPacketsCount);
                    _lastPacketReceivedTimestamp = DateTime.UtcNow;

                    if (!(packet is MqttPingReqPacket || packet is MqttPingRespPacket))
                    {
                        _lastNonKeepAlivePacketReceivedTimestamp = _lastPacketReceivedTimestamp;
                    }

                    _keepAliveMonitor.PacketReceived();

                    if (packet is MqttPublishPacket publishPacket)
                    {
                        await HandleIncomingPublishPacketAsync(publishPacket).ConfigureAwait(false);

                        continue;
                    }

                    if (packet is MqttPubRelPacket pubRelPacket)
                    {
                        var pubCompPacket = new MqttPubCompPacket
                        {
                            PacketIdentifier = pubRelPacket.PacketIdentifier,
                            ReasonCode       = MqttPubCompReasonCode.Success
                        };

                        await SendAsync(pubCompPacket).ConfigureAwait(false);

                        continue;
                    }

                    if (packet is MqttSubscribePacket subscribePacket)
                    {
                        await HandleIncomingSubscribePacketAsync(subscribePacket).ConfigureAwait(false);

                        continue;
                    }

                    if (packet is MqttUnsubscribePacket unsubscribePacket)
                    {
                        await HandleIncomingUnsubscribePacketAsync(unsubscribePacket).ConfigureAwait(false);

                        continue;
                    }

                    if (packet is MqttPingReqPacket)
                    {
                        await SendAsync(new MqttPingRespPacket()).ConfigureAwait(false);

                        continue;
                    }

                    if (packet is MqttDisconnectPacket)
                    {
                        Session.WillMessage = null;
                        disconnectType      = MqttClientDisconnectType.Clean;

                        StopInternal();
                        break;
                    }

                    _packetDispatcher.Dispatch(packet);
                }
            }
            catch (OperationCanceledException)
            {
            }
            catch (Exception exception)
            {
                if (exception is MqttCommunicationException)
                {
                    _logger.Warning(exception, "Client '{0}': Communication exception while receiving client packets.", ClientId);
                }
                else
                {
                    _logger.Error(exception, "Client '{0}': Error while receiving client packets.", ClientId);
                }

                StopInternal();
            }
            finally
            {
                if (_isTakeover)
                {
                    disconnectType = MqttClientDisconnectType.Takeover;
                }

                if (Session.WillMessage != null)
                {
                    _sessionsManager.DispatchApplicationMessage(Session.WillMessage, this);
                    Session.WillMessage = null;
                }

                _packetDispatcher.Reset();

                _channelAdapter.ReadingPacketStartedCallback   = null;
                _channelAdapter.ReadingPacketCompletedCallback = null;

                _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);

                try
                {
                    await _sessionsManager.CleanUpClient(ClientId, _channelAdapter, disconnectType);
                }
                catch (Exception e)
                {
                    _logger.Error(e, "Client '{0}': Error while cleaning up", ClientId);
                }
            }
        }
        public async Task HandleClientConnectionAsync(IMqttChannelAdapter channelAdapter, CancellationToken cancellationToken)
        {
            MqttClient client = null;

            try
            {
                var connectPacket = await ReceiveConnectPacket(channelAdapter, cancellationToken).ConfigureAwait(false);

                if (connectPacket == null)
                {
                    // Nothing was received in time etc.
                    return;
                }

                var validatingConnectionEventArgs = await ValidateConnection(connectPacket, channelAdapter).ConfigureAwait(false);

                var connAckPacket = _packetFactories.ConnAck.Create(validatingConnectionEventArgs);

                if (validatingConnectionEventArgs.ReasonCode != MqttConnectReasonCode.Success)
                {
                    // Send failure response here without preparing a connection and session!
                    await channelAdapter.SendPacketAsync(connAckPacket, cancellationToken).ConfigureAwait(false);

                    return;
                }

                // Pass connAckPacket so that IsSessionPresent flag can be set if the client session already exists.
                client = await CreateClientConnection(connectPacket, connAckPacket, channelAdapter, validatingConnectionEventArgs).ConfigureAwait(false);

                await client.SendPacketAsync(connAckPacket, cancellationToken).ConfigureAwait(false);

                if (_eventContainer.ClientConnectedEvent.HasHandlers)
                {
                    var eventArgs = new ClientConnectedEventArgs
                    {
                        ClientId        = connectPacket.ClientId,
                        UserName        = connectPacket.Username,
                        ProtocolVersion = channelAdapter.PacketFormatterAdapter.ProtocolVersion,
                        Endpoint        = channelAdapter.Endpoint
                    };

                    await _eventContainer.ClientConnectedEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
                }

                await client.RunAsync().ConfigureAwait(false);
            }
            catch (OperationCanceledException)
            {
            }
            catch (Exception exception)
            {
                _logger.Error(exception, exception.Message);
            }
            finally
            {
                if (client != null)
                {
                    if (client.Id != null)
                    {
                        // in case it is a takeover _clientConnections already contains the new connection
                        if (!client.IsTakenOver)
                        {
                            lock (_clients)
                            {
                                _clients.Remove(client.Id);
                            }

                            if (!_options.EnablePersistentSessions || !client.Session.IsPersistent)
                            {
                                await DeleteSessionAsync(client.Id).ConfigureAwait(false);
                            }
                        }
                    }

                    var endpoint = client.Endpoint;

                    if (client.Id != null && !client.IsTakenOver && _eventContainer.ClientDisconnectedEvent.HasHandlers)
                    {
                        var eventArgs = new ClientDisconnectedEventArgs
                        {
                            ClientId       = client.Id,
                            DisconnectType = client.IsCleanDisconnect ? MqttClientDisconnectType.Clean : MqttClientDisconnectType.NotClean,
                            Endpoint       = endpoint
                        };

                        await _eventContainer.ClientDisconnectedEvent.InvokeAsync(eventArgs).ConfigureAwait(false);
                    }
                }

                using (var timeout = new CancellationTokenSource(_options.DefaultCommunicationTimeout))
                {
                    await channelAdapter.DisconnectAsync(timeout.Token).ConfigureAwait(false);
                }
            }
        }
        public async Task HandleClientConnectionAsync(IMqttChannelAdapter channelAdapter,
                                                      CancellationToken cancellationToken)
        {
            MqttClientConnection clientConnection = null;

            try
            {
                var connectPacket = await ReceiveConnectPacket(channelAdapter, cancellationToken).ConfigureAwait(false);

                if (connectPacket == null)
                {
                    // Nothing was received in time etc.
                    return;
                }

                MqttConnAckPacket connAckPacket;

                var connectionValidatorContext =
                    await ValidateConnection(connectPacket, channelAdapter).ConfigureAwait(false);

                if (connectionValidatorContext.ReasonCode != MqttConnectReasonCode.Success)
                {
                    // Send failure response here without preparing a session!
                    connAckPacket =
                        channelAdapter.PacketFormatterAdapter.DataConverter.CreateConnAckPacket(
                            connectionValidatorContext);
                    await channelAdapter.SendPacketAsync(connAckPacket, cancellationToken).ConfigureAwait(false);

                    return;
                }

                connAckPacket =
                    channelAdapter.PacketFormatterAdapter.DataConverter.CreateConnAckPacket(connectionValidatorContext);

                // Pass connAckPacket so that IsSessionPresent flag can be set if the client session already exists
                clientConnection = await CreateClientConnection(connectPacket, connAckPacket, channelAdapter,
                                                                connectionValidatorContext.SessionItems).ConfigureAwait(false);

                await channelAdapter.SendPacketAsync(connAckPacket, cancellationToken).ConfigureAwait(false);

                await _eventDispatcher.SafeNotifyClientConnectedAsync(connectPacket, channelAdapter)
                .ConfigureAwait(false);

                await clientConnection.RunAsync().ConfigureAwait(false);
            }
            catch (OperationCanceledException)
            {
            }
            catch (Exception exception)
            {
                _logger.Error(exception, exception.Message);
            }
            finally
            {
                if (clientConnection != null)
                {
                    if (clientConnection.ClientId != null)
                    {
                        // in case it is a takeover _clientConnections already contains the new connection
                        if (!clientConnection.IsTakenOver)
                        {
                            lock (_clientConnections)
                            {
                                _clientConnections.Remove(clientConnection.ClientId);
                            }

                            if (!_options.EnablePersistentSessions)
                            {
                                await DeleteSessionAsync(clientConnection.ClientId).ConfigureAwait(false);
                            }
                        }
                    }

                    var endpoint = clientConnection.Endpoint;

                    if (clientConnection.ClientId != null && !clientConnection.IsTakenOver)
                    {
                        // The event is fired at a separate place in case of a handover!
                        await _eventDispatcher.SafeNotifyClientDisconnectedAsync(
                            clientConnection.ClientId,
                            clientConnection.IsCleanDisconnect
                            ?MqttClientDisconnectType.Clean
                            : MqttClientDisconnectType.NotClean,
                            endpoint).ConfigureAwait(false);
                    }
                }

                clientConnection?.Dispose();

                await channelAdapter.DisconnectAsync(_options.DefaultCommunicationTimeout, CancellationToken.None)
                .ConfigureAwait(false);
            }
        }
        private async Task TrySendNextQueuedPacketAsync(IMqttChannelAdapter adapter, CancellationToken cancellationToken)
        {
            MqttBasePacket packet = null;

            try
            {
                lock (_queue)
                {
                    if (_queue.Count > 0)
                    {
                        packet = _queue.Dequeue();
                    }
                }

                if (packet == null)
                {
                    await _queueAutoResetEvent.WaitOneAsync(cancellationToken).ConfigureAwait(false);

                    return;
                }

                if (cancellationToken.IsCancellationRequested)
                {
                    return;
                }

                adapter.SendPacketAsync(packet, cancellationToken).GetAwaiter().GetResult();

                _logger.Verbose("Enqueued packet sent (ClientId: {0}).", _clientSession.ClientId);
            }
            catch (Exception exception)
            {
                if (exception is MqttCommunicationTimedOutException)
                {
                    _logger.Warning(exception, "Sending publish packet failed: Timeout (ClientId: {0}).", _clientSession.ClientId);
                }
                else if (exception is MqttCommunicationException)
                {
                    _logger.Warning(exception, "Sending publish packet failed: Communication exception (ClientId: {0}).", _clientSession.ClientId);
                }
                else if (exception is OperationCanceledException)
                {
                }
                else
                {
                    _logger.Error(exception, "Sending publish packet failed (ClientId: {0}).", _clientSession.ClientId);
                }

                if (packet is MqttPublishPacket publishPacket)
                {
                    if (publishPacket.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
                    {
                        publishPacket.Dup = true;

                        Enqueue(publishPacket);
                    }
                }

                if (!cancellationToken.IsCancellationRequested)
                {
                    _clientSession.Stop(MqttClientDisconnectType.NotClean);
                }
            }
        }