public async Task SendPacketAsync(MqttBasePacket packet, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); ThrowIfDisposed(); using (await _syncRoot.WaitAsync(cancellationToken).ConfigureAwait(false)) { // Check for cancellation here again because "WaitAsync" might take some time. cancellationToken.ThrowIfCancellationRequested(); try { var packetData = PacketFormatterAdapter.Encode(packet); await _channel.WriteAsync(packetData.Array, packetData.Offset, packetData.Count, cancellationToken).ConfigureAwait(false); Interlocked.Add(ref _bytesReceived, packetData.Count); _logger.Verbose("TX ({0} bytes) >>> {1}", packetData.Count, packet); } catch (Exception exception) { if (IsWrappedException(exception)) { throw; } WrapAndThrowException(exception); } finally { PacketFormatterAdapter.FreeBuffer(); } } }
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 void Log_10000_Messages_NoHandler() { _useHandler = false; for (var i = 0; i < 10000; i++) { _childLogger.Verbose("test log message {0}", "parameter"); } }
public async Task SendPacketAsync(MqttBasePacket packet, TimeSpan timeout, CancellationToken cancellationToken) { ThrowIfDisposed(); cancellationToken.ThrowIfCancellationRequested(); try { await _writerSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); } catch (ObjectDisposedException) { throw new OperationCanceledException(); } try { var packetData = PacketFormatterAdapter.Encode(packet); if (timeout == TimeSpan.Zero) { await _channel.WriteAsync(packetData.Array, packetData.Offset, packetData.Count, cancellationToken).ConfigureAwait(false); } else { await MqttTaskTimeout.WaitAsync( t => _channel.WriteAsync(packetData.Array, packetData.Offset, packetData.Count, t), timeout, cancellationToken).ConfigureAwait(false); } Interlocked.Add(ref _bytesReceived, packetData.Count); _logger.Verbose("TX ({0} bytes) >>> {1}", packetData.Count, packet); } catch (Exception exception) { if (IsWrappedException(exception)) { throw; } WrapException(exception); } finally { PacketFormatterAdapter.FreeBuffer(); try { _writerSemaphore.Release(); } catch (ObjectDisposedException) { throw new OperationCanceledException(); } } }
public async Task HandleMessageAsync(string clientId, MqttApplicationMessage applicationMessage) { if (applicationMessage == null) { throw new ArgumentNullException(nameof(applicationMessage)); } try { using (await _messagesLock.WaitAsync().ConfigureAwait(false)) { var saveIsRequired = false; var hasPayload = applicationMessage.Payload != null && applicationMessage.Payload.Length > 0; if (!hasPayload) { saveIsRequired = _messages.Remove(applicationMessage.Topic); _logger.Verbose("Client '{0}' cleared retained message for topic '{1}'.", clientId, applicationMessage.Topic); } else { if (!_messages.TryGetValue(applicationMessage.Topic, out var existingMessage)) { _messages[applicationMessage.Topic] = applicationMessage; saveIsRequired = true; } else { if (existingMessage.QualityOfServiceLevel != applicationMessage.QualityOfServiceLevel || !existingMessage.Payload.SequenceEqual(applicationMessage.Payload ?? _emptyArray)) { _messages[applicationMessage.Topic] = applicationMessage; saveIsRequired = true; } } _logger.Verbose("Client '{0}' set retained message for topic '{1}'.", clientId, applicationMessage.Topic); } if (saveIsRequired) { if (_options.Storage != null) { var messagesForSave = new List <MqttApplicationMessage>(_messages.Values); await _options.Storage.SaveRetainedMessagesAsync(messagesForSave).ConfigureAwait(false); } } } } catch (Exception exception) { _logger.Error(exception, "Unhandled exception while handling retained messages."); } }
public async Task DeleteSessionAsync(string clientId) { if (_connections.TryGetValue(clientId, out var connection)) { await connection.StopAsync().ConfigureAwait(false); } if (_sessions.TryRemove(clientId, out _)) { } _logger.Verbose("Session for client '{0}' deleted.", clientId); }
public async Task DeleteSessionAsync(string clientId) { MqttClientConnection connection; lock (_connections) { _connections.TryGetValue(clientId, out connection); } lock (_sessions) { _sessions.Remove(clientId); } if (connection != null) { await connection.StopAsync(MqttClientDisconnectReason.NormalDisconnection).ConfigureAwait(false); } _logger.Verbose("Session for client '{0}' deleted.", clientId); }
public void EnqueueApplicationMessage(MqttApplicationMessage applicationMessage, string senderClientId, bool isRetainedApplicationMessage) { var checkSubscriptionsResult = SubscriptionsManager.CheckSubscriptions(applicationMessage.Topic, applicationMessage.QualityOfServiceLevel); if (!checkSubscriptionsResult.IsSubscribed) { return; } _logger.Verbose("Queued application message with topic '{0}' (ClientId: {1}).", applicationMessage.Topic, ClientId); ApplicationMessagesQueue.Enqueue(applicationMessage, senderClientId, checkSubscriptionsResult.QualityOfServiceLevel, isRetainedApplicationMessage); }
async Task RunAsync(int keepAlivePeriod, CancellationToken cancellationToken) { try { _lastPacketReceivedTracker.Restart(); while (!cancellationToken.IsCancellationRequested) { // Values described here: [MQTT-3.1.2-24]. // If the client sends 5 sec. the server will allow up to 7.5 seconds. // If the client sends 1 sec. the server will allow up to 1.5 seconds. if (!_isPaused && _lastPacketReceivedTracker.Elapsed.TotalSeconds >= keepAlivePeriod * 1.5D) { _logger.Warning(null, "Client '{0}': Did not receive any packet or keep alive signal.", _clientId); await _keepAliveElapsedCallback().ConfigureAwait(false); return; } // The server checks the keep alive timeout every 50 % of the overall keep alive timeout // because the server allows 1.5 times the keep alive value. This means that a value of 5 allows // up to 7.5 seconds. With an interval of 2.5 (5 / 2) the 7.5 is also affected. Waiting the whole // keep alive time will hit at 10 instead of 7.5 (but only one time instead of two times). await TaskExtension.Delay(TimeSpan.FromSeconds(keepAlivePeriod * 0.5D), cancellationToken).ConfigureAwait(false); } } catch (OperationCanceledException) { } catch (Exception exception) { _logger.Error(exception, "Client '{0}': Unhandled exception while checking keep alive timeouts.", _clientId); } finally { _logger.Verbose("Client '{0}': Stopped checking keep alive timeout.", _clientId); } }
void DoWork(CancellationToken cancellationToken) { try { _logger.Info("Starting keep alive monitor."); while (!cancellationToken.IsCancellationRequested) { TryMaintainConnections(); PlatformAbstractionLayer.Sleep(_options.KeepAliveMonitorInterval); } } catch (OperationCanceledException) { } catch (Exception exception) { _logger.Error(exception, "Unhandled exception while checking keep alive timeouts."); } finally { _logger.Verbose("Stopped checking keep alive timeout."); } }
public async Task <MqttClientAuthenticateResult> ConnectAsync(IMqttClientOptions options, CancellationToken cancellationToken) { 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."); ThrowIfDisposed(); MqttClientAuthenticateResult authenticateResult = null; try { Options = options; _packetIdentifierProvider.Reset(); _packetDispatcher.Reset(); _backgroundCancellationTokenSource = new CancellationTokenSource(); var backgroundCancellationToken = _backgroundCancellationTokenSource.Token; _isDisconnectPending = 0; var adapter = _adapterFactory.CreateClientAdapter(options); _adapter = adapter; using (var combined = CancellationTokenSource.CreateLinkedTokenSource(backgroundCancellationToken, cancellationToken)) { _logger.Verbose($"Trying to connect with server '{options.ChannelOptions}' (Timeout={options.CommunicationTimeout})."); await _adapter.ConnectAsync(options.CommunicationTimeout, combined.Token).ConfigureAwait(false); _logger.Verbose("Connection with server established."); _publishPacketReceiverQueue = new AsyncQueue <MqttPublishPacket>(); _publishPacketReceiverTask = Task.Run(() => ProcessReceivedPublishPackets(backgroundCancellationToken), backgroundCancellationToken); _packetReceiverTask = Task.Run(() => TryReceivePacketsAsync(backgroundCancellationToken), backgroundCancellationToken); authenticateResult = await AuthenticateAsync(adapter, options.WillMessage, combined.Token).ConfigureAwait(false); } _sendTracker.Restart(); _receiveTracker.Restart(); if (Options.KeepAlivePeriod != TimeSpan.Zero) { _keepAlivePacketsSenderTask = Task.Run(() => TrySendKeepAliveMessagesAsync(backgroundCancellationToken), backgroundCancellationToken); } _isConnected = true; _logger.Info("Connected."); var connectedHandler = ConnectedHandler; if (connectedHandler != null) { await connectedHandler.HandleConnectedAsync(new MqttClientConnectedEventArgs(authenticateResult)).ConfigureAwait(false); } return(authenticateResult); } catch (Exception exception) { _logger.Error(exception, "Error while connecting with server."); if (!DisconnectIsPending()) { await DisconnectInternalAsync(null, exception, authenticateResult).ConfigureAwait(false); } throw; } }
async Task TryHandleClientConnectionAsync(CrossPlatformSocket clientSocket) { Stream stream = null; string remoteEndPoint = null; try { remoteEndPoint = clientSocket.RemoteEndPoint.ToString(); _logger.Verbose("Client '{0}' accepted by TCP listener '{1}, {2}'.", remoteEndPoint, _localEndPoint, _addressFamily == AddressFamily.InterNetwork ? "ipv4" : "ipv6"); clientSocket.NoDelay = _options.NoDelay; stream = clientSocket.GetStream(); X509Certificate2 clientCertificate = null; if (_tlsCertificate != null) { var sslStream = new SslStream(stream, false, _tlsOptions.RemoteCertificateValidationCallback); await sslStream.AuthenticateAsServerAsync( _tlsCertificate, _tlsOptions.ClientCertificateRequired, _tlsOptions.SslProtocol, _tlsOptions.CheckCertificateRevocation).ConfigureAwait(false); stream = sslStream; clientCertificate = sslStream.RemoteCertificate as X509Certificate2; if (clientCertificate == null && sslStream.RemoteCertificate != null) { clientCertificate = new X509Certificate2(sslStream.RemoteCertificate.Export(X509ContentType.Cert)); } } var clientHandler = ClientHandler; if (clientHandler != null) { using (var clientAdapter = new MqttChannelAdapter( new MqttTcpChannel(stream, remoteEndPoint, clientCertificate), new MqttPacketFormatterAdapter(new MqttPacketWriter()), null, _rootLogger)) { await clientHandler(clientAdapter).ConfigureAwait(false); } } } catch (Exception exception) { if (exception is ObjectDisposedException) { // It can happen that the listener socket is accessed after the cancellation token is already set and the listener socket is disposed. return; } if (exception is SocketException socketException && socketException.SocketErrorCode == SocketError.OperationAborted) { return; } _logger.Error(exception, "Error while handling client connection."); } finally { try { stream?.Dispose(); clientSocket?.Dispose(); } catch (Exception disposeException) { _logger.Error(disposeException, "Error while cleaning up client connection"); } } _logger.Verbose("Client '{0}' disconnected at TCP listener '{1}, {2}'.", remoteEndPoint, _localEndPoint, _addressFamily == AddressFamily.InterNetwork ? "ipv4" : "ipv6"); }
async Task SendPendingPacketsAsync(CancellationToken cancellationToken) { MqttQueuedApplicationMessage queuedApplicationMessage = null; MqttPublishPacket publishPacket = null; try { while (!cancellationToken.IsCancellationRequested) { queuedApplicationMessage = await Session.ApplicationMessagesQueue.DequeueAsync(cancellationToken).ConfigureAwait(false); if (queuedApplicationMessage == null) { return; } if (cancellationToken.IsCancellationRequested) { return; } publishPacket = _dataConverter.CreatePublishPacket(queuedApplicationMessage.ApplicationMessage); publishPacket.QualityOfServiceLevel = queuedApplicationMessage.SubscriptionQualityOfServiceLevel; // Set the retain flag to true according to [MQTT-3.3.1-8] and [MQTT-3.3.1-9]. publishPacket.Retain = queuedApplicationMessage.IsRetainedMessage; if (_serverOptions.ClientMessageQueueInterceptor != null) { var context = new MqttClientMessageQueueInterceptorContext( queuedApplicationMessage.SenderClientId, ClientId, queuedApplicationMessage.ApplicationMessage, queuedApplicationMessage.SubscriptionQualityOfServiceLevel); if (_serverOptions.ClientMessageQueueInterceptor != null) { await _serverOptions.ClientMessageQueueInterceptor.InterceptClientMessageQueueEnqueueAsync(context).ConfigureAwait(false); } if (!context.AcceptEnqueue || context.ApplicationMessage == null) { return; } publishPacket.Topic = context.ApplicationMessage.Topic; publishPacket.Payload = context.ApplicationMessage.Payload; publishPacket.QualityOfServiceLevel = context.SubscriptionQualityOfServiceLevel; } if (publishPacket.QualityOfServiceLevel > 0) { publishPacket.PacketIdentifier = _packetIdentifierProvider.GetNextPacketIdentifier(); } if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce) { await SendAsync(publishPacket, cancellationToken).ConfigureAwait(false); } else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce) { var awaiter = _packetDispatcher.AddAwaiter <MqttPubAckPacket>(publishPacket.PacketIdentifier); await SendAsync(publishPacket, cancellationToken).ConfigureAwait(false); await awaiter.WaitOneAsync(_serverOptions.DefaultCommunicationTimeout).ConfigureAwait(false); } else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce) { using (var awaiter1 = _packetDispatcher.AddAwaiter <MqttPubRecPacket>(publishPacket.PacketIdentifier)) using (var awaiter2 = _packetDispatcher.AddAwaiter <MqttPubCompPacket>(publishPacket.PacketIdentifier)) { await SendAsync(publishPacket, cancellationToken).ConfigureAwait(false); await awaiter1.WaitOneAsync(_serverOptions.DefaultCommunicationTimeout).ConfigureAwait(false); await SendAsync(new MqttPubRelPacket { PacketIdentifier = publishPacket.PacketIdentifier, ReasonCode = MqttPubRelReasonCode.Success }, cancellationToken).ConfigureAwait(false); await awaiter2.WaitOneAsync(_serverOptions.DefaultCommunicationTimeout).ConfigureAwait(false); } } _logger.Verbose("Client '{0}': Queued application message sent.", ClientId); } } catch (Exception exception) { if (exception is OperationCanceledException) { } else if (exception is MqttCommunicationTimedOutException) { _logger.Warning(exception, "Client '{0}': Sending publish packet failed: Timeout.", ClientId); } else if (exception is MqttCommunicationException) { _logger.Warning(exception, "Client '{0}': Sending publish packet failed: Communication exception.", ClientId); } else { _logger.Error(exception, "Client '{0}': Sending publish packet failed.", ClientId); } if (publishPacket?.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce) { queuedApplicationMessage.IsDuplicate = true; Session.ApplicationMessagesQueue.Enqueue(queuedApplicationMessage); } StopInternal(); } }
public async Task <MqttClientAuthenticateResult> ConnectAsync(IMqttClientOptions options, CancellationToken cancellationToken) { 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."); ThrowIfDisposed(); if (CompareExchangeConnectionStatus(MqttClientConnectionStatus.Connecting, MqttClientConnectionStatus.Disconnected) != MqttClientConnectionStatus.Disconnected) { throw new InvalidOperationException("Not allowed to connect while connect/disconnect is pending."); } MqttClientAuthenticateResult authenticateResult = null; try { Options = options; _packetIdentifierProvider.Reset(); _packetDispatcher.CancelAll(); _backgroundCancellationTokenSource = new CancellationTokenSource(); var backgroundCancellationToken = _backgroundCancellationTokenSource.Token; var adapter = _adapterFactory.CreateClientAdapter(options); _adapter = adapter; using (var combined = CancellationTokenSource.CreateLinkedTokenSource(backgroundCancellationToken, cancellationToken)) { _logger.Verbose("Trying to connect with server '{0}' (Timeout={1}).", options.ChannelOptions, options.CommunicationTimeout); await adapter.ConnectAsync(options.CommunicationTimeout, combined.Token).ConfigureAwait(false); _logger.Verbose("Connection with server established."); _publishPacketReceiverQueue = new AsyncQueue <MqttPublishPacket>(); _publishPacketReceiverTask = Task.Run(() => ProcessReceivedPublishPackets(backgroundCancellationToken), backgroundCancellationToken); _packetReceiverTask = Task.Run(() => TryReceivePacketsAsync(backgroundCancellationToken), backgroundCancellationToken); authenticateResult = await AuthenticateAsync(adapter, options.WillMessage, combined.Token).ConfigureAwait(false); } _lastPacketSentTimestamp = DateTime.UtcNow; if (Options.KeepAlivePeriod != TimeSpan.Zero) { _keepAlivePacketsSenderTask = Task.Run(() => TrySendKeepAliveMessagesAsync(backgroundCancellationToken), backgroundCancellationToken); } CompareExchangeConnectionStatus(MqttClientConnectionStatus.Connected, MqttClientConnectionStatus.Connecting); _logger.Info("Connected."); var connectedHandler = ConnectedHandler; if (connectedHandler != null) { await connectedHandler.HandleConnectedAsync(new MqttClientConnectedEventArgs(authenticateResult)).ConfigureAwait(false); } return(authenticateResult); } catch (Exception exception) { _disconnectReason = MqttClientDisconnectReason.UnspecifiedError; _logger.Error(exception, "Error while connecting with server."); await DisconnectInternalAsync(null, exception, authenticateResult).ConfigureAwait(false); throw; } }
public void LogTrace(string message) => _scopedLogger.Verbose(message);