Пример #1
0
        /// <summary>
        /// Enqueue a message into the inflight queue
        /// </summary>
        /// <param name="msg">Message to enqueue</param>
        /// <param name="flow">Message flow (publish, acknowledge)</param>
        private void EnqueueInflight(MqttMsgBase msg, MqttMsgFlow flow)
        {
            // enqueue is needed (or not)
            bool enqueue = true;

            // if it is a PUBLISH message with QoS Level 2
            if ((msg.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE) &&
                (msg.QosLevel == MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE))
            {
                lock (this.inflightQueue)
                {
                    // if it is a PUBLISH message already received (it is in the inflight queue), the publisher
                    // re-sent it because it didn't received the PUBREC. In this case, we have to re-send PUBREC

                    // NOTE : I need to find on message id and flow because the broker could be publish/received
                    //        to/from client and message id could be the same (one tracked by broker and the other by client)
                    MqttMsgContextFinder msgCtxFinder = new MqttMsgContextFinder(((MqttMsgPublish)msg).MessageId, MqttMsgFlow.ToAcknowledge);
                    MqttMsgContext msgCtx = (MqttMsgContext)this.inflightQueue.Get(msgCtxFinder.Find);

                    // the PUBLISH message is alredy in the inflight queue, we don't need to re-enqueue but we need
                    // to change state to re-send PUBREC
                    if (msgCtx != null)
                    {
                        msgCtx.State = MqttMsgState.QueuedQos2;
                        msgCtx.Flow = MqttMsgFlow.ToAcknowledge;
                        enqueue = false;
                    }
                }
            }

            if (enqueue)
            {
                // set a default state
                MqttMsgState state = MqttMsgState.QueuedQos0;

                // based on QoS level, the messages flow between broker and client changes
                switch (msg.QosLevel)
                {
                    // QoS Level 0
                    case MqttMsgBase.QOS_LEVEL_AT_MOST_ONCE:

                        state = MqttMsgState.QueuedQos0;
                        break;

                    // QoS Level 1
                    case MqttMsgBase.QOS_LEVEL_AT_LEAST_ONCE:

                        state = MqttMsgState.QueuedQos1;
                        break;

                    // QoS Level 2
                    case MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE:

                        state = MqttMsgState.QueuedQos2;
                        break;
                }

                // queue message context
                MqttMsgContext msgContext = new MqttMsgContext()
                {
                    Message = msg,
                    State = state,
                    Flow = flow,
                    Attempt = 0
                };

                lock (this.inflightQueue)
                {
                    // enqueue message and unlock send thread
                    this.inflightQueue.Enqueue(msgContext);
                }
            }

            this.inflightWaitHandle.Set();
        }
        private void EnqueueInternal(MqttConnection connection, MqttMsgBase msg)
        {
            // enqueue is needed (or not)
            var enqueue = true;

            // if it is a PUBREL message (for QoS Level 2)
            if (msg.Type == MqttMsgBase.MQTT_MSG_PUBREL_TYPE)
            {
                // if it is a PUBREL but the corresponding PUBLISH isn't in the inflight queue,
                // it means that we processed PUBLISH message and received PUBREL and we sent PUBCOMP
                // but publisher didn't receive PUBCOMP so it re-sent PUBREL. We need only to re-send PUBCOMP.

                // NOTE : I need to find on message id and flow because the broker could be publish/received
                // to/from client and message id could be the same (one tracked by broker and the other by client)
                var msgCtxFinder = new MqttMsgContextFinder(msg.MessageId, MqttMsgFlow.ToAcknowledge);
                var msgCtx       = (MqttMsgContext)connection.InflightQueue.FirstOrDefault(msgCtxFinder.Find);

                // the PUBLISH message isn't in the inflight queue, it was already processed so
                // we need to re-send PUBCOMP only
                if (msgCtx == null)
                {
                    outgoingMessageHandler.Pubcomp(connection, msg.MessageId);
                    enqueue = false;
                }
            }

            // if it is a PUBCOMP message (for QoS Level 2)
            else if (msg.Type == MqttMsgBase.MQTT_MSG_PUBCOMP_TYPE)
            {
                // if it is a PUBCOMP but the corresponding PUBLISH isn't in the inflight queue,
                // it means that we sent PUBLISH message, sent PUBREL (after receiving PUBREC) and already received PUBCOMP
                // but publisher didn't receive PUBREL so it re-sent PUBCOMP. We need only to ignore connection PUBCOMP.

                // NOTE : I need to find on message id and flow because the broker could be publish/received
                // to/from client and message id could be the same (one tracked by broker and the other by client)
                var msgCtxFinder = new MqttMsgContextFinder(msg.MessageId, MqttMsgFlow.ToPublish);
                var msgCtx       = (MqttMsgContext)connection.InflightQueue.FirstOrDefault(msgCtxFinder.Find);

                // the PUBLISH message isn't in the inflight queue, it was already sent so we need to ignore connection PUBCOMP
                if (msgCtx == null)
                {
                    enqueue = false;
                }
            }

            // if it is a PUBREC message (for QoS Level 2)
            else if (msg.Type == MqttMsgBase.MQTT_MSG_PUBREC_TYPE)
            {
                // if it is a PUBREC but the corresponding PUBLISH isn't in the inflight queue,
                // it means that we sent PUBLISH message more times (retries) but broker didn't send PUBREC in time
                // the publish is failed and we need only to ignore rawMessage.Connection PUBREC.

                // NOTE : I need to find on message id and flow because the broker could be publish/received
                // to/from client and message id could be the same (one tracked by broker and the other by client)
                var msgCtxFinder = new MqttMsgContextFinder(msg.MessageId, MqttMsgFlow.ToPublish);
                var msgCtx       = (MqttMsgContext)connection.InflightQueue.FirstOrDefault(msgCtxFinder.Find);

                // the PUBLISH message isn't in the inflight queue, it was already sent so we need to ignore rawMessage.Connection PUBREC
                if (msgCtx == null)
                {
                    enqueue = false;
                }
            }

            if (enqueue)
            {
                connection.InternalQueue.Enqueue(msg);
            }
        }
