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; } }
public async Task LoadMessagesAsync() { if (_options.Storage == null) { return; } try { var retainedMessages = await _options.Storage.LoadRetainedMessagesAsync().ConfigureAwait(false); if (retainedMessages?.Any() == true) { using (await _messagesLock.WaitAsync().ConfigureAwait(false)) { _messages.Clear(); foreach (var retainedMessage in retainedMessages) { _messages[retainedMessage.Topic] = retainedMessage; } } } } catch (Exception exception) { _logger.Error(exception, "Unhandled exception while loading retained messages."); } }
private async void OnConnectionReceivedAsync(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args) { try { var clientHandler = ClientHandler; if (clientHandler != null) { using (var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(args.Socket, _options), new MqttPacketFormatterAdapter(), _logger)) { 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; } _logger.Error(exception, "Error while handling client connection."); } finally { try { args.Socket.Dispose(); } catch (Exception exception) { _logger.Error(exception, "Error while cleaning up client connection"); } } }
private async Task AcceptClientConnectionsAsync(CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { try { var clientSocket = await PlatformAbstractionLayer.AcceptAsync(_socket).ConfigureAwait(false); if (clientSocket == null) { continue; } Task.Run(() => TryHandleClientConnectionAsync(clientSocket), cancellationToken).Forget(_logger); } catch (OperationCanceledException) { } catch (Exception exception) { if (exception is SocketException socketException) { if (socketException.SocketErrorCode == SocketError.ConnectionAborted || socketException.SocketErrorCode == SocketError.OperationAborted) { continue; } } _logger.Error(exception, $"Error while accepting connection at TCP listener {_localEndPoint} TLS={_tlsCertificate != null}."); await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false); } } }
private async Task RunAsync(int keepAlivePeriod, CancellationToken cancellationToken) { try { _lastPacketReceivedTracker.Restart(); _lastNonKeepAlivePacketReceivedTracker.Restart(); while (!cancellationToken.IsCancellationRequested) { // Values described here: [MQTT-3.1.2-24]. if (!_isPaused && _lastPacketReceivedTracker.Elapsed.TotalSeconds > keepAlivePeriod * 1.5D) { _logger.Warning(null, "Client '{0}': Did not receive any packet or keep alive signal.", _clientSession.ClientId); _clientSession.Stop(MqttClientDisconnectType.NotClean); return; } await Task.Delay(keepAlivePeriod, cancellationToken).ConfigureAwait(false); } } catch (OperationCanceledException) { } catch (Exception exception) { _logger.Error(exception, "Client '{0}': Unhandled exception while checking keep alive timeouts.", _clientSession.ClientId); } finally { _logger.Verbose("Client {0}: Stopped checking keep alive timeout.", _clientSession.ClientId); } }
private void ProcessQueuedApplicationMessages(CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { try { var enqueuedApplicationMessage = _messageQueue.Take(cancellationToken); var sender = enqueuedApplicationMessage.Sender; var applicationMessage = enqueuedApplicationMessage.PublishPacket.ToApplicationMessage(); var interceptorContext = InterceptApplicationMessage(sender, applicationMessage); if (interceptorContext != null) { if (interceptorContext.CloseConnection) { enqueuedApplicationMessage.Sender.Stop(MqttClientDisconnectType.NotClean); } if (interceptorContext.ApplicationMessage == null || !interceptorContext.AcceptPublish) { return; } applicationMessage = interceptorContext.ApplicationMessage; } Server.OnApplicationMessageReceived(sender?.ClientId, applicationMessage); if (applicationMessage.Retain) { _retainedMessagesManager.HandleMessageAsync(sender?.ClientId, applicationMessage).GetAwaiter().GetResult(); } foreach (var clientSession in GetSessions()) { clientSession.EnqueueApplicationMessage(enqueuedApplicationMessage.Sender, applicationMessage.ToPublishPacket()); } } catch (OperationCanceledException) { } catch (Exception exception) { _logger.Error(exception, "Unhandled exception while processing queued application message."); } } }
public static void Forget(this Task task, IMqttNetChildLogger logger) { task?.ContinueWith(t => { logger.Error(t.Exception, "Unhandled exception."); }, TaskContinuationOptions.OnlyOnFaulted); }
private async Task TryProcessQueuedApplicationMessagesAsync(CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { try { await TryProcessNextQueuedApplicationMessageAsync(cancellationToken).ConfigureAwait(false); } catch (OperationCanceledException) { } catch (Exception exception) { _logger.Error(exception, "Unhandled exception while processing queued application messages."); } } }
private void TryProcessQueuedApplicationMessages(CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { try { TryProcessNextQueuedApplicationMessage(cancellationToken); } catch (OperationCanceledException) { } catch (Exception exception) { _logger.Error(exception, "Unhandled exception while processing queued application messages."); } } }
private async Task SendQueuedPacketsAsync(IMqttChannelAdapter adapter, CancellationToken cancellationToken) { try { while (!cancellationToken.IsCancellationRequested) { await TrySendNextQueuedPacketAsync(adapter, cancellationToken).ConfigureAwait(false); } } catch (OperationCanceledException) { } catch (Exception exception) { _logger.Error(exception, "Unhandled exception while sending enqueued packet (ClientId: {0}).", _clientSession.ClientId); } }
private void AcceptDefaultEndpointConnectionsAsync(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args) { try { var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(args.Socket, _options), new MqttPacketSerializer(), _logger); ClientAccepted?.Invoke(this, new MqttServerAdapterClientAcceptedEventArgs(clientAdapter)); } catch (Exception exception) { _logger.Error(exception, "Error while accepting connection at default endpoint."); } }
private async Task AcceptClientConnectionsAsync(CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { try { #if NET452 || NET461 var clientSocket = await Task.Factory.FromAsync(_socket.BeginAccept, _socket.EndAccept, null).ConfigureAwait(false); #else var clientSocket = await _socket.AcceptAsync().ConfigureAwait(false); #endif #pragma warning disable 4014 Task.Run(() => TryHandleClientConnectionAsync(clientSocket), cancellationToken); #pragma warning restore 4014 } catch (Exception exception) { _logger.Error(exception, $"Error while accepting connection at TCP listener {_socket.LocalEndPoint} TLS={_tlsCertificate != null}."); await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false); } } }
private async Task MaintainConnectionAsync(CancellationToken cancellationToken) { try { while (!cancellationToken.IsCancellationRequested) { await TryMaintainConnectionAsync(cancellationToken).ConfigureAwait(false); } } catch (OperationCanceledException) { } catch (Exception exception) { _logger.Error(exception, "Unhandled exception while maintaining connection."); } finally { await _mqttClient.DisconnectAsync().ConfigureAwait(false); _logger.Info("Stopped"); } }
public async Task TryHandleClientDisconnectedAsync(string clientId, MqttClientDisconnectType disconnectType) { try { var handler = ClientDisconnectedHandler; if (handler == null) { return; } await handler.HandleClientDisconnectedAsync(new MqttServerClientDisconnectedEventArgs(clientId, disconnectType)).ConfigureAwait(false); } catch (Exception exception) { _logger.Error(exception, "Error while handling 'ClientDisconnected' event."); } }
private async Task AcceptClientConnectionsAsync() { while (!_cancellationToken.IsCancellationRequested) { try { #if NET452 || NET461 var clientSocket = await Task.Factory.FromAsync(_socket.BeginAccept, _socket.EndAccept, null).ConfigureAwait(false); #else var clientSocket = await _socket.AcceptAsync().ConfigureAwait(false); #endif clientSocket.NoDelay = true; SslStream sslStream = null; if (_tlsCertificate != null) { sslStream = new SslStream(new NetworkStream(clientSocket), false); await sslStream.AuthenticateAsServerAsync(_tlsCertificate, false, _tlsOptions.SslProtocol, false).ConfigureAwait(false); } _logger.Verbose("Client '{0}' accepted by TCP listener '{1}, {2}'.", clientSocket.RemoteEndPoint, _socket.LocalEndPoint, _addressFamily == AddressFamily.InterNetwork ? "ipv4" : "ipv6"); var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(clientSocket, sslStream), new MqttPacketSerializer(), _logger); ClientAccepted?.Invoke(this, new MqttServerAdapterClientAcceptedEventArgs(clientAdapter)); } catch (ObjectDisposedException) { // It can happen that the listener socket is accessed after the cancellation token is already set and the listener socket is disposed. } catch (Exception exception) { if (exception is SocketException s && s.SocketErrorCode == SocketError.OperationAborted) { return; } _logger.Error(exception, $"Error while accepting connection at TCP listener {_socket.LocalEndPoint} TLS={_tlsCertificate != null}."); await Task.Delay(TimeSpan.FromSeconds(1), _cancellationToken).ConfigureAwait(false); } } }
private async Task RunAsync(int keepAlivePeriod, CancellationToken cancellationToken) { try { _lastPacketReceivedTracker.Restart(); _lastNonKeepAlivePacketReceivedTracker.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.", _clientSession.ClientId); _clientSession.Stop(MqttClientDisconnectType.NotClean); 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 Task.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.", _clientSession.ClientId); } finally { _logger.Verbose("Client '{0}': Stopped checking keep alive timeout.", _clientSession.ClientId); } }
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; _disconnectGate = 0; var adapter = _adapterFactory.CreateClientAdapter(options, _logger); _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."); _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; } }
private async Task <MqttClientDisconnectType> RunInternalAsync() { var disconnectType = MqttClientDisconnectType.NotClean; try { _logger.Info("Client '{0}': Session started.", ClientId); _channelAdapter.ReadingPacketStartedCallback = OnAdapterReadingPacketStarted; _channelAdapter.ReadingPacketCompletedCallback = OnAdapterReadingPacketCompleted; Session.WillMessage = _connectPacket.WillMessage; #pragma warning disable 4014 Task.Run(() => SendPendingPacketsAsync(_cancellationToken.Token), _cancellationToken.Token); #pragma warning restore 4014 // TODO: Change to single thread in SessionManager. Or use SessionManager and stats from KeepAliveMonitor. _keepAliveMonitor.Start(_connectPacket.KeepAlivePeriod, _cancellationToken.Token); await SendAsync( new MqttConnAckPacket { ReturnCode = MqttConnectReturnCode.ConnectionAccepted, ReasonCode = MqttConnectReasonCode.Success, IsSessionPresent = !Session.IsCleanSession }).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}': Unhandled exception while receiving client packets.", ClientId); } StopInternal(); } finally { if (Session.WillMessage != null) { _sessionsManager.DispatchApplicationMessage(Session.WillMessage, this); Session.WillMessage = null; } _packetDispatcher.Reset(); _channelAdapter.ReadingPacketStartedCallback = null; _channelAdapter.ReadingPacketCompletedCallback = null; _logger.Info("Client '{0}': Session stopped.", ClientId); _packageReceiverTask = null; } return(disconnectType); }
private async Task RunInternalAsync(MqttConnectPacket connectPacket, IMqttChannelAdapter adapter) { if (connectPacket == null) { throw new ArgumentNullException(nameof(connectPacket)); } if (adapter == null) { throw new ArgumentNullException(nameof(adapter)); } try { if (_cancellationTokenSource != null) { Stop(MqttClientDisconnectType.Clean, true); } adapter.ReadingPacketStarted += OnAdapterReadingPacketStarted; adapter.ReadingPacketCompleted += OnAdapterReadingPacketCompleted; _cancellationTokenSource = new CancellationTokenSource(); //workaround for https://github.com/dotnet/corefx/issues/24430 #pragma warning disable 4014 _cleanupHandle = _cancellationTokenSource.Token.Register(async() => { await TryDisconnectAdapterAsync(adapter).ConfigureAwait(false); TryDisposeAdapter(adapter); }); #pragma warning restore 4014 //end workaround _wasCleanDisconnect = false; _willMessage = connectPacket.WillMessage; _pendingPacketsQueue.Start(adapter, _cancellationTokenSource.Token); _keepAliveMonitor.Start(connectPacket.KeepAlivePeriod, _cancellationTokenSource.Token); _adapterEndpoint = adapter.Endpoint; _adapterProtocolVersion = adapter.PacketSerializer.ProtocolVersion; while (!_cancellationTokenSource.IsCancellationRequested) { var packet = await adapter.ReceivePacketAsync(TimeSpan.Zero, _cancellationTokenSource.Token).ConfigureAwait(false); if (packet != null) { _keepAliveMonitor.PacketReceived(packet); ProcessReceivedPacket(adapter, packet, _cancellationTokenSource.Token); } } } catch (OperationCanceledException) { } catch (Exception exception) { if (exception is MqttCommunicationException) { if (exception is MqttCommunicationClosedGracefullyException) { _logger.Verbose("Client '{0}': Connection closed gracefully.", ClientId); } else { _logger.Warning(exception, "Client '{0}': Communication exception while receiving client packets.", ClientId); } } else { _logger.Error(exception, "Client '{0}': Unhandled exception while receiving client packets.", ClientId); } Stop(MqttClientDisconnectType.NotClean, true); } finally { _adapterEndpoint = null; _adapterProtocolVersion = null; // Uncomment as soon as the workaround above is no longer needed. //await TryDisconnectAdapterAsync(adapter).ConfigureAwait(false); //TryDisposeAdapter(adapter); _cleanupHandle?.Dispose(); _cleanupHandle = null; _cancellationTokenSource?.Cancel(false); _cancellationTokenSource?.Dispose(); _cancellationTokenSource = null; } }
public async Task <bool> RunAsync(MqttConnectPacket connectPacket, IMqttChannelAdapter adapter) { if (connectPacket == null) { throw new ArgumentNullException(nameof(connectPacket)); } if (adapter == null) { throw new ArgumentNullException(nameof(adapter)); } try { _adapter = adapter; adapter.ReadingPacketStarted += OnAdapterReadingPacketStarted; adapter.ReadingPacketCompleted += OnAdapterReadingPacketCompleted; _cancellationTokenSource = new CancellationTokenSource(); _wasCleanDisconnect = false; _willMessage = connectPacket.WillMessage; _pendingPacketsQueue.Start(adapter, _cancellationTokenSource.Token); _keepAliveMonitor.Start(connectPacket.KeepAlivePeriod, _cancellationTokenSource.Token); while (!_cancellationTokenSource.IsCancellationRequested) { var packet = await adapter.ReceivePacketAsync(TimeSpan.Zero, _cancellationTokenSource.Token).ConfigureAwait(false); if (packet != null) { _keepAliveMonitor.PacketReceived(packet); ProcessReceivedPacket(adapter, packet, _cancellationTokenSource.Token); } } } catch (OperationCanceledException) { } catch (Exception exception) { if (exception is MqttCommunicationException) { if (exception is MqttCommunicationClosedGracefullyException) { _logger.Verbose("Client '{0}': Connection closed gracefully.", ClientId); } else { _logger.Warning(exception, "Client '{0}': Communication exception while receiving client packets.", ClientId); } } else { _logger.Error(exception, "Client '{0}': Unhandled exception while receiving client packets.", ClientId); } Stop(MqttClientDisconnectType.NotClean); } finally { if (_adapter != null) { _adapter.ReadingPacketStarted -= OnAdapterReadingPacketStarted; _adapter.ReadingPacketCompleted -= OnAdapterReadingPacketCompleted; } _adapter = null; _cancellationTokenSource?.Dispose(); _cancellationTokenSource = null; } return(_wasCleanDisconnect); }