public async Task DeleteSessionAsync(string clientId) { MqttClientConnection connection; MqttClientSession session; lock (_clientConnections) { _clientConnections.TryGetValue(clientId, out connection); } lock (_clientSessions) { _clientSessions.TryGetValue(clientId, out session); _clientSessions.Remove(clientId); } if (connection != null) { await connection.StopAsync(MqttClientDisconnectReason.NormalDisconnection).ConfigureAwait(false); } session?.Dispose(); _logger.Verbose("Session for client '{0}' deleted.", clientId); }
public async Task <MqttPacket> ReceivePacketAsync(CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); ThrowIfDisposed(); try { _packetInspector?.BeginReceivePacket(); ReceivedMqttPacket receivedPacket; var receivedPacketTask = ReceiveAsync(cancellationToken); if (receivedPacketTask.IsCompleted) { receivedPacket = receivedPacketTask.Result; } else { receivedPacket = await receivedPacketTask.ConfigureAwait(false); } if (receivedPacket.TotalLength == 0 || cancellationToken.IsCancellationRequested) { return(null); } _packetInspector?.EndReceivePacket(); Interlocked.Add(ref _bytesSent, receivedPacket.TotalLength); if (PacketFormatterAdapter.ProtocolVersion == MqttProtocolVersion.Unknown) { PacketFormatterAdapter.DetectProtocolVersion(receivedPacket); } var packet = PacketFormatterAdapter.Decode(receivedPacket); if (packet == null) { throw new MqttProtocolViolationException("Received malformed packet."); } _logger.Verbose("RX ({0} bytes) <<< {1}", receivedPacket.TotalLength, packet); return(packet); } catch (OperationCanceledException) { } catch (ObjectDisposedException) { } catch (Exception exception) { if (!WrapAndThrowException(exception)) { throw; } } return(null); }
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 '{0}' (Timeout={1}).", options.ChannelOptions, 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 bool Start(bool treatErrorsAsWarning, CancellationToken cancellationToken) { try { var boundIp = _options.BoundInterNetworkAddress; if (_addressFamily == AddressFamily.InterNetworkV6) { boundIp = _options.BoundInterNetworkV6Address; } _localEndPoint = new IPEndPoint(boundIp, _options.Port); _logger.Info("Starting TCP listener (Endpoint='{0}', TLS={1}).", _localEndPoint, _tlsCertificate != null); _socket = new CrossPlatformSocket(_addressFamily); // Usage of socket options is described here: https://docs.microsoft.com/en-us/dotnet/api/system.net.sockets.socket.setsocketoption?view=netcore-2.2 if (_options.ReuseAddress) { _socket.ReuseAddress = true; } if (_options.NoDelay) { _socket.NoDelay = true; } if (_options.LingerState != null) { _socket.LingerState = _options.LingerState; } _socket.Bind(_localEndPoint); // Get the local endpoint back from the socket. The port may have changed. // This can happen when port 0 is used. Then the OS will choose the next free port. _localEndPoint = (IPEndPoint)_socket.LocalEndPoint; _options.Port = _localEndPoint.Port; _socket.Listen(_options.ConnectionBacklog); _logger.Verbose("TCP listener started (Endpoint='{0}'.", _localEndPoint); Task.Run(() => AcceptClientConnectionsAsync(cancellationToken), cancellationToken).RunInBackground(_logger); return(true); } catch (Exception exception) { if (!treatErrorsAsWarning) { throw; } _logger.Warning(exception, "Error while creating listener socket for local end point '{0}'.", _localEndPoint); return(false); } }
public void Log_10000_Messages_With_To_String() { _useHandler = true; for (var i = 0; i < 10000; i++) { _sourceEventLogger.Verbose("test log message {0}", "parameter"); } }
public async Task DeleteSessionAsync(string clientId) { MqttClient connection; lock (_clients) { _clients.TryGetValue(clientId, out connection); } MqttSession session; lock (_sessionsManagementLock) { _sessions.TryGetValue(clientId, out session); _sessions.Remove(clientId); if (session != null) { _subscriberSessions.Remove(session); } } try { if (connection != null) { await connection.StopAsync(MqttDisconnectReasonCode.NormalDisconnection).ConfigureAwait(false); } } catch (Exception exception) { _logger.Error(exception, $"Error while deleting session '{clientId}'."); } try { if (_eventContainer.SessionDeletedEvent.HasHandlers) { var eventArgs = new SessionDeletedEventArgs { Id = session?.Id }; await _eventContainer.SessionDeletedEvent.TryInvokeAsync(eventArgs, _logger).ConfigureAwait(false); } } catch (Exception exception) { _logger.Error(exception, $"Error while executing session deleted event for session '{clientId}'."); } session?.Dispose(); _logger.Verbose("Session for client '{0}' deleted.", clientId); }
public void Log_10000_Messages_With_NullLogger() { for (var i = 0; i < 10000; i++) { _sourceNullLogger.Verbose("test log message {0}", "parameter"); } }
public void Log_10000_Messages_No_Listener() { _useHandler = false; for (var i = 0; i < 10000; i++) { _sourceEventLoggerNoListener.Verbose("test log message {0}", "parameter"); } }
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); _packetInspectorHandler.BeginSendPacket(packetData); 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(); } } }
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 HandleMessageAsync(string clientId, MqttApplicationMessage applicationMessage) { if (applicationMessage == null) { throw new ArgumentNullException(nameof(applicationMessage)); } try { List <MqttApplicationMessage> messagesForSave = null; var saveIsRequired = false; lock (_messages) { 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 ?? PlatformAbstractionLayer.EmptyByteArray)) { _messages[applicationMessage.Topic] = applicationMessage; saveIsRequired = true; } } _logger.Verbose("Client '{0}' set retained message for topic '{1}'.", clientId, applicationMessage.Topic); } if (saveIsRequired) { messagesForSave = new List <MqttApplicationMessage>(_messages.Values); } } if (saveIsRequired) { if (_options.Storage != null) { using (await _storageAccessLock.WaitAsync(CancellationToken.None).ConfigureAwait(false)) { await _options.Storage.SaveRetainedMessagesAsync(messagesForSave).ConfigureAwait(false); } } } } catch (Exception exception) { _logger.Error(exception, "Unhandled exception while handling retained messages."); } }
async Task SendPacketsLoop(CancellationToken cancellationToken) { MqttQueuedApplicationMessage queuedApplicationMessage = null; MqttPublishPacket publishPacket = null; try { while (!cancellationToken.IsCancellationRequested) { queuedApplicationMessage = await Session.ApplicationMessagesQueue.Dequeue(cancellationToken).ConfigureAwait(false); // Also check the cancellation token here because the dequeue is blocking and may take some time. if (cancellationToken.IsCancellationRequested) { return; } if (queuedApplicationMessage == null) { continue; } publishPacket = await CreatePublishPacket(queuedApplicationMessage).ConfigureAwait(false); if (publishPacket == null) { continue; } if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce) { await SendPacketAsync(publishPacket, cancellationToken).ConfigureAwait(false); } else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce) { using (var awaitable = _packetDispatcher.AddAwaitable <MqttPubAckPacket>(publishPacket.PacketIdentifier)) { await SendPacketAsync(publishPacket, cancellationToken).ConfigureAwait(false); await awaitable.WaitOneAsync(_serverOptions.DefaultCommunicationTimeout).ConfigureAwait(false); } } else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce) { using (var awaitableRec = _packetDispatcher.AddAwaitable <MqttPubRecPacket>(publishPacket.PacketIdentifier)) using (var awaitableComp = _packetDispatcher.AddAwaitable <MqttPubCompPacket>(publishPacket.PacketIdentifier)) { await SendPacketAsync(publishPacket, cancellationToken).ConfigureAwait(false); var pubRecPacket = await awaitableRec.WaitOneAsync(_serverOptions.DefaultCommunicationTimeout).ConfigureAwait(false); var pubRelPacket = _channelAdapter.PacketFormatterAdapter.DataConverter.CreatePubRelPacket(pubRecPacket, MqttApplicationMessageReceivedReasonCode.Success); await SendPacketAsync(pubRelPacket, cancellationToken).ConfigureAwait(false); await awaitableComp.WaitOneAsync(_serverOptions.DefaultCommunicationTimeout).ConfigureAwait(false); } } _logger.Verbose("Client '{0}': Queued application message sent.", ClientId); } } catch (OperationCanceledException) { } catch (Exception exception) { 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) { if (queuedApplicationMessage != null) { queuedApplicationMessage.IsDuplicate = true; Session.ApplicationMessagesQueue.Enqueue(queuedApplicationMessage); } } StopInternal(); } }
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"); }
public async Task <MqttClientConnectResult> 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."); } MqttClientConnectResult connectResult = 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); connectResult = 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(connectResult)).ConfigureAwait(false); } return(connectResult); } catch (Exception exception) { _disconnectReason = MqttClientDisconnectReason.UnspecifiedError; _logger.Error(exception, "Error while connecting with server."); await DisconnectInternalAsync(null, exception, connectResult).ConfigureAwait(false); throw; } }