Esempio n. 1
0
        private Task ProcessReceivedPacketAsync(IMqttChannelAdapter adapter, MqttBasePacket packet, CancellationToken cancellationToken)
        {
            if (packet is MqttPublishPacket publishPacket)
            {
                return(HandleIncomingPublishPacketAsync(adapter, publishPacket, cancellationToken));
            }

            if (packet is MqttPingReqPacket)
            {
                return(adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new[] { new MqttPingRespPacket() }));
            }

            if (packet is MqttPubRelPacket pubRelPacket)
            {
                return(HandleIncomingPubRelPacketAsync(adapter, pubRelPacket, cancellationToken));
            }

            if (packet is MqttPubRecPacket pubRecPacket)
            {
                var responsePacket = new MqttPubRelPacket
                {
                    PacketIdentifier = pubRecPacket.PacketIdentifier
                };

                return(adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new[] { responsePacket }));
            }

            if (packet is MqttPubAckPacket || packet is MqttPubCompPacket)
            {
                // Discard message.
                return(Task.FromResult(0));
            }

            if (packet is MqttSubscribePacket subscribePacket)
            {
                return(HandleIncomingSubscribePacketAsync(adapter, subscribePacket, cancellationToken));
            }

            if (packet is MqttUnsubscribePacket unsubscribePacket)
            {
                return(HandleIncomingUnsubscribePacketAsync(adapter, unsubscribePacket, cancellationToken));
            }

            if (packet is MqttDisconnectPacket)
            {
                return(StopAsync(true));
            }

            if (packet is MqttConnectPacket)
            {
                return(StopAsync());
            }

            _logger.Warning <MqttClientSession>("Client '{0}': Received not supported packet ({1}). Closing connection.", ClientId, packet);
            return(StopAsync());
        }
Esempio n. 2
0
        private async Task HandleIncomingSubscribePacketAsync(IMqttChannelAdapter adapter, MqttSubscribePacket subscribePacket, CancellationToken cancellationToken)
        {
            var subscribeResult = await _subscriptionsManager.SubscribeAsync(subscribePacket, ClientId);

            await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, subscribeResult.ResponsePacket).ConfigureAwait(false);

            if (subscribeResult.CloseConnection)
            {
                await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new MqttDisconnectPacket()).ConfigureAwait(false);
                await StopAsync().ConfigureAwait(false);
            }

            await EnqueueSubscribedRetainedMessagesAsync(subscribePacket).ConfigureAwait(false);
        }
Esempio n. 3
0
        public async Task PublishAsync(IEnumerable <MqttApplicationMessage> applicationMessages)
        {
            ThrowIfNotConnected();

            var publishPackets = applicationMessages.Select(m => m.ToPublishPacket());

            foreach (var qosGroup in publishPackets.GroupBy(p => p.QualityOfServiceLevel))
            {
                var qosPackets = qosGroup.ToArray();
                switch (qosGroup.Key)
                {
                case MqttQualityOfServiceLevel.AtMostOnce:
                {
                    // No packet identifier is used for QoS 0 [3.3.2.2 Packet Identifier]
                    await _adapter.SendPacketsAsync(_options.CommunicationTimeout, _cancellationTokenSource.Token, qosPackets).ConfigureAwait(false);

                    break;
                }

                case MqttQualityOfServiceLevel.AtLeastOnce:
                {
                    foreach (var publishPacket in qosPackets)
                    {
                        publishPacket.PacketIdentifier = GetNewPacketIdentifier();
                        await SendAndReceiveAsync <MqttPubAckPacket>(publishPacket).ConfigureAwait(false);
                    }

                    break;
                }

                case MqttQualityOfServiceLevel.ExactlyOnce:
                {
                    foreach (var publishPacket in qosPackets)
                    {
                        publishPacket.PacketIdentifier = GetNewPacketIdentifier();
                        var pubRecPacket = await SendAndReceiveAsync <MqttPubRecPacket>(publishPacket).ConfigureAwait(false);
                        await SendAndReceiveAsync <MqttPubCompPacket>(pubRecPacket.CreateResponse <MqttPubRelPacket>()).ConfigureAwait(false);
                    }

                    break;
                }

                default:
                {
                    throw new InvalidOperationException();
                }
                }
            }
        }
Esempio n. 4
0
        private Task HandleIncomingPubRelPacketAsync(IMqttChannelAdapter adapter, MqttPubRelPacket pubRelPacket, CancellationToken cancellationToken)
        {
            var response = new MqttPubCompPacket {
                PacketIdentifier = pubRelPacket.PacketIdentifier
            };

            return(adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, response));
        }
