public async Task LoadMessagesAsync() { if (_options.Storage == null) { return; } try { var retainedMessages = await _options.Storage.LoadRetainedMessagesAsync(); lock (_retainedMessages) { _retainedMessages.Clear(); foreach (var retainedMessage in retainedMessages) { _retainedMessages[retainedMessage.Topic] = retainedMessage.ToPublishPacket(); } } } catch (Exception exception) { MqttNetTrace.Error(nameof(MqttClientRetainedMessagesManager), exception, "Unhandled exception while loading retained messages."); } }
private async Task DisconnectInternalAsync() { var cts = _cancellationTokenSource; if (cts == null || cts.IsCancellationRequested) { return; } cts.Cancel(false); cts.Dispose(); _cancellationTokenSource = null; try { await _adapter.DisconnectAsync(_options.DefaultCommunicationTimeout).ConfigureAwait(false); MqttNetTrace.Information(nameof(MqttClient), "Disconnected from adapter."); } catch (Exception exception) { MqttNetTrace.Warning(nameof(MqttClient), exception, "Error while disconnecting from adapter."); } finally { Disconnected?.Invoke(this, EventArgs.Empty); } }
private async Task SendPendingPublishPacketAsync(IMqttCommunicationAdapter adapter, CancellationToken cancellationToken) { var packet = _pendingPublishPackets.Take(cancellationToken); try { await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, packet).ConfigureAwait(false); } catch (Exception exception) { if (exception is MqttCommunicationTimedOutException) { MqttNetTrace.Warning(nameof(MqttClientPendingMessagesQueue), exception, "Sending publish packet failed due to timeout."); } else if (exception is MqttCommunicationException) { MqttNetTrace.Warning(nameof(MqttClientPendingMessagesQueue), exception, "Sending publish packet failed due to communication exception."); } if (exception is OperationCanceledException) { } else { MqttNetTrace.Error(nameof(MqttClientPendingMessagesQueue), exception, "Sending publish packet failed."); } if (packet.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce) { packet.Dup = true; _pendingPublishPackets.Add(packet, cancellationToken); } _session.Stop(); } }
public async Task RunAsync(MqttApplicationMessage willMessage, IMqttCommunicationAdapter adapter) { if (adapter == null) { throw new ArgumentNullException(nameof(adapter)); } _willMessage = willMessage; try { _adapter = adapter; _cancellationTokenSource = new CancellationTokenSource(); _pendingMessagesQueue.Start(adapter, _cancellationTokenSource.Token); await ReceivePacketsAsync(adapter, _cancellationTokenSource.Token); } catch (OperationCanceledException) { } catch (MqttCommunicationException exception) { MqttNetTrace.Warning(nameof(MqttClientSession), exception, "Client '{0}': Communication exception while processing client packets.", ClientId); } catch (Exception exception) { MqttNetTrace.Error(nameof(MqttClientSession), exception, "Client '{0}': Unhandled exception while processing client packets.", ClientId); } }
private void FireApplicationMessageReceivedEvent(MqttPublishPacket publishPacket) { try { var applicationMessage = publishPacket.ToApplicationMessage(); ApplicationMessageReceived?.Invoke(this, new MqttApplicationMessageReceivedEventArgs(applicationMessage)); } catch (Exception exception) { MqttNetTrace.Error(nameof(MqttClient), exception, "Unhandled exception while handling application message."); } }
private void AcceptDefaultEndpointConnectionsAsync(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args) { try { var clientAdapter = new MqttChannelCommunicationAdapter(new MqttTcpChannel(args.Socket), new MqttPacketSerializer()); ClientAccepted?.Invoke(clientAdapter); } catch (Exception exception) { MqttNetTrace.Error(nameof(MqttServerAdapter), exception, "Error while accepting connection at default endpoint."); } }
public async Task SendPacketsAsync(TimeSpan timeout, CancellationToken cancellationToken, IEnumerable <MqttBasePacket> packets) { await _semaphore.WaitAsync(cancellationToken); try { foreach (var packet in packets) { if (packet == null) { continue; } MqttNetTrace.Information(nameof(MqttChannelCommunicationAdapter), "TX >>> {0} [Timeout={1}]", packet, timeout); var writeBuffer = PacketSerializer.Serialize(packet); await _channel.SendStream.WriteAsync(writeBuffer, 0, writeBuffer.Length, cancellationToken).ConfigureAwait(false); } if (timeout > TimeSpan.Zero) { await _channel.SendStream.FlushAsync(cancellationToken).TimeoutAfter(timeout).ConfigureAwait(false); } else { await _channel.SendStream.FlushAsync(cancellationToken).ConfigureAwait(false); } } catch (TaskCanceledException) { throw; } catch (OperationCanceledException) { throw; } catch (MqttCommunicationTimedOutException) { throw; } catch (MqttCommunicationException) { throw; } catch (Exception exception) { throw new MqttCommunicationException(exception); } finally { _semaphore.Release(); } }
public async Task <MqttBasePacket> ReceivePacketAsync(TimeSpan timeout, CancellationToken cancellationToken) { try { ReceivedMqttPacket receivedMqttPacket; if (timeout > TimeSpan.Zero) { receivedMqttPacket = await ReceiveAsync(_channel.RawReceiveStream, cancellationToken).TimeoutAfter(timeout).ConfigureAwait(false); } else { receivedMqttPacket = await ReceiveAsync(_channel.ReceiveStream, cancellationToken).ConfigureAwait(false); } if (cancellationToken.IsCancellationRequested) { throw new TaskCanceledException(); } var packet = PacketSerializer.Deserialize(receivedMqttPacket); if (packet == null) { throw new MqttProtocolViolationException("Received malformed packet."); } MqttNetTrace.Information(nameof(MqttChannelCommunicationAdapter), "RX <<< {0}", packet); return(packet); } catch (TaskCanceledException) { throw; } catch (OperationCanceledException) { throw; } catch (MqttCommunicationTimedOutException) { throw; } catch (MqttCommunicationException) { throw; } catch (Exception exception) { throw new MqttCommunicationException(exception); } }
public void EnqueuePublishPacket(MqttPublishPacket publishPacket) { if (publishPacket == null) { throw new ArgumentNullException(nameof(publishPacket)); } if (!_subscriptionsManager.IsSubscribed(publishPacket)) { return; } _pendingMessagesQueue.Enqueue(publishPacket); MqttNetTrace.Verbose(nameof(MqttClientSession), "Client '{0}': Enqueued pending publish packet.", ClientId); }
public void Stop() { if (_willMessage != null) { _mqttClientSessionsManager.DispatchPublishPacket(this, _willMessage.ToPublishPacket()); } _cancellationTokenSource?.Cancel(false); _cancellationTokenSource?.Dispose(); _cancellationTokenSource = null; _adapter = null; MqttNetTrace.Information(nameof(MqttClientSession), "Client '{0}': Disconnected.", ClientId); }
public async Task StopAsync() { _cancellationTokenSource?.Cancel(false); _cancellationTokenSource?.Dispose(); _cancellationTokenSource = null; foreach (var adapter in _adapters) { adapter.ClientAccepted -= OnClientAccepted; await adapter.StopAsync(); } _clientSessionsManager.Clear(); MqttNetTrace.Information(nameof(MqttServer), "Stopped."); }
private async Task SendPendingPublishPacketsAsync(IMqttCommunicationAdapter adapter, CancellationToken cancellationToken) { try { while (!cancellationToken.IsCancellationRequested) { await SendPendingPublishPacketAsync(adapter, cancellationToken); } } catch (OperationCanceledException) { } catch (Exception exception) { MqttNetTrace.Error(nameof(MqttClientPendingMessagesQueue), exception, "Unhandled exception while sending pending publish packets."); } }
public async Task StartAsync() { if (_cancellationTokenSource != null) { throw new InvalidOperationException("The MQTT server is already started."); } _cancellationTokenSource = new CancellationTokenSource(); await _clientSessionsManager.RetainedMessagesManager.LoadMessagesAsync(); foreach (var adapter in _adapters) { adapter.ClientAccepted += OnClientAccepted; await adapter.StartAsync(_options); } MqttNetTrace.Information(nameof(MqttServer), "Started."); }
private async Task ReceivePackets(CancellationToken cancellationToken) { MqttNetTrace.Information(nameof(MqttClient), "Start receiving packets."); try { while (!cancellationToken.IsCancellationRequested) { _isReceivingPackets = true; var packet = await _adapter.ReceivePacketAsync(TimeSpan.Zero, cancellationToken).ConfigureAwait(false); if (cancellationToken.IsCancellationRequested) { return; } StartProcessReceivedPacket(packet, cancellationToken); } } catch (OperationCanceledException) { } catch (MqttCommunicationException exception) { if (cancellationToken.IsCancellationRequested) { return; } MqttNetTrace.Warning(nameof(MqttClient), exception, "MQTT communication exception while receiving packets."); await DisconnectInternalAsync().ConfigureAwait(false); } catch (Exception exception) { MqttNetTrace.Error(nameof(MqttClient), exception, "Unhandled exception while receiving packets."); await DisconnectInternalAsync().ConfigureAwait(false); } finally { MqttNetTrace.Information(nameof(MqttClient), "Stopped receiving packets."); } }
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; } }
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 void DispatchPublishPacket(MqttClientSession senderClientSession, MqttPublishPacket publishPacket) { try { var eventArgs = new MqttApplicationMessageReceivedEventArgs(senderClientSession?.ClientId, publishPacket.ToApplicationMessage()); ApplicationMessageReceived?.Invoke(this, eventArgs); } catch (Exception exception) { MqttNetTrace.Error(nameof(MqttClientSessionsManager), exception, "Error while processing application message"); } lock (_clientSessions) { foreach (var clientSession in _clientSessions.Values.ToList()) { clientSession.EnqueuePublishPacket(publishPacket); } } }
private async Task AcceptDefaultEndpointConnectionsAsync(CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { try { var clientSocket = await _defaultEndpointSocket.AcceptAsync().ConfigureAwait(false); var clientAdapter = new MqttChannelCommunicationAdapter(new MqttTcpChannel(clientSocket, null), new MqttPacketSerializer()); ClientAccepted?.Invoke(clientAdapter); } catch (Exception exception) { MqttNetTrace.Error(nameof(MqttServerAdapter), exception, "Error while accepting connection at default endpoint."); //excessive CPU consumed if in endless loop of socket errors await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false); } } }
private async Task SendKeepAliveMessagesAsync(CancellationToken cancellationToken) { MqttNetTrace.Information(nameof(MqttClient), "Start sending keep alive packets."); try { while (!cancellationToken.IsCancellationRequested) { await Task.Delay(_options.KeepAlivePeriod, cancellationToken).ConfigureAwait(false); if (cancellationToken.IsCancellationRequested) { return; } await SendAndReceiveAsync <MqttPingRespPacket>(new MqttPingReqPacket()).ConfigureAwait(false); } } catch (OperationCanceledException) { } catch (MqttCommunicationException exception) { if (cancellationToken.IsCancellationRequested) { return; } MqttNetTrace.Warning(nameof(MqttClient), exception, "MQTT communication exception while sending/receiving keep alive packets."); await DisconnectInternalAsync().ConfigureAwait(false); } catch (Exception exception) { MqttNetTrace.Warning(nameof(MqttClient), exception, "Unhandled exception while sending/receiving keep alive packets."); await DisconnectInternalAsync().ConfigureAwait(false); } finally { MqttNetTrace.Information(nameof(MqttClient), "Stopped sending keep alive packets."); } }
private async Task ProcessReceivedPacketAsync(MqttBasePacket packet) { try { MqttNetTrace.Information(nameof(MqttClient), "Received <<< {0}", packet); if (packet is MqttPingReqPacket) { await SendAsync(new MqttPingRespPacket()); return; } if (packet is MqttDisconnectPacket) { await DisconnectAsync(); return; } if (packet is MqttPublishPacket publishPacket) { await ProcessReceivedPublishPacket(publishPacket); return; } if (packet is MqttPubRelPacket pubRelPacket) { await ProcessReceivedPubRelPacket(pubRelPacket); return; } _packetDispatcher.Dispatch(packet); } catch (Exception exception) { MqttNetTrace.Error(nameof(MqttClient), exception, "Unhandled exception while processing received packet."); } }
public async Task HandleMessageAsync(string clientId, MqttPublishPacket publishPacket) { if (publishPacket == null) { throw new ArgumentNullException(nameof(publishPacket)); } List <MqttPublishPacket> allRetainedMessages; lock (_retainedMessages) { if (publishPacket.Payload?.Any() == false) { _retainedMessages.Remove(publishPacket.Topic); MqttNetTrace.Information(nameof(MqttClientRetainedMessagesManager), "Client '{0}' cleared retained message for topic '{1}'.", clientId, publishPacket.Topic); } else { _retainedMessages[publishPacket.Topic] = publishPacket; MqttNetTrace.Information(nameof(MqttClientRetainedMessagesManager), "Client '{0}' updated retained message for topic '{1}'.", clientId, publishPacket.Topic); } allRetainedMessages = new List <MqttPublishPacket>(_retainedMessages.Values); } try { // ReSharper disable once UseNullPropagation if (_options.Storage != null) { await _options.Storage.SaveRetainedMessagesAsync(allRetainedMessages.Select(p => p.ToApplicationMessage()).ToList()); } } catch (Exception exception) { MqttNetTrace.Error(nameof(MqttClientRetainedMessagesManager), exception, "Unhandled exception while saving retained messages."); } }
private async Task AcceptTlsEndpointConnectionsAsync(CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { try { var clientSocket = await _tlsEndpointSocket.AcceptAsync().ConfigureAwait(false); var sslStream = new SslStream(new NetworkStream(clientSocket)); await sslStream.AuthenticateAsServerAsync(_tlsCertificate, false, SslProtocols.Tls12, false).ConfigureAwait(false); var clientAdapter = new MqttChannelCommunicationAdapter(new MqttTcpChannel(clientSocket, sslStream), new MqttPacketSerializer()); ClientAccepted?.Invoke(clientAdapter); } catch (Exception exception) { MqttNetTrace.Error(nameof(MqttServerAdapter), exception, "Error while accepting connection at TLS endpoint."); //excessive CPU consumed if in endless loop of socket errors await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false); } } }
public async Task <MqttBasePacket> WaitForPacketAsync(MqttBasePacket request, Type responseType, TimeSpan timeout) { if (request == null) { throw new ArgumentNullException(nameof(request)); } var packetAwaiter = AddPacketAwaiter(request, responseType); try { return(await packetAwaiter.Task.TimeoutAfter(timeout).ConfigureAwait(false)); } catch (MqttCommunicationTimedOutException) { MqttNetTrace.Warning(nameof(MqttPacketDispatcher), "Timeout while waiting for packet of type '{0}'.", responseType.Name); throw; } finally { RemovePacketAwaiter(request, responseType); } }
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).ConfigureAwait(false); } } catch (OperationCanceledException) { } catch (MqttCommunicationException exception) { MqttNetTrace.Warning(nameof(MqttClientSession), exception, "Client '{0}': Communication exception while processing client packets.", ClientId); Stop(); } catch (Exception exception) { MqttNetTrace.Error(nameof(MqttClientSession), exception, "Client '{0}': Unhandled exception while processing client packets.", ClientId); Stop(); } }
private GetOrCreateClientSessionResult GetOrCreateClientSession(MqttConnectPacket connectPacket) { lock (_clientSessions) { var isSessionPresent = _clientSessions.TryGetValue(connectPacket.ClientId, out var clientSession); if (isSessionPresent) { if (connectPacket.CleanSession) { _clientSessions.Remove(connectPacket.ClientId); clientSession.Dispose(); clientSession = null; MqttNetTrace.Verbose(nameof(MqttClientSessionsManager), "Disposed existing session of client '{0}'.", connectPacket.ClientId); } else { MqttNetTrace.Verbose(nameof(MqttClientSessionsManager), "Reusing existing session of client '{0}'.", connectPacket.ClientId); } } var isExistingSession = true; if (clientSession == null) { isExistingSession = false; clientSession = new MqttClientSession(connectPacket.ClientId, _options, this); _clientSessions[connectPacket.ClientId] = clientSession; MqttNetTrace.Verbose(nameof(MqttClientSessionsManager), "Created a new session for client '{0}'.", connectPacket.ClientId); } return(new GetOrCreateClientSessionResult { IsExistingSession = isExistingSession, Session = clientSession }); } }
public async Task RunClientSessionAsync(IMqttCommunicationAdapter clientAdapter) { var clientId = string.Empty; try { if (!(await clientAdapter.ReceivePacketAsync(_options.DefaultCommunicationTimeout, CancellationToken.None).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.None, new MqttConnAckPacket { ConnectReturnCode = connectReturnCode }).ConfigureAwait(false); return; } var clientSession = GetOrCreateClientSession(connectPacket); await clientAdapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, CancellationToken.None, new MqttConnAckPacket { ConnectReturnCode = connectReturnCode, IsSessionPresent = clientSession.IsExistingSession }).ConfigureAwait(false); ClientConnected?.Invoke(this, new MqttClientConnectedEventArgs(new ConnectedMqttClient { ClientId = clientId, ProtocolVersion = clientAdapter.PacketSerializer.ProtocolVersion })); await clientSession.Session.RunAsync(connectPacket.WillMessage, clientAdapter).ConfigureAwait(false); } catch (Exception exception) { MqttNetTrace.Error(nameof(MqttServer), 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 OnClientDisconnected(object sender, MqttClientDisconnectedEventArgs eventArgs) { MqttNetTrace.Information(nameof(MqttServer), "Client '{0}': Disconnected.", eventArgs.Client.ClientId); ClientDisconnected?.Invoke(this, eventArgs); }