public async Task RunAsync(string identifier, MqttApplicationMessage willApplicationMessage, IMqttCommunicationAdapter adapter) { if (adapter == null) { throw new ArgumentNullException(nameof(adapter)); } _willApplicationMessage = willApplicationMessage; try { _identifier = identifier; _adapter = adapter; _cancellationTokenSource = new CancellationTokenSource(); _messageQueue.Start(adapter); while (!_cancellationTokenSource.IsCancellationRequested) { var packet = await adapter.ReceivePacketAsync(TimeSpan.Zero); await HandleIncomingPacketAsync(packet); } } catch (MqttCommunicationException) { } catch (Exception exception) { MqttTrace.Error(nameof(MqttClientSession), exception, $"Client '{_identifier}': Unhandled exception while processing client packets."); } finally { if (willApplicationMessage != null) { _publishPacketReceivedCallback(this, _willApplicationMessage.ToPublishPacket()); } _messageQueue.Stop(); _cancellationTokenSource.Cancel(); _adapter = null; MqttTrace.Information(nameof(MqttClientSession), $"Client '{_identifier}': Disconnected."); } }
private async Task ProcessReceivedPacketAsync(IMqttCommunicationAdapter adapter, MqttBasePacket packet) { if (packet is MqttSubscribePacket subscribePacket) { await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, _cancellationTokenSource.Token, _subscriptionsManager.Subscribe(subscribePacket)); EnqueueRetainedMessages(subscribePacket); } else if (packet is MqttUnsubscribePacket unsubscribePacket) { await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, _cancellationTokenSource.Token, _subscriptionsManager.Unsubscribe(unsubscribePacket)); } else if (packet is MqttPublishPacket publishPacket) { await HandleIncomingPublishPacketAsync(adapter, publishPacket); } else if (packet is MqttPubRelPacket pubRelPacket) { await HandleIncomingPubRelPacketAsync(adapter, pubRelPacket); } else if (packet is MqttPubRecPacket pubRecPacket) { await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, _cancellationTokenSource.Token, pubRecPacket.CreateResponse <MqttPubRelPacket>()); } else if (packet is MqttPubAckPacket || packet is MqttPubCompPacket) { // Discard message. } else if (packet is MqttPingReqPacket) { await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, _cancellationTokenSource.Token, new MqttPingRespPacket()); } else if (packet is MqttDisconnectPacket || packet is MqttConnectPacket) { Stop(); } else { MqttNetTrace.Warning(nameof(MqttClientSession), "Client '{0}': Received not supported packet ({1}). Closing connection.", ClientId, packet); Stop(); } }
public async Task ConnectAsync(MqttClientOptions options) { if (options == null) { throw new ArgumentNullException(nameof(options)); } ThrowIfConnected("It is not allowed to connect with a server after the connection is established."); try { _options = options; _cancellationTokenSource = new CancellationTokenSource(); _latestPacketIdentifier = 0; _packetDispatcher.Reset(); _adapter = _communicationChannelFactory.CreateMqttCommunicationAdapter(options); MqttNetTrace.Verbose(nameof(MqttClient), "Trying to connect with server."); await _adapter.ConnectAsync(_options.DefaultCommunicationTimeout).ConfigureAwait(false); MqttNetTrace.Verbose(nameof(MqttClient), "Connection with server established."); await SetupIncomingPacketProcessingAsync(); await AuthenticateAsync(options.WillMessage); MqttNetTrace.Verbose(nameof(MqttClient), "MQTT connection with server established."); if (_options.KeepAlivePeriod != TimeSpan.Zero) { StartSendKeepAliveMessages(_cancellationTokenSource.Token); } Connected?.Invoke(this, EventArgs.Empty); } catch (Exception) { await DisconnectInternalAsync().ConfigureAwait(false); throw; } }
public void Stop() { try { _cancellationTokenSource?.Cancel(false); _cancellationTokenSource?.Dispose(); _cancellationTokenSource = null; _adapter = null; _logger.LogInformation("Client '{0}': Disconnected.", ClientId); } finally { if (_willMessage != null) { _sessionsManager.DispatchApplicationMessage(this, _willMessage); } } }
public void Stop() { try { _cancellationTokenSource?.Cancel(false); _cancellationTokenSource?.Dispose(); _cancellationTokenSource = null; _adapter = null; _logger.LogInformation("Client '{0}': Disconnected.", ClientId); } finally { var willMessage = _willMessage; if (willMessage != null) { _willMessage = null; //clear willmessage so it is send just once _sessionsManager.DispatchApplicationMessage(this, willMessage); } } }
private async Task SendPendingPublishPacketAsync(IMqttCommunicationAdapter adapter, CancellationToken cancellationToken) { MqttPublishPacket packet = null; try { packet = _pendingPublishPackets.Take(cancellationToken); await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, packet).ConfigureAwait(false); _logger.LogTrace("Enqueued packet sent (ClientId: {0}).", _session.ClientId); } catch (Exception exception) { if (exception is MqttCommunicationTimedOutException) { _logger.LogWarning(new EventId(), exception, "Sending publish packet failed due to timeout (ClientId: {0}).", _session.ClientId); } else if (exception is MqttCommunicationException) { _logger.LogWarning(new EventId(), exception, "Sending publish packet failed due to communication exception (ClientId: {0}).", _session.ClientId); } else if (exception is OperationCanceledException) { } else { _logger.LogError(new EventId(), exception, "Sending publish packet failed (ClientId: {0}).", _session.ClientId); } if (packet != null && packet.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce) { packet.Dup = true; _pendingPublishPackets.Add(packet, CancellationToken.None); } _session.Stop(); } }
private async Task HandleIncomingPublishPacketAsync(IMqttCommunicationAdapter adapter, MqttPublishPacket publishPacket) { if (publishPacket.Retain) { await _mqttClientSessionsManager.RetainedMessagesManager.HandleMessageAsync(ClientId, publishPacket); } if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce) { _mqttClientSessionsManager.DispatchPublishPacket(this, publishPacket); return; } if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce) { _mqttClientSessionsManager.DispatchPublishPacket(this, publishPacket); await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, _cancellationTokenSource.Token, new MqttPubAckPacket { PacketIdentifier = publishPacket.PacketIdentifier }); return; } if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce) { // QoS 2 is implement as method "B" [4.3.3 QoS 2: Exactly once delivery] lock (_unacknowledgedPublishPackets) { _unacknowledgedPublishPackets.Add(publishPacket.PacketIdentifier); } _mqttClientSessionsManager.DispatchPublishPacket(this, publishPacket); await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, _cancellationTokenSource.Token, new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier }); return; } throw new MqttCommunicationException("Received a not supported QoS level."); }
private async Task ReceivePacketsAsync(IMqttCommunicationAdapter adapter, CancellationToken cancellationToken) { try { while (!cancellationToken.IsCancellationRequested) { var packet = await adapter.ReceivePacketAsync(TimeSpan.Zero, cancellationToken).ConfigureAwait(false); await ProcessReceivedPacketAsync(adapter, packet, cancellationToken).ConfigureAwait(false); } } catch (OperationCanceledException) { } catch (MqttCommunicationException exception) { _logger.LogWarning(new EventId(), exception, "Client '{0}': Communication exception while processing client packets.", ClientId); await StopAsync(); } catch (Exception exception) { _logger.LogError(new EventId(), exception, "Client '{0}': Unhandled exception while processing client packets.", ClientId); await StopAsync(); } }
private void FireClientAcceptedEvent(IMqttCommunicationAdapter adapter) { ClientAccepted?.Invoke(adapter); }
public MqttClient(MqttClientOptions options, IMqttCommunicationAdapter adapter) { _options = options ?? throw new ArgumentNullException(nameof(options)); _adapter = adapter ?? throw new ArgumentNullException(nameof(adapter)); }
public async Task RunClientSessionAsync(IMqttCommunicationAdapter clientAdapter, CancellationToken cancellationToken) { var clientId = string.Empty; try { if (!(await clientAdapter.ReceivePacketAsync(_options.DefaultCommunicationTimeout, cancellationToken).ConfigureAwait(false) is MqttConnectPacket connectPacket)) { throw new MqttProtocolViolationException("The first packet from a client must be a 'CONNECT' packet [MQTT-3.1.0-1]."); } clientId = connectPacket.ClientId; // Switch to the required protocol version before sending any response. clientAdapter.PacketSerializer.ProtocolVersion = connectPacket.ProtocolVersion; var connectReturnCode = ValidateConnection(connectPacket); if (connectReturnCode != MqttConnectReturnCode.ConnectionAccepted) { await clientAdapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new MqttConnAckPacket { ConnectReturnCode = connectReturnCode }).ConfigureAwait(false); return; } var clientSession = await GetOrCreateClientSessionAsync(connectPacket); await clientAdapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new MqttConnAckPacket { ConnectReturnCode = connectReturnCode, IsSessionPresent = clientSession.IsExistingSession }).ConfigureAwait(false); ClientConnected?.Invoke(this, new MqttClientConnectedEventArgs(new ConnectedMqttClient { ClientId = clientId, ProtocolVersion = clientAdapter.PacketSerializer.ProtocolVersion })); using (_logger.BeginScope(clientId)) { await clientSession.Session.RunAsync(connectPacket.WillMessage, clientAdapter).ConfigureAwait(false); } } catch (Exception exception) { _logger.LogError(new EventId(), exception, exception.Message); } finally { try { await clientAdapter.DisconnectAsync(_options.DefaultCommunicationTimeout).ConfigureAwait(false); } catch (Exception) { // ignored } ClientDisconnected?.Invoke(this, new MqttClientDisconnectedEventArgs(new ConnectedMqttClient { ClientId = clientId, ProtocolVersion = clientAdapter.PacketSerializer.ProtocolVersion })); } }
private void FireClientAcceptedEvent(IMqttCommunicationAdapter adapter) { ClientAccepted?.Invoke(this, new MqttServerAdapterClientAcceptedEventArgs(adapter)); }
public TestMqttCommunicationAdapterFactory(IMqttCommunicationAdapter adapter) { _adapter = adapter; }
public void Stop() { _adapter = null; _cancellationTokenSource?.Cancel(); _cancellationTokenSource = null; }
public static Task SendPacketsAsync(this IMqttCommunicationAdapter adapter, TimeSpan timeout, CancellationToken cancellationToken, params MqttBasePacket[] packets) { return(adapter.SendPacketsAsync(timeout, cancellationToken, packets)); }
private async Task SendPendingPublishPacketsAsync(CancellationToken cancellationToken, IMqttCommunicationAdapter adapter) { var consumable = _pendingPublishPackets.GetConsumingEnumerable(); while (!cancellationToken.IsCancellationRequested) { if (_pendingPublishPackets.Count == 0) { await Task.Delay(TimeSpan.FromMilliseconds(5), cancellationToken).ConfigureAwait(false); continue; } var packets = consumable.Take(_pendingPublishPackets.Count).ToList(); try { await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, packets).ConfigureAwait(false); } catch (MqttCommunicationException exception) { MqttTrace.Warning(nameof(MqttClientMessageQueue), exception, "Sending publish packet failed."); foreach (var publishPacket in packets) { publishPacket.Dup = true; _pendingPublishPackets.Add(publishPacket, cancellationToken); } } catch (Exception exception) { MqttTrace.Error(nameof(MqttClientMessageQueue), exception, "Sending publish packet failed."); foreach (var publishPacket in packets) { publishPacket.Dup = true; _pendingPublishPackets.Add(publishPacket, cancellationToken); } } } }
public MqttServerAdapterClientAcceptedEventArgs(IMqttCommunicationAdapter client) { Client = client ?? throw new ArgumentNullException(nameof(client)); }
public MqttClientConnectedEventArgs(string identifier, IMqttCommunicationAdapter clientAdapter) { Identifier = identifier ?? throw new ArgumentNullException(nameof(identifier)); ClientAdapter = clientAdapter ?? throw new ArgumentNullException(nameof(clientAdapter)); }
private void OnClientAccepted(IMqttCommunicationAdapter adapter) { Task.Run(() => _clientSessionsManager.RunClientSessionAsync(adapter), _cancellationTokenSource.Token); }