Esempio n. 5
0
        private async Task HandleIncomingPublishPacketWithQoS1(IMqttChannelAdapter adapter, MqttApplicationMessage applicationMessage, MqttPublishPacket publishPacket, CancellationToken cancellationToken)
        {
            await _sessionsManager.DispatchApplicationMessageAsync(this, applicationMessage).ConfigureAwait(false);

            var response = new MqttPubAckPacket {
                PacketIdentifier = publishPacket.PacketIdentifier
            };
            await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, response).ConfigureAwait(false);
        }
Esempio n. 6
0
        public static Task SendPacketsAsync(this IMqttChannelAdapter adapter, TimeSpan timeout, CancellationToken cancellationToken, params MqttBasePacket[] packets)
        {
            if (adapter == null)
            {
                throw new ArgumentNullException(nameof(adapter));
            }

            return(adapter.SendPacketsAsync(timeout, cancellationToken, packets));
        }
        private async Task SendNextQueuedPacketAsync(IMqttChannelAdapter adapter, CancellationToken cancellationToken)
        {
            MqttBasePacket packet = null;

            try
            {
                await _queueWaitSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);

                if (!_queue.TryDequeue(out packet))
                {
                    throw new InvalidOperationException(); // should not happen
                }

                if (cancellationToken.IsCancellationRequested)
                {
                    return;
                }

                await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new[] { packet }).ConfigureAwait(false);

                _logger.Trace <MqttClientPendingMessagesQueue>("Enqueued packet sent (ClientId: {0}).", _clientSession.ClientId);
            }
            catch (Exception exception)
            {
                if (exception is MqttCommunicationTimedOutException)
                {
                    _logger.Warning <MqttClientPendingMessagesQueue>(exception, "Sending publish packet failed due to timeout (ClientId: {0}).", _clientSession.ClientId);
                }
                else if (exception is MqttCommunicationException)
                {
                    _logger.Warning <MqttClientPendingMessagesQueue>(exception, "Sending publish packet failed due to communication exception (ClientId: {0}).", _clientSession.ClientId);
                }
                else if (exception is OperationCanceledException)
                {
                }
                else
                {
                    _logger.Error <MqttClientPendingMessagesQueue>(exception, "Sending publish packet failed (ClientId: {0}).", _clientSession.ClientId);
                }

                if (packet is MqttPublishPacket publishPacket)
                {
                    if (publishPacket.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
                    {
                        publishPacket.Dup = true;
                        _queue.Enqueue(packet);
                        _queueWaitSemaphore.Release();
                    }
                }

                if (!cancellationToken.IsCancellationRequested)
                {
                    await _clientSession.StopAsync().ConfigureAwait(false);
                }
            }
        }
Esempio n. 8
0
        private async Task HandleIncomingPublishPacketWithQoS2(IMqttChannelAdapter adapter, MqttApplicationMessage applicationMessage, MqttPublishPacket publishPacket, CancellationToken cancellationToken)
        {
            // QoS 2 is implement as method "B" [4.3.3 QoS 2: Exactly once delivery]
            await _sessionsManager.DispatchApplicationMessageAsync(this, applicationMessage).ConfigureAwait(false);

            var response = new MqttPubRecPacket {
                PacketIdentifier = publishPacket.PacketIdentifier
            };
            await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, response).ConfigureAwait(false);
        }
Esempio n. 9
0
        private async Task HandleIncomingPublishPacketAsync(IMqttChannelAdapter adapter, MqttPublishPacket publishPacket, CancellationToken cancellationToken)
        {
            var applicationMessage = publishPacket.ToApplicationMessage();

            switch (applicationMessage.QualityOfServiceLevel)
            {
            case MqttQualityOfServiceLevel.AtMostOnce:
            {
                await _sessionsManager.DispatchApplicationMessageAsync(this, applicationMessage);

                return;
            }

            case MqttQualityOfServiceLevel.AtLeastOnce:
            {
                await _sessionsManager.DispatchApplicationMessageAsync(this, applicationMessage);

                await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken,
                                               new MqttPubAckPacket { PacketIdentifier = publishPacket.PacketIdentifier });

                return;
            }

            case MqttQualityOfServiceLevel.ExactlyOnce:
            {
                // QoS 2 is implement as method "B" [4.3.3 QoS 2: Exactly once delivery]
                lock (_unacknowledgedPublishPackets)
                {
                    _unacknowledgedPublishPackets.Add(publishPacket.PacketIdentifier);
                }

                await _sessionsManager.DispatchApplicationMessageAsync(this, applicationMessage);

                await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken,
                                               new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier }).ConfigureAwait(false);

                return;
            }

            default:
                throw new MqttCommunicationException("Received a not supported QoS level.");
            }
        }
