public void Start() { #if NET40 TaskExtension.Run(() => TryProcessQueuedApplicationMessagesAsync(_cancellationToken).Wait(), _cancellationToken).Forget(_logger); #else Task.Run(() => TryProcessQueuedApplicationMessagesAsync(_cancellationToken), _cancellationToken).Forget(_logger); #endif }
public void Start(int keepAlivePeriod, CancellationToken cancellationToken) { if (keepAlivePeriod == 0) { return; } #if NET40 TaskExtension.Run(() => RunAsync(keepAlivePeriod, cancellationToken).Wait(), cancellationToken).Forget(_logger); #else Task.Run(() => RunAsync(keepAlivePeriod, cancellationToken), cancellationToken).Forget(_logger); #endif }
#pragma warning disable 4014 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 for {_localEndPoint} TLS={_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; } _socket.Bind(_localEndPoint); _socket.Listen(_options.ConnectionBacklog); #if NET40 TaskExtension.Run(() => AcceptClientConnectionsAsync(cancellationToken).Wait(), cancellationToken).Forget(_logger); #else TaskExtension.Run(() => AcceptClientConnectionsAsync(cancellationToken), cancellationToken).Forget(_logger); #endif return(true); } catch (Exception exception) { if (!treatErrorsAsWarning) { throw; } _logger.Warning(exception, "Error while creating listener socket for local end point '{0}'.", _localEndPoint); return(false); } }
#pragma warning disable 4014 async Task AcceptClientConnectionsAsync(CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { try { var clientSocket = await _socket.AcceptAsync().ConfigureAwait(false); if (clientSocket == null) { continue; } #if NET40 TaskExtension.Run(() => TryHandleClientConnectionAsync(clientSocket).Wait(), cancellationToken).Forget(_logger); #else TaskExtension.Run(() => TryHandleClientConnectionAsync(clientSocket), cancellationToken).Forget(_logger); #endif } 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 TaskExtension.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false); } } }
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>(); #if NET40 _publishPacketReceiverTask = TaskExtension.Run(() => ProcessReceivedPublishPackets(backgroundCancellationToken).Wait(), backgroundCancellationToken); _packetReceiverTask = TaskExtension.Run(() => TryReceivePacketsAsync(backgroundCancellationToken).Wait(), backgroundCancellationToken); #else _publishPacketReceiverTask = Task.Run(() => ProcessReceivedPublishPackets(backgroundCancellationToken), backgroundCancellationToken); _packetReceiverTask = Task.Run(() => TryReceivePacketsAsync(backgroundCancellationToken), backgroundCancellationToken); #endif authenticateResult = await AuthenticateAsync(adapter, options.WillMessage, combined.Token).ConfigureAwait(false); } _sendTracker.Restart(); _receiveTracker.Restart(); if (Options.KeepAlivePeriod != TimeSpan.Zero) { #if NET40 _keepAlivePacketsSenderTask = TaskExtension.Run(() => TrySendKeepAliveMessagesAsync(backgroundCancellationToken).Wait(), backgroundCancellationToken); #else _keepAlivePacketsSenderTask = Task.Run(() => TrySendKeepAliveMessagesAsync(backgroundCancellationToken), backgroundCancellationToken); #endif } _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 DisconnectInternalAsync(Task sender, Exception exception, MqttClientAuthenticateResult authenticateResult) { var clientWasConnected = IsConnected; var reasonCode = MqttClientDisconnectReason.NormalDisconnection; TryInitiateDisconnect(); _isConnected = false; try { if (_adapter != null) { _logger.Verbose("Disconnecting [Timeout={0}]", Options.CommunicationTimeout); await _adapter.DisconnectAsync(Options.CommunicationTimeout, CancellationToken.None).ConfigureAwait(false); } _logger.Verbose("Disconnected from adapter."); } catch (Exception adapterException) { _logger.Warning(adapterException, "Error while disconnecting from adapter."); reasonCode = MqttClientDisconnectReason.UnspecifiedError; } try { var receiverTask = WaitForTaskAsync(_packetReceiverTask, sender); var publishPacketReceiverTask = WaitForTaskAsync(_publishPacketReceiverTask, sender); var keepAliveTask = WaitForTaskAsync(_keepAlivePacketsSenderTask, sender); #if NET40 Task.WaitAll(receiverTask, publishPacketReceiverTask, keepAliveTask); #else await Task.WhenAll(receiverTask, publishPacketReceiverTask, keepAliveTask).ConfigureAwait(false); #endif _publishPacketReceiverQueue?.Dispose(); } catch (Exception e) { _logger.Warning(e, "Error while waiting for internal tasks."); reasonCode = MqttClientDisconnectReason.UnspecifiedError; } finally { Cleanup(); _cleanDisconnectInitiated = false; _logger.Info("Disconnected."); var disconnectedHandler = DisconnectedHandler; if (disconnectedHandler != null) { // This handler must be executed in a new thread because otherwise a dead lock may happen // when trying to reconnect in that handler etc. #if NET40 TaskExtension.Run(() => disconnectedHandler.HandleDisconnectedAsync(new MqttClientDisconnectedEventArgs(clientWasConnected, exception, authenticateResult, reasonCode))).Forget(_logger); #else Task.Run(() => disconnectedHandler.HandleDisconnectedAsync(new MqttClientDisconnectedEventArgs(clientWasConnected, exception, authenticateResult, reasonCode))).Forget(_logger); #endif } } }
async Task RunInternalAsync(MqttConnectionValidatorContext connectionValidatorContext) { var disconnectType = MqttClientDisconnectType.NotClean; try { await _onStart(); _logger.Info("Client '{0}': Session started.", ClientId); _channelAdapter.ReadingPacketStartedCallback = OnAdapterReadingPacketStarted; _channelAdapter.ReadingPacketCompletedCallback = OnAdapterReadingPacketCompleted; Session.WillMessage = ConnectPacket.WillMessage; #if NET40 TaskExtension.Run(() => SendPendingPacketsAsync(_cancellationToken.Token).Wait(), _cancellationToken.Token).Forget(_logger); #else Task.Run(() => SendPendingPacketsAsync(_cancellationToken.Token), _cancellationToken.Token).Forget(_logger); #endif // 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; _logger.Info("Client '{0}': Connection stopped.", ClientId); _packageReceiverTask = null; try { await _onStop(disconnectType); } catch (Exception e) { _logger.Error(e, "client '{0}': Error while cleaning up", ClientId); } } }