コード例 #1
0
        async Task <MqttPublishPacket> CreatePublishPacket(MqttQueuedApplicationMessage queuedApplicationMessage)
        {
            var publishPacket = _channelAdapter.PacketFormatterAdapter.DataConverter.CreatePublishPacket(queuedApplicationMessage.ApplicationMessage);

            publishPacket.QualityOfServiceLevel = queuedApplicationMessage.SubscriptionQualityOfServiceLevel;

            if (_channelAdapter.PacketFormatterAdapter.ProtocolVersion == MqttProtocolVersion.V500)
            {
                publishPacket.Properties.SubscriptionIdentifiers = queuedApplicationMessage.SubscriptionIdentifiers;
            }

            // Set the retain flag to true according to [MQTT-3.3.1-8] and [MQTT-3.3.1-9].
            publishPacket.Retain = queuedApplicationMessage.IsRetainedMessage;

            publishPacket = await InvokeClientMessageQueueInterceptor(publishPacket, queuedApplicationMessage).ConfigureAwait(false);

            if (publishPacket == null)
            {
                // The interceptor has decided that the message is not relevant and will be fully ignored.
                return(null);
            }

            if (publishPacket.QualityOfServiceLevel > 0)
            {
                publishPacket.PacketIdentifier = _packetIdentifierProvider.GetNextPacketIdentifier();
            }

            return(publishPacket);
        }
コード例 #2
0
        public void Enqueue(MqttQueuedApplicationMessage queuedApplicationMessage)
        {
            if (queuedApplicationMessage == null)
            {
                throw new ArgumentNullException(nameof(queuedApplicationMessage));
            }

            lock (_messageQueue)
            {
                if (_messageQueue.Count >= _options.MaxPendingMessagesPerClient)
                {
                    if (_options.PendingMessagesOverflowStrategy == MqttPendingMessagesOverflowStrategy.DropNewMessage)
                    {
                        return;
                    }

                    if (_options.PendingMessagesOverflowStrategy == MqttPendingMessagesOverflowStrategy.DropOldestQueuedMessage)
                    {
                        _messageQueue.TryDequeue();
                    }
                }

                _messageQueue.Enqueue(queuedApplicationMessage);
            }
        }
コード例 #3
0
        static void FilterRetainedApplicationMessages(IList <MqttApplicationMessage> retainedApplicationMessages, Subscription subscription, SubscribeResult subscribeResult)
        {
            for (var i = retainedApplicationMessages.Count - 1; i >= 0; i--)
            {
                var retainedApplicationMessage = retainedApplicationMessages[i];
                if (retainedApplicationMessage == null)
                {
                    continue;
                }

                if (subscription.RetainHandling == MqttRetainHandling.DoNotSendOnSubscribe)
                {
                    // This is a MQTT V5+ feature.
                    continue;
                }

                if (subscription.RetainHandling == MqttRetainHandling.SendAtSubscribeIfNewSubscriptionOnly && !subscription.IsNewSubscription)
                {
                    // This is a MQTT V5+ feature.
                    continue;
                }

                if (!MqttTopicFilterComparer.IsMatch(retainedApplicationMessage.Topic, subscription.Topic))
                {
                    continue;
                }

                var queuedApplicationMessage = new MqttQueuedApplicationMessage
                {
                    ApplicationMessage = retainedApplicationMessage,
                    IsRetainedMessage  = true,
                    SubscriptionQualityOfServiceLevel = subscription.QualityOfServiceLevel
                };

                if (subscription.Identifier > 0)
                {
                    queuedApplicationMessage.SubscriptionIdentifiers = new List <uint> {
                        subscription.Identifier
                    };
                }

                subscribeResult.RetainedApplicationMessages.Add(queuedApplicationMessage);

                retainedApplicationMessages[i] = null;
            }
        }
