Task SendAsync(MqttBasePacket packet, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); _sendTracker.Restart(); return(_adapter.SendPacketAsync(packet, cancellationToken)); }
private Task SendAsync(MqttBasePacket packet, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { return(Task.FromResult(0)); } _sendTracker.Restart(); return(_adapter.SendPacketAsync(packet, Options.CommunicationTimeout, cancellationToken)); }
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); } }
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(); }
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); }
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; } }
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); } } }
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); }
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); } } }