Esempio n. 10
0
        private Task HandleIncomingPubRelPacketAsync(IMqttChannelAdapter adapter, MqttPubRelPacket pubRelPacket, CancellationToken cancellationToken)
        {
            lock (_unacknowledgedPublishPackets)
            {
                _unacknowledgedPublishPackets.Remove(pubRelPacket.PacketIdentifier);
            }

            return(adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new MqttPubCompPacket {
                PacketIdentifier = pubRelPacket.PacketIdentifier
            }));
        }
Esempio n. 11
0
        private async Task SendQueuedPacketAsync(IMqttChannelAdapter adapter, CancellationToken cancellationToken)
        {
            MqttBasePacket packet = null;

            try
            {
                packet = _queue.Take(cancellationToken);
                await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, packet).ConfigureAwait(false);

                _logger.Trace <MqttClientPendingMessagesQueue>("Enqueued packet sent (ClientId: {0}).", _session.ClientId);
            }
            catch (Exception exception)
            {
                if (exception is MqttCommunicationTimedOutException)
                {
                    _logger.Warning <MqttClientPendingMessagesQueue>(exception, "Sending publish packet failed due to timeout (ClientId: {0}).", _session.ClientId);
                }
                else if (exception is MqttCommunicationException)
                {
                    _logger.Warning <MqttClientPendingMessagesQueue>(exception, "Sending publish packet failed due to communication exception (ClientId: {0}).", _session.ClientId);
                }
                else if (exception is OperationCanceledException)
                {
                }
                else
                {
                    _logger.Error <MqttClientPendingMessagesQueue>(exception, "Sending publish packet failed (ClientId: {0}).", _session.ClientId);
                }

                if (packet is MqttPublishPacket publishPacket)
                {
                    if (publishPacket.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
                    {
                        publishPacket.Dup = true;
                        _queue.Add(packet, CancellationToken.None);
                    }
                }

                await _session.StopAsync();
            }
        }
Esempio n. 12
0
        public async Task RunSessionAsync(IMqttChannelAdapter clientAdapter, CancellationToken cancellationToken)
        {
            var clientId = string.Empty;
            MqttClientSession clientSession = null;

            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 result = await GetOrCreateClientSessionAsync(connectPacket).ConfigureAwait(false);

                clientSession = result.Session;

                await clientAdapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new MqttConnAckPacket
                {
                    ConnectReturnCode = connectReturnCode,
                    IsSessionPresent  = result.IsExistingSession
                }).ConfigureAwait(false);

                ClientConnectedCallback?.Invoke(new ConnectedMqttClient
                {
                    ClientId        = clientId,
                    ProtocolVersion = clientAdapter.PacketSerializer.ProtocolVersion
                });

                await clientSession.RunAsync(connectPacket, clientAdapter).ConfigureAwait(false);
            }
            catch (Exception exception)
            {
                _logger.Error <MqttClientSessionsManager>(exception, exception.Message);
            }
            finally
            {
                try
                {
                    await clientAdapter.DisconnectAsync(_options.DefaultCommunicationTimeout).ConfigureAwait(false);
                }
                catch (Exception)
                {
                    // ignored
                }

                ClientDisconnectedCallback?.Invoke(new ConnectedMqttClient
                {
                    ClientId                   = clientId,
                    ProtocolVersion            = clientAdapter.PacketSerializer.ProtocolVersion,
                    PendingApplicationMessages = clientSession?.PendingMessagesQueue.Count ?? 0
                });
            }
        }
Esempio n. 13
0
        private async Task HandleIncomingUnsubscribePacketAsync(IMqttChannelAdapter adapter, MqttUnsubscribePacket unsubscribePacket, CancellationToken cancellationToken)
        {
            var unsubscribeResult = await _subscriptionsManager.UnsubscribeAsync(unsubscribePacket);

            await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, unsubscribeResult);
        }
Esempio n. 14
0
 private Task SendAsync(params MqttBasePacket[] packets)
 {
     _sendTracker.Restart();
     return(_adapter.SendPacketsAsync(_options.CommunicationTimeout, _cancellationTokenSource.Token, packets));
 }