Пример #3
0
        /// <summary>
        /// Enqueue a message into the internal queue
        /// </summary>
        /// <param name="msg">Message to enqueue</param>
        private void EnqueueInternal(MqttMsgBase msg)
        {
            // enqueue is needed (or not)
            bool enqueue = true;

            // if it is a PUBREL message (for QoS Level 2)
            if (msg.Type == MqttMsgBase.MQTT_MSG_PUBREL_TYPE)
            {
                lock (this.inflightQueue)
                {
                    // if it is a PUBREL but the corresponding PUBLISH isn't in the inflight queue,
                    // it means that we processed PUBLISH message and received PUBREL and we sent PUBCOMP
                    // but publisher didn't receive PUBCOMP so it re-sent PUBREL. We need only to re-send PUBCOMP.

                    // NOTE : I need to find on message id and flow because the broker could be publish/received
                    //        to/from client and message id could be the same (one tracked by broker and the other by client)
                    MqttMsgContextFinder msgCtxFinder = new MqttMsgContextFinder(((MqttMsgPubrel)msg).MessageId, MqttMsgFlow.ToAcknowledge);
                    MqttMsgContext msgCtx = (MqttMsgContext)this.inflightQueue.Get(msgCtxFinder.Find);

                    // the PUBLISH message isn't in the inflight queue, it was already processed so
                    // we need to re-send PUBCOMP only
                    if (msgCtx == null)
                    {
                        MqttMsgPubcomp pubcomp = new MqttMsgPubcomp();
                        pubcomp.MessageId = ((MqttMsgPubrel)msg).MessageId;

                        this.Send(pubcomp);

                        enqueue = false;
                    }
                }
            }

            if (enqueue)
            {
                lock (this.internalQueue)
                {
                    this.internalQueue.Enqueue(msg);
                    this.inflightWaitHandle.Set();
                }
            }
        }
        private void EnqueueInflight(MqttConnection connection, MqttMsgBase msg, MqttMsgFlow flow)
        {
            // enqueue is needed (or not)
            var enqueue = true;

            // if it is a PUBLISH message with QoS Level 2
            if (msg.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE && msg.QosLevel == MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE)
            {
                // if it is a PUBLISH message already received (it is in the inflight queue), the publisher
                // re-sent it because it didn't received the PUBREC. In connection case, we have to re-send PUBREC

                // NOTE : I need to find on message id and flow because the broker could be publish/received
                // to/from client and message id could be the same (one tracked by broker and the other by client)
                var msgCtxFinder = new MqttMsgContextFinder(msg.MessageId, MqttMsgFlow.ToAcknowledge);
                var msgCtx       = (MqttMsgContext)connection.InflightQueue.FirstOrDefault(msgCtxFinder.Find);

                // the PUBLISH message is alredy in the inflight queue, we don't need to re-enqueue but we need
                // to change state to re-send PUBREC
                if (msgCtx != null)
                {
                    msgCtx.State = MqttMsgState.QueuedQos2;
                    msgCtx.Flow  = MqttMsgFlow.ToAcknowledge;
                    enqueue      = false;
                }
            }

            if (enqueue)
            {
                // set a default state
                var state = MqttMsgState.QueuedQos0;

                // based on QoS level, the messages flow between broker and client changes
                switch (msg.QosLevel)
                {
                // QoS Level 0
                case MqttMsgBase.QOS_LEVEL_AT_MOST_ONCE:
                    state = MqttMsgState.QueuedQos0;
                    break;

                // QoS Level 1
                case MqttMsgBase.QOS_LEVEL_AT_LEAST_ONCE:
                    state = MqttMsgState.QueuedQos1;
                    break;

                // QoS Level 2
                case MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE:
                    state = MqttMsgState.QueuedQos2;
                    break;
                }

                // [v3.1.1] SUBSCRIBE and UNSUBSCRIBE aren't "officially" QOS = 1
                // so QueuedQos1 state isn't valid for them
                if (msg.Type == MqttMsgBase.MQTT_MSG_SUBSCRIBE_TYPE)
                {
                    state = MqttMsgState.SendSubscribe;
                }
                else if (msg.Type == MqttMsgBase.MQTT_MSG_UNSUBSCRIBE_TYPE)
                {
                    state = MqttMsgState.SendUnsubscribe;
                }

                // queue message context
                var msgContext = new MqttMsgContext()
                {
                    Message = msg,
                    State   = state,
                    Flow    = flow,
                    Attempt = 0
                };

                // enqueue message and unlock send thread
                connection.EnqueueInflight(msgContext);

                // PUBLISH message
                if (msg.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE)
                {
                    if (msgContext.Flow == MqttMsgFlow.ToPublish &&
                        (msg.QosLevel == MqttMsgBase.QOS_LEVEL_AT_LEAST_ONCE ||
                         msg.QosLevel == MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE))
                    {
                        if (connection.Session != null)
                        {
                            connection.Session.InflightMessages.TryAdd(msgContext.Key, msgContext);
                        }
                    }

                    // to acknowledge and QoS level 2
                    else if (msgContext.Flow == MqttMsgFlow.ToAcknowledge &&
                             msg.QosLevel == MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE)
                    {
                        if (connection.Session != null)
                        {
                            connection.Session.InflightMessages.TryAdd(msgContext.Key, msgContext);
                        }
                    }
                }
            }
        }