public async Task ConnectAsync(IMqttClientOptions options, CancellationToken cancellationToken) { if (options is null) { throw new ArgumentNullException(nameof(options)); } if (_adapter != null) { throw new InvalidOperationException("Low level MQTT client is already connected. Disconnect first before connecting again."); } var newAdapter = _clientAdapterFactory.CreateClientAdapter(options); try { _logger.Verbose($"Trying to connect with server '{options.ChannelOptions}' (Timeout={options.CommunicationTimeout})."); await newAdapter.ConnectAsync(options.CommunicationTimeout, cancellationToken).ConfigureAwait(false); _logger.Verbose("Connection with server established."); _options = options; } catch (Exception) { _adapter?.Dispose(); throw; } _adapter = newAdapter; }
public MqttClientConnection(MqttConnectPacket connectPacket, IMqttChannelAdapter channelAdapter, MqttClientSession session, MqttConnectionValidatorContext connectionValidatorContext, IMqttServerOptions serverOptions, MqttClientSessionsManager sessionsManager, IMqttRetainedMessagesManager retainedMessagesManager, IMqttNetLogger logger) { Session = session ?? throw new ArgumentNullException(nameof(session)); _serverOptions = serverOptions ?? throw new ArgumentNullException(nameof(serverOptions)); _sessionsManager = sessionsManager ?? throw new ArgumentNullException(nameof(sessionsManager)); _retainedMessagesManager = retainedMessagesManager ?? throw new ArgumentNullException(nameof(retainedMessagesManager)); _channelAdapter = channelAdapter ?? throw new ArgumentNullException(nameof(channelAdapter)); _connectionValidatorContext = connectionValidatorContext ?? throw new ArgumentNullException(nameof(connectionValidatorContext)); _dataConverter = _channelAdapter.PacketFormatterAdapter.DataConverter; _endpoint = _channelAdapter.Endpoint; ConnectPacket = connectPacket ?? throw new ArgumentNullException(nameof(connectPacket)); if (logger == null) { throw new ArgumentNullException(nameof(logger)); } _logger = logger.CreateScopedLogger(nameof(MqttClientConnection)); _connectedTimestamp = DateTime.UtcNow; LastPacketReceivedTimestamp = _connectedTimestamp; _lastNonKeepAlivePacketReceivedTimestamp = LastPacketReceivedTimestamp; }
async Task <MqttConnectPacket> ReceiveConnectPacket(IMqttChannelAdapter channelAdapter, CancellationToken cancellationToken) { try { using (var timeoutToken = new CancellationTokenSource(_options.DefaultCommunicationTimeout)) using (var effectiveCancellationToken = CancellationTokenSource.CreateLinkedTokenSource(timeoutToken.Token, cancellationToken)) { var firstPacket = await channelAdapter.ReceivePacketAsync(effectiveCancellationToken.Token).ConfigureAwait(false); if (firstPacket is MqttConnectPacket connectPacket) { return(connectPacket); } } } catch (OperationCanceledException) { _logger.Warning("Client '{0}': Connected but did not sent a CONNECT packet.", channelAdapter.Endpoint); } catch (MqttCommunicationTimedOutException) { _logger.Warning("Client '{0}': Connected but did not sent a CONNECT packet.", channelAdapter.Endpoint); } _logger.Warning("Client '{0}': First received packet was no 'CONNECT' packet [MQTT-3.1.0-1].", channelAdapter.Endpoint); return(null); }
public MqttClient( MqttConnectPacket connectPacket, IMqttChannelAdapter channelAdapter, MqttSession session, MqttServerOptions serverOptions, MqttServerEventContainer eventContainer, MqttClientSessionsManager sessionsManager, IMqttNetLogger logger) { _serverOptions = serverOptions ?? throw new ArgumentNullException(nameof(serverOptions)); _eventContainer = eventContainer; _sessionsManager = sessionsManager ?? throw new ArgumentNullException(nameof(sessionsManager)); ChannelAdapter = channelAdapter ?? throw new ArgumentNullException(nameof(channelAdapter)); Endpoint = channelAdapter.Endpoint; Session = session ?? throw new ArgumentNullException(nameof(session)); _connectPacket = connectPacket ?? throw new ArgumentNullException(nameof(connectPacket)); if (logger == null) { throw new ArgumentNullException(nameof(logger)); } _logger = logger.WithSource(nameof(MqttClient)); }
async Task <MqttClientAuthenticateResult> AuthenticateAsync(IMqttChannelAdapter channelAdapter, MqttApplicationMessage willApplicationMessage, CancellationToken cancellationToken) { MqttClientAuthenticateResult result; try { var connectPacket = channelAdapter.PacketFormatterAdapter.DataConverter.CreateConnectPacket( willApplicationMessage, Options); var connAckPacket = await SendAndReceiveAsync <MqttConnAckPacket>(connectPacket, cancellationToken).ConfigureAwait(false); result = channelAdapter.PacketFormatterAdapter.DataConverter.CreateClientConnectResult(connAckPacket); } catch (Exception exception) { throw new MqttConnectingFailedException($"Error while authenticating. {exception.Message}", exception, null); } if (result.ResultCode != MqttClientConnectResultCode.Success) { throw new MqttConnectingFailedException($"Connecting with MQTT server failed ({result.ResultCode}).", null, result); } _logger.Verbose("Authenticated MQTT connection with server established."); return(result); }
public async Task <MqttClientConnectResult> ConnectAsync(IMqttClientOptions options) { if (options == null) { throw new ArgumentNullException(nameof(options)); } if (options.ChannelOptions == null) { throw new ArgumentException("ChannelOptions are not set."); } ThrowIfConnected("It is not allowed to connect with a server after the connection is established."); try { Options = options; _packetIdentifierProvider.Reset(); _packetDispatcher.Reset(); _cancellationTokenSource = new CancellationTokenSource(); _disconnectGate = 0; _adapter = _adapterFactory.CreateClientAdapter(options, _logger); _logger.Verbose($"Trying to connect with server ({Options.ChannelOptions})."); await _adapter.ConnectAsync(Options.CommunicationTimeout, _cancellationTokenSource.Token).ConfigureAwait(false); _logger.Verbose("Connection with server established."); StartReceivingPackets(_cancellationTokenSource.Token); var connectResponse = await AuthenticateAsync(options.WillMessage, _cancellationTokenSource.Token).ConfigureAwait(false); _logger.Verbose("MQTT connection with server established."); _sendTracker.Restart(); if (Options.KeepAlivePeriod != TimeSpan.Zero) { StartSendingKeepAliveMessages(_cancellationTokenSource.Token); } IsConnected = true; Connected?.Invoke(this, new MqttClientConnectedEventArgs(connectResponse.IsSessionPresent)); _logger.Info("Connected."); return(new MqttClientConnectResult(connectResponse.IsSessionPresent)); } catch (Exception exception) { _logger.Error(exception, "Error while connecting with server."); if (!DisconnectIsPending()) { await DisconnectInternalAsync(null, exception).ConfigureAwait(false); } throw; } }
private Task HandleIncomingPublishPacketAsync(IMqttChannelAdapter adapter, MqttPublishPacket publishPacket, CancellationToken cancellationToken) { var applicationMessage = publishPacket.ToApplicationMessage(); switch (applicationMessage.QualityOfServiceLevel) { case MqttQualityOfServiceLevel.AtMostOnce: { return(ApplicationMessageReceivedCallback?.Invoke(this, applicationMessage)); } case MqttQualityOfServiceLevel.AtLeastOnce: { return(HandleIncomingPublishPacketWithQoS1(adapter, applicationMessage, publishPacket, cancellationToken)); } case MqttQualityOfServiceLevel.ExactlyOnce: { return(HandleIncomingPublishPacketWithQoS2(adapter, applicationMessage, publishPacket, cancellationToken)); } default: { throw new MqttCommunicationException("Received a not supported QoS level."); } } }
private void HandleIncomingPublishPacket(IMqttChannelAdapter adapter, MqttPublishPacket publishPacket, CancellationToken cancellationToken) { switch (publishPacket.QualityOfServiceLevel) { case MqttQualityOfServiceLevel.AtMostOnce: { HandleIncomingPublishPacketWithQoS0(publishPacket); break; } case MqttQualityOfServiceLevel.AtLeastOnce: { HandleIncomingPublishPacketWithQoS1(adapter, publishPacket, cancellationToken); break; } case MqttQualityOfServiceLevel.ExactlyOnce: { HandleIncomingPublishPacketWithQoS2(adapter, publishPacket, cancellationToken); break; } default: { throw new MqttCommunicationException("Received a not supported QoS level."); } } }
private async Task ReceivePacketsAsync(IMqttChannelAdapter adapter, CancellationToken cancellationToken) { try { while (!cancellationToken.IsCancellationRequested) { var packet = await adapter.ReceivePacketAsync(TimeSpan.Zero, cancellationToken).ConfigureAwait(false); KeepAliveMonitor.PacketReceived(packet); await ProcessReceivedPacketAsync(adapter, packet, cancellationToken).ConfigureAwait(false); } } catch (OperationCanceledException) { } catch (MqttCommunicationException exception) { _logger.Warning <MqttClientSession>(exception, "Client '{0}': Communication exception while processing client packets.", ClientId); await StopAsync().ConfigureAwait(false); } catch (Exception exception) { _logger.Error <MqttClientSession>(exception, "Client '{0}': Unhandled exception while processing client packets.", ClientId); await StopAsync().ConfigureAwait(false); } }
public async Task StopAsync() { try { if (_cancellationTokenSource == null) { return; } _cancellationTokenSource?.Cancel(false); _cancellationTokenSource?.Dispose(); _cancellationTokenSource = null; if (_adapter != null) { await _adapter.DisconnectAsync(_options.DefaultCommunicationTimeout).ConfigureAwait(false); _adapter = null; } _logger.Info <MqttClientSession>("Client '{0}': Session stopped.", ClientId); } finally { var willMessage = _willMessage; if (willMessage != null) { _willMessage = null; // clear willmessage so it is send just once await ApplicationMessageReceivedCallback(this, willMessage).ConfigureAwait(false); } } }
public MqttClientConnection( MqttConnectPacket connectPacket, IMqttChannelAdapter channelAdapter, MqttClientSession session, IMqttServerOptions serverOptions, MqttClientSessionsManager sessionsManager, IMqttRetainedMessagesManager retainedMessagesManager, Func <Task> onStart, Func <MqttClientDisconnectType, Task> onStop, IMqttNetLogger logger) { Session = session ?? throw new ArgumentNullException(nameof(session)); _serverOptions = serverOptions ?? throw new ArgumentNullException(nameof(serverOptions)); _sessionsManager = sessionsManager ?? throw new ArgumentNullException(nameof(sessionsManager)); _retainedMessagesManager = retainedMessagesManager ?? throw new ArgumentNullException(nameof(retainedMessagesManager)); _onStart = onStart ?? throw new ArgumentNullException(nameof(onStart)); _onStop = onStop ?? throw new ArgumentNullException(nameof(onStop)); _channelAdapter = channelAdapter ?? throw new ArgumentNullException(nameof(channelAdapter)); _dataConverter = _channelAdapter.PacketFormatterAdapter.DataConverter; _endpoint = _channelAdapter.Endpoint; ConnectPacket = connectPacket ?? throw new ArgumentNullException(nameof(connectPacket)); if (logger == null) { throw new ArgumentNullException(nameof(logger)); } _logger = logger.CreateScopedLogger(nameof(MqttClientConnection)); _keepAliveMonitor = new MqttClientKeepAliveMonitor(ConnectPacket.ClientId, () => StopAsync(), logger); _connectedTimestamp = DateTime.UtcNow; _lastPacketReceivedTimestamp = _connectedTimestamp; _lastNonKeepAlivePacketReceivedTimestamp = _lastPacketReceivedTimestamp; }
public async Task CleanUpClient(string clientId, IMqttChannelAdapter channelAdapter, MqttClientDisconnectType disconnectType) { if (clientId != null) { // in case it is a takeover _connections already contains the new connection if (disconnectType != MqttClientDisconnectType.Takeover) { lock (_connections) { _connections.Remove(clientId); } if (!_options.EnablePersistentSessions) { await DeleteSessionAsync(clientId).ConfigureAwait(false); } } } await SafeCleanupChannelAsync(channelAdapter).ConfigureAwait(false); if (clientId != null) { await _eventDispatcher.SafeNotifyClientDisconnectedAsync(clientId, disconnectType).ConfigureAwait(false); } }
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); } }
private Task HandleIncomingPubRelPacketAsync(IMqttChannelAdapter adapter, MqttPubRelPacket pubRelPacket, CancellationToken cancellationToken) { var response = new MqttPubCompPacket { PacketIdentifier = pubRelPacket.PacketIdentifier }; return(adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, response)); }
async Task <MqttClientConnection> CreateClientConnection( MqttConnectPacket connectPacket, MqttConnAckPacket connAckPacket, IMqttChannelAdapter channelAdapter, IDictionary <object, object> sessionItems) { MqttClientConnection connection; using (await _createConnectionSyncRoot.WaitAsync(CancellationToken.None).ConfigureAwait(false)) { MqttClientSession session; lock (_clientSessions) { if (!_clientSessions.TryGetValue(connectPacket.ClientId, out session)) { _logger.Verbose("Created a new session for client '{0}'.", connectPacket.ClientId); session = CreateSession(connectPacket.ClientId, sessionItems); } else { if (connectPacket.CleanSession) { _logger.Verbose("Deleting existing session of client '{0}'.", connectPacket.ClientId); session = CreateSession(connectPacket.ClientId, sessionItems); } else { _logger.Verbose("Reusing existing session of client '{0}'.", connectPacket.ClientId); connAckPacket.IsSessionPresent = true; } } _clientSessions[connectPacket.ClientId] = session; } MqttClientConnection existingConnection; lock (_clientConnections) { _clientConnections.TryGetValue(connectPacket.ClientId, out existingConnection); connection = CreateConnection(connectPacket, channelAdapter, session); _clientConnections[connectPacket.ClientId] = connection; } if (existingConnection != null) { await _eventDispatcher.SafeNotifyClientDisconnectedAsync(existingConnection.ClientId, MqttClientDisconnectType.Takeover, existingConnection.Endpoint); existingConnection.IsTakenOver = true; await existingConnection.StopAsync(MqttClientDisconnectReason.SessionTakenOver) .ConfigureAwait(false); } } return(connection); }
private void Cleanup() { _backgroundCancellationTokenSource?.Cancel(false); _backgroundCancellationTokenSource?.Dispose(); _backgroundCancellationTokenSource = null; _adapter?.Dispose(); _adapter = null; }
public Task HandleClientConnectionAsync(IMqttChannelAdapter clientAdapter) { if (clientAdapter is null) { throw new ArgumentNullException(nameof(clientAdapter)); } return(HandleClientConnectionAsync(clientAdapter, _cancellationToken)); }
public void Dispose() { _cancellationTokenSource?.Cancel(false); _cancellationTokenSource?.Dispose(); _cancellationTokenSource = null; _adapter?.Dispose(); _adapter = null; }
public static Task SendPacketsAsync(this IMqttChannelAdapter adapter, TimeSpan timeout, CancellationToken cancellationToken, params MqttBasePacket[] packets) { if (adapter == null) { throw new ArgumentNullException(nameof(adapter)); } return(adapter.SendPacketsAsync(timeout, cancellationToken, packets)); }
private async Task HandleIncomingPublishPacketWithQoS1(IMqttChannelAdapter adapter, MqttApplicationMessage applicationMessage, MqttPublishPacket publishPacket, CancellationToken cancellationToken) { await _sessionsManager.DispatchApplicationMessageAsync(this, applicationMessage).ConfigureAwait(false); var response = new MqttPubAckPacket { PacketIdentifier = publishPacket.PacketIdentifier }; await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, response).ConfigureAwait(false); }
private async Task SendNextQueuedPacketAsync(IMqttChannelAdapter adapter, CancellationToken cancellationToken) { MqttBasePacket packet = null; try { await _queueWaitSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); if (!_queue.TryDequeue(out packet)) { throw new InvalidOperationException(); // should not happen } if (cancellationToken.IsCancellationRequested) { return; } await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new[] { packet }).ConfigureAwait(false); _logger.Trace <MqttClientPendingMessagesQueue>("Enqueued packet sent (ClientId: {0}).", _clientSession.ClientId); } catch (Exception exception) { if (exception is MqttCommunicationTimedOutException) { _logger.Warning <MqttClientPendingMessagesQueue>(exception, "Sending publish packet failed due to timeout (ClientId: {0}).", _clientSession.ClientId); } else if (exception is MqttCommunicationException) { _logger.Warning <MqttClientPendingMessagesQueue>(exception, "Sending publish packet failed due to communication exception (ClientId: {0}).", _clientSession.ClientId); } else if (exception is OperationCanceledException) { } else { _logger.Error <MqttClientPendingMessagesQueue>(exception, "Sending publish packet failed (ClientId: {0}).", _clientSession.ClientId); } if (packet is MqttPublishPacket publishPacket) { if (publishPacket.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce) { publishPacket.Dup = true; _queue.Enqueue(packet); _queueWaitSemaphore.Release(); } } if (!cancellationToken.IsCancellationRequested) { await _clientSession.StopAsync().ConfigureAwait(false); } } }
private Task ProcessReceivedPacketAsync(IMqttChannelAdapter adapter, MqttBasePacket packet, CancellationToken cancellationToken) { if (packet is MqttPublishPacket publishPacket) { return(HandleIncomingPublishPacketAsync(adapter, publishPacket, cancellationToken)); } if (packet is MqttPingReqPacket) { return(adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new[] { new MqttPingRespPacket() })); } if (packet is MqttPubRelPacket pubRelPacket) { return(HandleIncomingPubRelPacketAsync(adapter, pubRelPacket, cancellationToken)); } if (packet is MqttPubRecPacket pubRecPacket) { var responsePacket = new MqttPubRelPacket { PacketIdentifier = pubRecPacket.PacketIdentifier }; return(adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new[] { responsePacket })); } if (packet is MqttPubAckPacket || packet is MqttPubCompPacket) { // Discard message. return(Task.FromResult(0)); } if (packet is MqttSubscribePacket subscribePacket) { return(HandleIncomingSubscribePacketAsync(adapter, subscribePacket, cancellationToken)); } if (packet is MqttUnsubscribePacket unsubscribePacket) { return(HandleIncomingUnsubscribePacketAsync(adapter, unsubscribePacket, cancellationToken)); } if (packet is MqttDisconnectPacket) { return(StopAsync(true)); } if (packet is MqttConnectPacket) { return(StopAsync()); } _logger.Warning <MqttClientSession>("Client '{0}': Received not supported packet ({1}). Closing connection.", ClientId, packet); return(StopAsync()); }
private async Task HandleIncomingPublishPacketWithQoS2(IMqttChannelAdapter adapter, MqttApplicationMessage applicationMessage, MqttPublishPacket publishPacket, CancellationToken cancellationToken) { // QoS 2 is implement as method "B" [4.3.3 QoS 2: Exactly once delivery] await _sessionsManager.DispatchApplicationMessageAsync(this, applicationMessage).ConfigureAwait(false); var response = new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier }; await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, response).ConfigureAwait(false); }
async Task SafeCleanupChannelAsync(IMqttChannelAdapter channelAdapter) { try { await channelAdapter.DisconnectAsync(_options.DefaultCommunicationTimeout, CancellationToken.None).ConfigureAwait(false); } catch (Exception exception) { _logger.Error(exception, "Error while disconnecting client channel."); } }
Task OnClientAcceptedAsync(IMqttChannelAdapter channelAdapter) { var clientHandler = ClientHandler; if (clientHandler == null) { return(Task.FromResult(0)); } return(clientHandler(channelAdapter)); }
MqttClientConnection CreateConnection(MqttConnectPacket connectPacket, IMqttChannelAdapter channelAdapter, MqttClientSession session) { return(new MqttClientConnection( connectPacket, channelAdapter, session, _options, this, _rootLogger)); }
private Task HandleIncomingPubRelPacketAsync(IMqttChannelAdapter adapter, MqttPubRelPacket pubRelPacket, CancellationToken cancellationToken) { lock (_unacknowledgedPublishPackets) { _unacknowledgedPublishPackets.Remove(pubRelPacket.PacketIdentifier); } return(adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new MqttPubCompPacket { PacketIdentifier = pubRelPacket.PacketIdentifier })); }
public async Task DisconnectAsync(CancellationToken cancellationToken) { if (_adapter == null) { return; } await SafeDisconnect(cancellationToken).ConfigureAwait(false); _adapter = null; }
MqttClientConnection CreateConnection(MqttConnectPacket connectPacket, IMqttChannelAdapter channelAdapter, MqttClientSession session, MqttConnectionValidatorContext connectionValidatorContext) { return(new MqttClientConnection( connectPacket, channelAdapter, session, connectionValidatorContext, _options, this, _retainedMessagesManager, _rootLogger)); }
private async Task HandleIncomingSubscribePacketAsync(IMqttChannelAdapter adapter, MqttSubscribePacket subscribePacket, CancellationToken cancellationToken) { var subscribeResult = await SubscriptionsManager.SubscribeAsync(subscribePacket).ConfigureAwait(false); await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new[] { subscribeResult.ResponsePacket }).ConfigureAwait(false); if (subscribeResult.CloseConnection) { await StopAsync().ConfigureAwait(false); } await EnqueueSubscribedRetainedMessagesAsync(subscribePacket.TopicFilters).ConfigureAwait(false); }