コード例 #4
0
        async Task TryProcessNextQueuedApplicationMessage()
        {
            try
            {
                MqttPendingApplicationMessage pendingApplicationMessage;
                try
                {
                    pendingApplicationMessage = _messageQueue.Take();
                }
                catch (ArgumentNullException)
                {
                    return;
                }
                catch (ObjectDisposedException)
                {
                    return;
                }

                var clientConnection   = pendingApplicationMessage.Sender;
                var senderClientId     = clientConnection?.ClientId ?? _options.ClientId;
                var applicationMessage = pendingApplicationMessage.ApplicationMessage;

                var interceptor = _options.ApplicationMessageInterceptor;
                if (interceptor != null)
                {
                    var interceptorContext =
                        await InterceptApplicationMessageAsync(interceptor, clientConnection, applicationMessage)
                        .ConfigureAwait(false);

                    if (interceptorContext != null)
                    {
                        if (interceptorContext.CloseConnection)
                        {
                            if (clientConnection != null)
                            {
                                await clientConnection.StopAsync(MqttClientDisconnectReason.NormalDisconnection)
                                .ConfigureAwait(false);
                            }
                        }

                        if (interceptorContext.ApplicationMessage == null || !interceptorContext.AcceptPublish)
                        {
                            return;
                        }

                        applicationMessage = interceptorContext.ApplicationMessage;
                    }
                }

                await _eventDispatcher.SafeNotifyApplicationMessageReceivedAsync(senderClientId, applicationMessage)
                .ConfigureAwait(false);

                if (applicationMessage.Retain)
                {
                    await _retainedMessagesManager.HandleMessageAsync(senderClientId, applicationMessage)
                    .ConfigureAwait(false);
                }

                var deliveryCount = 0;
                List <MqttClientSession> sessions;
                lock (_clientSessions)
                {
                    sessions = _clientSessions.Values.ToList();
                }

                foreach (var clientSession in sessions)
                {
                    var checkSubscriptionsResult = clientSession.SubscriptionsManager.CheckSubscriptions(
                        applicationMessage.Topic,
                        applicationMessage.QualityOfServiceLevel,
                        senderClientId);

                    if (!checkSubscriptionsResult.IsSubscribed)
                    {
                        continue;
                    }

                    _logger.Verbose("Client '{0}': Queued application message with topic '{1}'.",
                                    clientSession.ClientId, applicationMessage.Topic);

                    var queuedApplicationMessage = new MqttQueuedApplicationMessage
                    {
                        ApplicationMessage = applicationMessage,
                        SubscriptionQualityOfServiceLevel = checkSubscriptionsResult.QualityOfServiceLevel,
                        SubscriptionIdentifiers           = checkSubscriptionsResult.SubscriptionIdentifiers
                    };

                    if (checkSubscriptionsResult.RetainAsPublished)
                    {
                        // Transfer the original retain state from the publisher.
                        // This is a MQTTv5 feature.
                        queuedApplicationMessage.IsRetainedMessage = applicationMessage.Retain;
                    }

                    clientSession.ApplicationMessagesQueue.Enqueue(queuedApplicationMessage);
                    deliveryCount++;
                }

                if (deliveryCount == 0)
                {
                    var undeliveredMessageInterceptor = _options.UndeliveredMessageInterceptor;
                    if (undeliveredMessageInterceptor == null)
                    {
                        return;
                    }

                    // The delegate signature is the same as for regular message interceptor. So the call is fine and just uses a different interceptor.
                    await InterceptApplicationMessageAsync(undeliveredMessageInterceptor, clientConnection,
                                                           applicationMessage).ConfigureAwait(false);
                }
            }
            catch (Exception exception)
            {
                _logger.Error(exception, "Unhandled exception while processing next queued application message.");
            }
        }
