public static void RunInBackground(this Task task, MqttNetSourceLogger logger = null) { task?.ContinueWith(t => { // Consume the exception first so that we get no exception regarding the not observed exception. var exception = t.Exception; logger?.Error(exception, "Unhandled exception in background task."); }, TaskContinuationOptions.OnlyOnFaulted); }
void InspectPacket(byte[] buffer, MqttPacketFlowDirection direction) { try { var eventArgs = new InspectMqttPacketEventArgs { Buffer = buffer, Direction = direction }; _asyncEvent.InvokeAsync(eventArgs).GetAwaiter().GetResult(); } catch (Exception exception) { _logger.Error(exception, "Error while inspecting packet."); } }
void InspectPacket(byte[] buffer, MqttPacketFlowDirection direction) { try { var context = new ProcessMqttPacketContext { Buffer = buffer, Direction = direction }; _packetInspector.ProcessMqttPacket(context); } catch (Exception exception) { _logger.Error(exception, "Error while inspecting packet."); } }
public async Task HandleClientConnectionAsync(IMqttChannelAdapter channelAdapter, CancellationToken cancellationToken) { MqttClientConnection clientConnection = null; try { var connectPacket = await ReceiveConnectPacket(channelAdapter, cancellationToken).ConfigureAwait(false); if (connectPacket == null) { // Nothing was received in time etc. return; } MqttConnAckPacket connAckPacket; var connectionValidatorContext = await ValidateConnection(connectPacket, channelAdapter).ConfigureAwait(false); if (connectionValidatorContext.ReasonCode != MqttConnectReasonCode.Success) { // Send failure response here without preparing a session! connAckPacket = channelAdapter.PacketFormatterAdapter.DataConverter.CreateConnAckPacket( connectionValidatorContext); await channelAdapter.SendPacketAsync(connAckPacket, cancellationToken).ConfigureAwait(false); return; } connAckPacket = channelAdapter.PacketFormatterAdapter.DataConverter.CreateConnAckPacket(connectionValidatorContext); // Pass connAckPacket so that IsSessionPresent flag can be set if the client session already exists clientConnection = await CreateClientConnection(connectPacket, connAckPacket, channelAdapter, connectionValidatorContext.SessionItems).ConfigureAwait(false); await channelAdapter.SendPacketAsync(connAckPacket, cancellationToken).ConfigureAwait(false); await _eventDispatcher.SafeNotifyClientConnectedAsync(connectPacket, channelAdapter) .ConfigureAwait(false); await clientConnection.RunAsync().ConfigureAwait(false); } catch (OperationCanceledException) { } catch (Exception exception) { _logger.Error(exception, exception.Message); } finally { if (clientConnection != null) { if (clientConnection.ClientId != null) { // in case it is a takeover _clientConnections already contains the new connection if (!clientConnection.IsTakenOver) { lock (_clientConnections) { _clientConnections.Remove(clientConnection.ClientId); } if (!_options.EnablePersistentSessions) { await DeleteSessionAsync(clientConnection.ClientId).ConfigureAwait(false); } } } var endpoint = clientConnection.Endpoint; if (clientConnection.ClientId != null && !clientConnection.IsTakenOver) { // The event is fired at a separate place in case of a handover! await _eventDispatcher.SafeNotifyClientDisconnectedAsync( clientConnection.ClientId, clientConnection.IsCleanDisconnect ?MqttClientDisconnectType.Clean : MqttClientDisconnectType.NotClean, endpoint).ConfigureAwait(false); } } clientConnection?.Dispose(); await channelAdapter.DisconnectAsync(_options.DefaultCommunicationTimeout, CancellationToken.None) .ConfigureAwait(false); } }
async Task ReceivePackagesLoop(CancellationToken cancellationToken) { try { // We do not listen for the cancellation token here because the internal buffer might still // contain data to be read even if the TCP connection was already dropped. So we rely on an // own exception in the reading loop! while (!cancellationToken.IsCancellationRequested) { var packet = await _channelAdapter.ReceivePacketAsync(cancellationToken).ConfigureAwait(false); if (packet == null) { // The client has closed the connection gracefully. return; } Statistics.HandleReceivedPacket(packet); if (packet is MqttPublishPacket publishPacket) { await HandleIncomingPublishPacket(publishPacket, cancellationToken).ConfigureAwait(false); } else if (packet is MqttPubRelPacket pubRelPacket) { await HandleIncomingPubRelPacket(pubRelPacket, cancellationToken).ConfigureAwait(false); } else if (packet is MqttSubscribePacket subscribePacket) { await HandleIncomingSubscribePacket(subscribePacket, cancellationToken).ConfigureAwait(false); } else if (packet is MqttUnsubscribePacket unsubscribePacket) { await HandleIncomingUnsubscribePacket(unsubscribePacket, cancellationToken).ConfigureAwait(false); } else if (packet is MqttPingReqPacket) { // See: The Server MUST send a PINGRESP packet in response to a PINGREQ packet [MQTT-3.12.4-1]. await SendPacketAsync(MqttPingRespPacket.Instance, cancellationToken).ConfigureAwait(false); } else if (packet is MqttPingRespPacket) { throw new MqttProtocolViolationException("A PINGRESP Packet is sent by the Server to the Client in response to a PINGREQ Packet only."); } else if (packet is MqttDisconnectPacket) { IsCleanDisconnect = true; return; } else { if (!_packetDispatcher.TryDispatch(packet)) { throw new MqttProtocolViolationException($"Received packet '{packet}' at an unexpected time."); } } } } 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); } } }
/// <inheritdoc/> /// <exception cref="ArgumentException"></exception> public IObservable <MqttApplicationMessageReceivedEventArgs> Connect(string topic) { if (string.IsNullOrWhiteSpace(topic)) { throw new ArgumentException($"'{nameof(topic)}' cannot be null or whitespace", nameof(topic)); } ThrowIfDisposed(); lock (topicSubscriptionCache) { // try get exiting observable for topic if (!topicSubscriptionCache.TryGetValue(topic, out IObservable <MqttApplicationMessageReceivedEventArgs> observable)) { // create new observable for topic observable = Observable .Create <MqttApplicationMessageReceivedEventArgs>(async observer => { // subscribe to topic try { await InternalClient.SubscribeAsync(topic).ConfigureAwait(false); } catch (Exception exception) { logger.Error(exception, "Error while maintaining subscribe from topic."); observer.OnError(exception); return(Disposable.Empty); } // filter all received messages // and subscribe to messages for this topic var messageSubscription = applicationMessageReceived .FilterTopic(topic) .Subscribe(observer); return(Disposable.Create(async() => { // clean up subscription when no observer subscribed lock (topicSubscriptionCache) { messageSubscription.Dispose(); topicSubscriptionCache.Remove(topic); } try { await InternalClient.UnsubscribeAsync(topic).ConfigureAwait(false); } catch (ObjectDisposedException) { } // if disposed there is nothing to unsubscribe catch (Exception exception) { logger.Error(exception, "Error while maintaining unsubscribe from topic."); } })); }) .Publish() // publish from on source observable .RefCount(); // count subscriptions and dispose source observable when no subscription // save observable for topic lock (topicSubscriptionCache) topicSubscriptionCache.Add(topic, observable); } return(observable); } }
async Task ReceivePackagesLoop(CancellationToken cancellationToken) { try { // We do not listen for the cancellation token here because the internal buffer might still // contain data to be read even if the TCP connection was already dropped. So we rely on an // own exception in the reading loop! while (!cancellationToken.IsCancellationRequested) { var packet = await ChannelAdapter.ReceivePacketAsync(cancellationToken).ConfigureAwait(false); if (packet == null) { return; } var processPacket = true; if (_eventContainer.InterceptingInboundPacketEvent.HasHandlers) { var interceptingPacketEventArgs = new InterceptingPacketEventArgs { ClientId = Id, Endpoint = Endpoint, Packet = packet, CancellationToken = cancellationToken }; await _eventContainer.InterceptingInboundPacketEvent.InvokeAsync(interceptingPacketEventArgs).ConfigureAwait(false); packet = interceptingPacketEventArgs.Packet; processPacket = interceptingPacketEventArgs.ProcessPacket; } if (!processPacket || packet == null) { // Restart the receiving process to get the next packet ignoring the current one.. continue; } Statistics.HandleReceivedPacket(packet); if (packet is MqttPublishPacket publishPacket) { await HandleIncomingPublishPacket(publishPacket, cancellationToken).ConfigureAwait(false); } else if (packet is MqttPubAckPacket pubAckPacket) { Session.AcknowledgePublishPacket(pubAckPacket.PacketIdentifier); } else if (packet is MqttPubCompPacket pubCompPacket) { Session.AcknowledgePublishPacket(pubCompPacket.PacketIdentifier); } else if (packet is MqttPubRecPacket pubRecPacket) { HandleIncomingPubRecPacket(pubRecPacket); } else if (packet is MqttPubRelPacket pubRelPacket) { HandleIncomingPubRelPacket(pubRelPacket); } else if (packet is MqttSubscribePacket subscribePacket) { await HandleIncomingSubscribePacket(subscribePacket, cancellationToken).ConfigureAwait(false); } else if (packet is MqttUnsubscribePacket unsubscribePacket) { await HandleIncomingUnsubscribePacket(unsubscribePacket, cancellationToken).ConfigureAwait(false); } else if (packet is MqttPingReqPacket) { // See: The Server MUST send a PINGRESP packet in response to a PINGREQ packet [MQTT-3.12.4-1]. Session.EnqueueHealthPacket(new MqttPacketBusItem(MqttPingRespPacket.Instance)); } else if (packet is MqttPingRespPacket) { throw new MqttProtocolViolationException("A PINGRESP Packet is sent by the Server to the Client in response to a PINGREQ Packet only."); } else if (packet is MqttDisconnectPacket) { IsCleanDisconnect = true; return; } else { if (!_packetDispatcher.TryDispatch(packet)) { throw new MqttProtocolViolationException($"Received packet '{packet}' at an unexpected time."); } } } } catch (OperationCanceledException) { } catch (Exception exception) { if (exception is MqttCommunicationException) { _logger.Warning(exception, "Client '{0}': Communication exception while receiving client packets.", Id); } else { _logger.Error(exception, "Client '{0}': Error while receiving client packets.", Id); } } }
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; } }