private static int SendPubrel(MqttClientConnection clientConnection, MqttMsgContext msgContext, MqttMsgBase msgInflight, int timeout)
        {
            // QoS 2, PUBREL message to send to broker, state change to wait PUBCOMP
            if (msgContext.Flow == MqttMsgFlow.ToPublish)
            {
                msgContext.State     = MqttMsgState.WaitForPubcomp;
                msgContext.Timestamp = Environment.TickCount;
                msgContext.Attempt++;
                // retry ? set dup flag [v3.1.1] no needed
                if (clientConnection.ProtocolVersion == MqttProtocolVersion.Version_3_1 && msgContext.Attempt > 1)
                {
                    MqttOutgoingMessageManager.Pubrel(clientConnection, msgInflight.MessageId, true);
                }
                else
                {
                    MqttOutgoingMessageManager.Pubrel(clientConnection, msgInflight.MessageId, false);
                }

                // update timeout : minimum between delay (based on current message sent) or current timeout
                timeout = (clientConnection.Settings.DelayOnRetry < timeout) ? clientConnection.Settings.DelayOnRetry : timeout;

                // re-enqueue message
                clientConnection.EnqueueInflight(msgContext);
            }

            return(timeout);
        }
        private static MqttMsgBase WaitForPubrel(MqttClientConnection clientConnection, MqttMsgContext msgContext, MqttMsgBase msgInflight, ref bool msgReceivedProcessed)
        {
            // QoS 2, waiting for PUBREL of a PUBREC message sent
            if (msgContext.Flow != MqttMsgFlow.ToAcknowledge)
            {
                return(null);
            }

            MqttMsgBase msgReceived;

            if (!clientConnection.InternalQueue.TryPeek(out msgReceived))
            {
                return(null);
            }

            InternalEvent internalEvent;

            // it is a PUBREL message
            if ((msgReceived != null) && (msgReceived.Type == MqttMsgBase.MQTT_MSG_PUBREL_TYPE))
            {
                // PUBREL message for the current message, send PUBCOMP
                if (msgReceived.MessageId == msgInflight.MessageId)
                {
                    // received message processed
                    MqttMsgBase dequeuedMsg;
                    clientConnection.InternalQueue.TryDequeue(out dequeuedMsg);
                    msgReceivedProcessed = true;


                    MqttOutgoingMessageManager.Pubcomp(clientConnection, msgInflight.MessageId);


                    internalEvent = new MsgInternalEvent(msgInflight);
                    // notify published message from broker and acknowledged
                    clientConnection.EnqueueInternalEvent(internalEvent);

                    // PUBREL received (and PUBCOMP sent) for PUBLISH message with QoS Level 2, remove from session state
                    if ((msgInflight.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE) && (clientConnection.Session != null) &&
                        (clientConnection.Session.InflightMessages.ContainsKey(msgContext.Key)))
                    {
                        MqttMsgContext contextToBeRemoved;
                        clientConnection.Session.InflightMessages.TryRemove(msgContext.Key, out contextToBeRemoved);
                    }
                }
                else
                {
                    // re-enqueue message
                    clientConnection.EnqueueInflight(msgContext);
                }
            }
            else
            {
                // re-enqueue message
                clientConnection.EnqueueInflight(msgContext);
            }

            return(msgReceived);
        }
        private static int HandleQueuedQos1SendSubscribeAndSendUnsubscribe(
            MqttClientConnection clientConnection,
            MqttMsgContext msgContext,
            MqttMsgBase msgInflight,
            int timeout)
        {
            InternalEvent internalEvent;

            // QoS 1, PUBLISH or SUBSCRIBE/UNSUBSCRIBE message to send to broker, state change to wait PUBACK or SUBACK/UNSUBACK
            if (msgContext.Flow == MqttMsgFlow.ToPublish)
            {
                msgContext.Timestamp = Environment.TickCount;
                msgContext.Attempt++;

                if (msgInflight.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE)
                {
                    // PUBLISH message to send, wait for PUBACK
                    msgContext.State = MqttMsgState.WaitForPuback;
                    // retry ? set dup flag [v3.1.1] only for PUBLISH message
                    if (msgContext.Attempt > 1)
                    {
                        msgInflight.DupFlag = true;
                    }
                }
                else if (msgInflight.Type == MqttMsgBase.MQTT_MSG_SUBSCRIBE_TYPE)
                {
                    // SUBSCRIBE message to send, wait for SUBACK
                    msgContext.State = MqttMsgState.WaitForSuback;
                }
                else if (msgInflight.Type == MqttMsgBase.MQTT_MSG_UNSUBSCRIBE_TYPE)
                {
                    // UNSUBSCRIBE message to send, wait for UNSUBACK
                    msgContext.State = MqttMsgState.WaitForUnsuback;
                }

                MqttOutgoingMessageManager.Send(clientConnection, msgInflight);

                // update timeout : minimum between delay (based on current message sent) or current timeout
                timeout = (clientConnection.Settings.DelayOnRetry < timeout) ? clientConnection.Settings.DelayOnRetry : timeout;

                // re-enqueue message (I have to re-analyze for receiving PUBACK, SUBACK or UNSUBACK)
                clientConnection.EnqueueInflight(msgContext);
            }
            // QoS 1, PUBLISH message received from broker to acknowledge, send PUBACK
            else if (msgContext.Flow == MqttMsgFlow.ToAcknowledge)
            {
                MqttOutgoingMessageManager.Puback(clientConnection, msgInflight.MessageId);

                internalEvent = new MsgInternalEvent(msgInflight);
                // notify published message from broker and acknowledged
                clientConnection.EnqueueInternalEvent(internalEvent);
            }
            return(timeout);
        }
        private static void OnMqttMsgUnsubscribeReceived(MqttClientConnection clientConnection, ushort messageId, string[] topics)
        {
            for (int i = 0; i < topics.Length; i++)
            {
                // unsubscribe client for each topic requested
                MqttSubscriberManager.Unsubscribe(topics[i], clientConnection);
            }

            // send UNSUBACK message to the client
            MqttOutgoingMessageManager.Unsuback(clientConnection, messageId);
        }
 private static void HandleQueuedQos0(
     MqttClientConnection clientConnection,
     MqttMsgContext msgContext,
     MqttMsgBase msgInflight)
 {
     // QoS 0, PUBLISH message to send to broker, no state change, no acknowledge
     if (msgContext.Flow == MqttMsgFlow.ToPublish)
     {
         MqttOutgoingMessageManager.Send(clientConnection, msgInflight);
     }
     // QoS 0, no need acknowledge
     else if (msgContext.Flow == MqttMsgFlow.ToAcknowledge)
     {
         var internalEvent = new MsgInternalEvent(msgInflight);
         clientConnection.EnqueueInternalEvent(internalEvent);
     }
 }
        private static int HandleQueuedQos2(
            MqttClientConnection clientConnection,
            MqttMsgContext msgContext,
            MqttMsgBase msgInflight,
            int timeout)
        {
            // QoS 2, PUBLISH message to send to broker, state change to wait PUBREC
            if (msgContext.Flow == MqttMsgFlow.ToPublish)
            {
                msgContext.Timestamp = Environment.TickCount;
                msgContext.Attempt++;
                msgContext.State = MqttMsgState.WaitForPubrec;
                // retry ? set dup flag
                if (msgContext.Attempt > 1)
                {
                    msgInflight.DupFlag = true;
                }

                MqttOutgoingMessageManager.Send(clientConnection, msgInflight);

                // update timeout : minimum between delay (based on current message sent) or current timeout
                timeout = (clientConnection.Settings.DelayOnRetry < timeout) ? clientConnection.Settings.DelayOnRetry : timeout;

                // re-enqueue message (I have to re-analyze for receiving PUBREC)
                clientConnection.EnqueueInflight(msgContext);
            }
            // QoS 2, PUBLISH message received from broker to acknowledge, send PUBREC, state change to wait PUBREL
            else if (msgContext.Flow == MqttMsgFlow.ToAcknowledge)
            {
                msgContext.State = MqttMsgState.WaitForPubrel;

                MqttOutgoingMessageManager.Pubrec(clientConnection, msgInflight.MessageId);

                // re-enqueue message (I have to re-analyze for receiving PUBREL)
                clientConnection.EnqueueInflight(msgContext);
            }

            return(timeout);
        }
        private static MqttMsgBase HandleWaitForPubrec(
            MqttClientConnection clientConnection,
            MqttMsgContext msgContext,
            MqttMsgBase msgInflight,
            ref bool msgReceivedProcessed,
            ref int timeout)
        {
            // QoS 2, waiting for PUBREC of a PUBLISH message sent
            if (msgContext.Flow != MqttMsgFlow.ToPublish)
            {
                return(null);
            }

            MqttMsgBase msgReceived;

            if (!clientConnection.InternalQueue.TryPeek(out msgReceived))
            {
                return(null);
            }

            bool          acknowledge = false;
            InternalEvent internalEvent;

            // it is a PUBREC message
            if (msgReceived.Type == MqttMsgBase.MQTT_MSG_PUBREC_TYPE)
            {
                // PUBREC message for the current PUBLISH message, send PUBREL, wait for PUBCOMP
                if (msgReceived.MessageId == msgInflight.MessageId)
                {
                    // received message processed
                    MqttMsgBase dequeuedMsg;
                    clientConnection.InternalQueue.TryDequeue(out dequeuedMsg);
                    acknowledge          = true;
                    msgReceivedProcessed = true;

                    MqttOutgoingMessageManager.Pubrel(clientConnection, msgInflight.MessageId, false);

                    msgContext.State     = MqttMsgState.WaitForPubcomp;
                    msgContext.Timestamp = Environment.TickCount;
                    msgContext.Attempt   = 1;

                    // update timeout : minimum between delay (based on current message sent) or current timeout
                    timeout = (clientConnection.Settings.DelayOnRetry < timeout)
                                  ? clientConnection.Settings.DelayOnRetry
                                  : timeout;

                    // re-enqueue message
                    clientConnection.EnqueueInflight(msgContext);
                }
            }

            // current message not acknowledged
            if (!acknowledge)
            {
                var delta = Environment.TickCount - msgContext.Timestamp;
                // check timeout for receiving PUBREC since PUBLISH was sent
                if (delta >= clientConnection.Settings.DelayOnRetry)
                {
                    // max retry not reached, resend
                    if (msgContext.Attempt < clientConnection.Settings.AttemptsOnRetry)
                    {
                        msgContext.State = MqttMsgState.QueuedQos2;

                        // re-enqueue message
                        clientConnection.EnqueueInflight(msgContext);

                        // update timeout (0 -> reanalyze queue immediately)
                        timeout = 0;
                    }
                    else
                    {
                        // PUBREC not received in time, PUBLISH retries failed, need to remove from session inflight messages too
                        if ((clientConnection.Session != null) &&
                            (clientConnection.Session.InflightMessages.ContainsKey(msgContext.Key)))
                        {
                            MqttMsgContext contextToBeRemoved;
                            clientConnection.Session.InflightMessages.TryRemove(msgContext.Key, out contextToBeRemoved);
                        }

                        // if PUBREC for a PUBLISH message not received after retries, raise event for not published
                        internalEvent = new MsgPublishedInternalEvent(msgInflight, false);
                        // notify not received acknowledge from broker and message not published
                        clientConnection.EnqueueInternalEvent(internalEvent);
                    }
                }
                else
                {
                    // re-enqueue message
                    clientConnection.EnqueueInflight(msgContext);

                    // update timeout
                    int msgTimeout = (clientConnection.Settings.DelayOnRetry - delta);
                    timeout = (msgTimeout < timeout) ? msgTimeout : timeout;
                }
            }

            return(msgReceived);
        }
        private static void EnqueueInternal(MqttClientConnection clientConnection, 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)
            {
                // 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(msg.MessageId, MqttMsgFlow.ToAcknowledge);
                MqttMsgContext       msgCtx       = (MqttMsgContext)clientConnection.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)
                {
                    MqttOutgoingMessageManager.Pubcomp(clientConnection, 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 clientConnection 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(msg.MessageId, MqttMsgFlow.ToPublish);
                MqttMsgContext       msgCtx       = (MqttMsgContext)clientConnection.InflightQueue.FirstOrDefault(msgCtxFinder.Find);

                // the PUBLISH message isn't in the inflight queue, it was already sent so we need to ignore clientConnection 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.ClientConnection 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(msg.MessageId, MqttMsgFlow.ToPublish);
                MqttMsgContext msgCtx = (MqttMsgContext)clientConnection.InflightQueue.FirstOrDefault(msgCtxFinder.Find);

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

            if (enqueue)
            {
                clientConnection.InternalQueue.Enqueue(msg);
            }
        }
        public static void ProcessReceivedMessage(MqttRawMessage rawMessage)
        {
            if (!rawMessage.ClientConnection.IsRunning)
            {
                return;
            }

            // update last message received ticks
            rawMessage.ClientConnection.LastCommunicationTime = Environment.TickCount;

            // extract message type from received byte
            byte msgType         = (byte)((rawMessage.MessageType & MqttMsgBase.MSG_TYPE_MASK) >> MqttMsgBase.MSG_TYPE_OFFSET);
            byte protocolVersion = (byte)rawMessage.ClientConnection.ProtocolVersion;

            switch (msgType)
            {
            case MqttMsgBase.MQTT_MSG_CONNECT_TYPE:
                MqttMsgConnect connect = MqttMsgConnect.Parse(rawMessage.MessageType, protocolVersion, rawMessage.PayloadBuffer);
                rawMessage.ClientConnection.EnqueueInternalEvent(new MsgInternalEvent(connect));
                break;

            case MqttMsgBase.MQTT_MSG_PINGREQ_TYPE:
                var pingReqest = MqttMsgPingReq.Parse(rawMessage.MessageType, protocolVersion);
                MqttOutgoingMessageManager.PingResp(rawMessage.ClientConnection);
                break;

            case MqttMsgBase.MQTT_MSG_SUBSCRIBE_TYPE:
                MqttMsgSubscribe subscribe = MqttMsgSubscribe.Parse(rawMessage.MessageType, protocolVersion, rawMessage.PayloadBuffer, rawMessage.PayloadLength);
                rawMessage.ClientConnection.EnqueueInternalEvent(new MsgInternalEvent(subscribe));
                break;

            case MqttMsgBase.MQTT_MSG_PUBLISH_TYPE:
                MqttMsgPublish publish = MqttMsgPublish.Parse(rawMessage.MessageType, protocolVersion, rawMessage.PayloadBuffer, rawMessage.PayloadLength);
                EnqueueInflight(rawMessage.ClientConnection, publish, MqttMsgFlow.ToAcknowledge);
                break;

            case MqttMsgBase.MQTT_MSG_PUBACK_TYPE:
                // enqueue PUBACK message received (for QoS Level 1) into the internal queue
                MqttMsgPuback puback = MqttMsgPuback.Parse(rawMessage.MessageType, protocolVersion, rawMessage.PayloadBuffer);
                EnqueueInternal(rawMessage.ClientConnection, puback);
                break;

            case MqttMsgBase.MQTT_MSG_PUBREC_TYPE:
                // enqueue PUBREC message received (for QoS Level 2) into the internal queue
                MqttMsgPubrec pubrec = MqttMsgPubrec.Parse(rawMessage.MessageType, protocolVersion, rawMessage.PayloadBuffer);
                EnqueueInternal(rawMessage.ClientConnection, pubrec);
                break;

            case MqttMsgBase.MQTT_MSG_PUBREL_TYPE:
                // enqueue PUBREL message received (for QoS Level 2) into the internal queue
                MqttMsgPubrel pubrel = MqttMsgPubrel.Parse(rawMessage.MessageType, protocolVersion, rawMessage.PayloadBuffer);
                EnqueueInternal(rawMessage.ClientConnection, pubrel);
                break;

            case MqttMsgBase.MQTT_MSG_PUBCOMP_TYPE:
                // enqueue PUBCOMP message received (for QoS Level 2) into the internal queue
                MqttMsgPubcomp pubcomp = MqttMsgPubcomp.Parse(rawMessage.MessageType, protocolVersion, rawMessage.PayloadBuffer);
                EnqueueInternal(rawMessage.ClientConnection, pubcomp);
                break;

            case MqttMsgBase.MQTT_MSG_UNSUBSCRIBE_TYPE:
                MqttMsgUnsubscribe unsubscribe = MqttMsgUnsubscribe.Parse(rawMessage.MessageType, protocolVersion, rawMessage.PayloadBuffer, rawMessage.PayloadLength);
                rawMessage.ClientConnection.EnqueueInternalEvent(new MsgInternalEvent(unsubscribe));
                break;

            case MqttMsgBase.MQTT_MSG_DISCONNECT_TYPE:
                MqttMsgDisconnect disconnect = MqttMsgDisconnect.Parse(rawMessage.MessageType, protocolVersion);
                rawMessage.ClientConnection.EnqueueInternalEvent(new MsgInternalEvent(disconnect));
                break;

            case MqttMsgBase.MQTT_MSG_CONNACK_TYPE:
            case MqttMsgBase.MQTT_MSG_PINGRESP_TYPE:
            case MqttMsgBase.MQTT_MSG_SUBACK_TYPE:
            case MqttMsgBase.MQTT_MSG_UNSUBACK_TYPE:
                throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage);

            default:
                throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage);
            }
        }