async Task RunInternalAsync(CancellationToken cancellationToken) { var disconnectType = MqttClientDisconnectType.NotClean; try { _logger.Info("Client '{0}': Session started.", ClientId); Session.WillMessage = ConnectPacket.WillMessage; await SendAsync(_channelAdapter.PacketFormatterAdapter.DataConverter.CreateConnAckPacket(_connectionValidatorContext), cancellationToken).ConfigureAwait(false); Task.Run(() => SendPendingPacketsAsync(cancellationToken), cancellationToken).Forget(_logger); Session.IsCleanSession = false; while (!cancellationToken.IsCancellationRequested) { Status = MqttClientConnectionStatus.Running; var packet = await _channelAdapter.ReceivePacketAsync(cancellationToken).ConfigureAwait(false); if (packet == null) { // The client has closed the connection gracefully. return; } Interlocked.Increment(ref _sentPacketsCount); LastPacketReceivedTimestamp = DateTime.UtcNow; if (!(packet is MqttPingReqPacket || packet is MqttPingRespPacket)) { _lastNonKeepAlivePacketReceivedTimestamp = LastPacketReceivedTimestamp; } if (packet is MqttPublishPacket publishPacket) { await HandleIncomingPublishPacketAsync(publishPacket, cancellationToken).ConfigureAwait(false); } else if (packet is MqttPubRelPacket pubRelPacket) { await HandleIncomingPubRelPacketAsync(pubRelPacket, cancellationToken).ConfigureAwait(false); } else if (packet is MqttSubscribePacket subscribePacket) { await HandleIncomingSubscribePacketAsync(subscribePacket, cancellationToken).ConfigureAwait(false); } else if (packet is MqttUnsubscribePacket unsubscribePacket) { await HandleIncomingUnsubscribePacketAsync(unsubscribePacket, cancellationToken).ConfigureAwait(false); } else if (packet is MqttPingReqPacket) { await SendAsync(MqttPingRespPacket.Instance, cancellationToken).ConfigureAwait(false); } else if (packet is MqttDisconnectPacket) { Session.WillMessage = null; disconnectType = MqttClientDisconnectType.Clean; StopInternal(); return; } else { _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 (_disconnectReason == MqttDisconnectReasonCode.SessionTakenOver) { disconnectType = MqttClientDisconnectType.Takeover; } if (Session.WillMessage != null) { _sessionsManager.DispatchApplicationMessage(Session.WillMessage, this); Session.WillMessage = null; } _packetDispatcher.Cancel(); _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); } } }
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); } } }