コード例 #5
0
        async Task <MqttPublishPacket> InvokeClientMessageQueueInterceptor(MqttPublishPacket publishPacket, MqttQueuedApplicationMessage queuedApplicationMessage)
        {
            if (_serverOptions.ClientMessageQueueInterceptor == null)
            {
                return(publishPacket);
            }

            var context = new MqttClientMessageQueueInterceptorContext
            {
                SenderClientId     = queuedApplicationMessage.SenderClientId,
                ReceiverClientId   = ClientId,
                ApplicationMessage = queuedApplicationMessage.ApplicationMessage,
                SubscriptionQualityOfServiceLevel = queuedApplicationMessage.SubscriptionQualityOfServiceLevel
            };

            if (_serverOptions.ClientMessageQueueInterceptor != null)
            {
                await _serverOptions.ClientMessageQueueInterceptor.InterceptClientMessageQueueEnqueueAsync(context).ConfigureAwait(false);
            }

            if (!context.AcceptEnqueue || context.ApplicationMessage == null)
            {
                return(null);
            }

            publishPacket.Topic   = context.ApplicationMessage.Topic;
            publishPacket.Payload = context.ApplicationMessage.Payload;
            publishPacket.QualityOfServiceLevel = context.SubscriptionQualityOfServiceLevel;

            return(publishPacket);
        }
コード例 #6
0
        async Task SendPacketsLoop(CancellationToken cancellationToken)
        {
            MqttQueuedApplicationMessage queuedApplicationMessage = null;
            MqttPublishPacket            publishPacket            = null;

            try
            {
                while (!cancellationToken.IsCancellationRequested)
                {
                    queuedApplicationMessage = await Session.ApplicationMessagesQueue.Dequeue(cancellationToken).ConfigureAwait(false);

                    // Also check the cancellation token here because the dequeue is blocking and may take some time.
                    if (cancellationToken.IsCancellationRequested)
                    {
                        return;
                    }

                    if (queuedApplicationMessage == null)
                    {
                        continue;
                    }

                    publishPacket = await CreatePublishPacket(queuedApplicationMessage).ConfigureAwait(false);

                    if (publishPacket == null)
                    {
                        continue;
                    }

                    if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
                    {
                        await SendPacketAsync(publishPacket, cancellationToken).ConfigureAwait(false);
                    }
                    else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce)
                    {
                        using (var awaitable = _packetDispatcher.AddAwaitable <MqttPubAckPacket>(publishPacket.PacketIdentifier))
                        {
                            await SendPacketAsync(publishPacket, cancellationToken).ConfigureAwait(false);

                            await awaitable.WaitOneAsync(_serverOptions.DefaultCommunicationTimeout).ConfigureAwait(false);
                        }
                    }
                    else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce)
                    {
                        using (var awaitableRec = _packetDispatcher.AddAwaitable <MqttPubRecPacket>(publishPacket.PacketIdentifier))
                            using (var awaitableComp = _packetDispatcher.AddAwaitable <MqttPubCompPacket>(publishPacket.PacketIdentifier))
                            {
                                await SendPacketAsync(publishPacket, cancellationToken).ConfigureAwait(false);

                                var pubRecPacket = await awaitableRec.WaitOneAsync(_serverOptions.DefaultCommunicationTimeout).ConfigureAwait(false);

                                var pubRelPacket = _channelAdapter.PacketFormatterAdapter.DataConverter.CreatePubRelPacket(pubRecPacket, MqttApplicationMessageReceivedReasonCode.Success);
                                await SendPacketAsync(pubRelPacket, cancellationToken).ConfigureAwait(false);

                                await awaitableComp.WaitOneAsync(_serverOptions.DefaultCommunicationTimeout).ConfigureAwait(false);
                            }
                    }

                    _logger.Verbose("Client '{0}': Queued application message sent.", ClientId);
                }
            }
            catch (OperationCanceledException)
            {
            }
            catch (Exception exception)
            {
                if (exception is MqttCommunicationTimedOutException)
                {
                    _logger.Warning(exception, "Client '{0}': Sending publish packet failed: Timeout.", ClientId);
                }
                else if (exception is MqttCommunicationException)
                {
                    _logger.Warning(exception, "Client '{0}': Sending publish packet failed: Communication exception.", ClientId);
                }
                else
                {
                    _logger.Error(exception, "Client '{0}': Sending publish packet failed.", ClientId);
                }

                if (publishPacket?.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
                {
                    if (queuedApplicationMessage != null)
                    {
                        queuedApplicationMessage.IsDuplicate = true;
                        Session.ApplicationMessagesQueue.Enqueue(queuedApplicationMessage);
                    }
                }

                StopInternal();
            }
        }