private int HandleQueuedQos1SendSubscribeAndSendUnsubscribe( MqttConnection connection, 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; } outgoingMessageHandler.Send(connection, msgInflight); // update timeout : minimum between delay (based on current message sent) or current timeout timeout = connection.Settings.DelayOnRetry < timeout ? connection.Settings.DelayOnRetry : timeout; // re-enqueue message (I have to re-analyze for receiving PUBACK, SUBACK or UNSUBACK) connection.EnqueueInflight(msgContext); } // QoS 1, PUBLISH message received from broker to acknowledge, send PUBACK else if (msgContext.Flow == MqttMsgFlow.ToAcknowledge) { outgoingMessageHandler.Puback(connection, msgInflight.MessageId); internalEvent = new InternalEvent(msgInflight); // notify published message from broker and acknowledged connection.EnqueueInternalEvent(internalEvent); } return(timeout); }
private MqttMsgBase WaitForPubrel( MqttConnection connection, MqttMsgContext msgContext, MqttMsgBase msgInflight, ref bool msgReceivedProcessed) { // QoS 2, waiting for PUBREL of a PUBREC message sent if (msgContext.Flow != MqttMsgFlow.ToAcknowledge) { return(null); } if (!connection.InternalQueue.TryPeek(out MqttMsgBase msgReceived)) { return(null); } InternalEvent internalEvent; // it is a PUBREL message if (msgReceived != null && msgReceived.Type == MqttMsgBase.MQTT_MSG_PUBREL_TYPE) { if (msgReceived.MessageId == msgInflight.MessageId) { // received message processed connection.InternalQueue.TryDequeue(out MqttMsgBase dequeuedMsg); msgReceivedProcessed = true; outgoingMessageHandler.Pubcomp(connection, msgInflight.MessageId); internalEvent = new InternalEvent(msgInflight); // notify published message from broker and acknowledged connection.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 && connection.Session != null && connection.Session.InflightMessages.ContainsKey(msgContext.Key)) { connection.Session.InflightMessages.TryRemove( msgContext.Key, out MqttMsgContext contextToBeRemoved); } } else { // re-enqueue message connection.EnqueueInflight(msgContext); } } else { connection.EnqueueInflight(msgContext); } return(msgReceived); }
private void HandleQueuedQos0( MqttConnection connection, MqttMsgContext msgContext, MqttMsgBase msgInflight) { // QoS 0, PUBLISH message to send to broker, no state change, no acknowledge if (msgContext.Flow == MqttMsgFlow.ToPublish) { outgoingMessageHandler.Send(connection, msgInflight); } // QoS 0, no need acknowledge else if (msgContext.Flow == MqttMsgFlow.ToAcknowledge) { var internalEvent = new InternalEvent(msgInflight); connection.EnqueueInternalEvent(internalEvent); } }
private MqttMsgBase WaitForPubcomp( MqttConnection connection, MqttMsgContext msgContext, MqttMsgBase msgInflight, ref bool msgReceivedProcessed, ref int timeout) { // QoS 2, waiting for PUBCOMP of a PUBREL message sent if (msgContext.Flow != MqttMsgFlow.ToPublish) { return(null); } if (!connection.InternalQueue.TryPeek(out MqttMsgBase msgReceived)) { return(null); } var acknowledge = false; InternalEvent internalEvent; // it is a PUBCOMP message if (msgReceived.Type == MqttMsgBase.MQTT_MSG_PUBCOMP_TYPE) { // PUBCOMP message for the current message if (msgReceived.MessageId == msgInflight.MessageId) { connection.InternalQueue.TryDequeue(out MqttMsgBase dequeuedMsg); acknowledge = true; msgReceivedProcessed = true; internalEvent = new PublishedInternalEvent(msgReceived, true); // notify received acknowledge from broker of a published message connection.EnqueueInternalEvent(internalEvent); // PUBCOMP received for PUBLISH message with QoS Level 2, remove from session state if (msgInflight.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE && connection.Session != null && connection.Session.InflightMessages.ContainsKey(msgContext.Key)) { connection.Session.InflightMessages.TryRemove( msgContext.Key, out MqttMsgContext contextToBeRemoved); } } } // it is a PUBREC message else if (msgReceived.Type == MqttMsgBase.MQTT_MSG_PUBREC_TYPE) { // another PUBREC message for the current message due to a retransmitted PUBLISH // I'm in waiting for PUBCOMP, so I can discard connection PUBREC if (msgReceived.MessageId == msgInflight.MessageId) { connection.InternalQueue.TryDequeue(out MqttMsgBase dequeuedMsg); acknowledge = true; msgReceivedProcessed = true; // re-enqueue message connection.EnqueueInflight(msgContext); } } // current message not acknowledged if (!acknowledge) { var delta = Environment.TickCount - msgContext.Timestamp; // check timeout for receiving PUBCOMP since PUBREL was sent if (delta >= connection.Settings.DelayOnRetry) { // max retry not reached, resend if (msgContext.Attempt < connection.Settings.AttemptsOnRetry) { msgContext.State = MqttMsgState.SendPubrel; // re-enqueue message connection.EnqueueInflight(msgContext); // update timeout (0 -> reanalyze queue immediately) timeout = 0; } else { // PUBCOMP not received, PUBREL retries failed, need to remove from session inflight messages too if (connection.Session != null && connection.Session.InflightMessages.ContainsKey(msgContext.Key)) { connection.Session.InflightMessages.TryRemove( msgContext.Key, out MqttMsgContext contextToBeRemoved); } // if PUBCOMP for a PUBLISH message not received after retries, raise event for not published internalEvent = new PublishedInternalEvent(msgInflight, false); // notify not received acknowledge from broker and message not published connection.EnqueueInternalEvent(internalEvent); } } else { // re-enqueue message connection.EnqueueInflight(msgContext); // update timeout var msgTimeout = connection.Settings.DelayOnRetry - delta; timeout = msgTimeout < timeout ? msgTimeout : timeout; } } return(msgReceived); }
private MqttMsgBase HandleWaitForPubrec( MqttConnection connection, 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); } if (!connection.InternalQueue.TryPeek(out MqttMsgBase msgReceived)) { return(null); } var acknowledge = false; InternalEvent internalEvent; // it is a PUBREC message if (msgReceived.Type == MqttMsgBase.MQTT_MSG_PUBREC_TYPE) { if (msgReceived.MessageId == msgInflight.MessageId) { // received message processed connection.InternalQueue.TryDequeue(out MqttMsgBase dequeuedMsg); acknowledge = true; msgReceivedProcessed = true; outgoingMessageHandler.Pubrel(connection, 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 = connection.Settings.DelayOnRetry < timeout ? connection.Settings.DelayOnRetry : timeout; // re-enqueue message connection.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 >= connection.Settings.DelayOnRetry) { // max retry not reached, resend if (msgContext.Attempt < connection.Settings.AttemptsOnRetry) { msgContext.State = MqttMsgState.QueuedQos2; // re-enqueue message connection.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 (connection.Session != null && connection.Session.InflightMessages.ContainsKey(msgContext.Key)) { connection.Session.InflightMessages.TryRemove( msgContext.Key, out MqttMsgContext contextToBeRemoved); } // if PUBREC for a PUBLISH message not received after retries, raise event for not published internalEvent = new PublishedInternalEvent(msgInflight, false); // notify not received acknowledge from broker and message not published connection.EnqueueInternalEvent(internalEvent); } } else { // re-enqueue message connection.EnqueueInflight(msgContext); // update timeout var msgTimeout = connection.Settings.DelayOnRetry - delta; timeout = msgTimeout < timeout ? msgTimeout : timeout; } } return(msgReceived); }
private MqttMsgBase HandleWaitForPubackSubackUbsuback( MqttConnection connection, MqttMsgContext msgContext, MqttMsgBase msgInflight, ref bool msgReceivedProcessed, ref int timeout) { // QoS 1, waiting for PUBACK of a PUBLISH message sent or // waiting for SUBACK of a SUBSCRIBE message sent or // waiting for UNSUBACK of a UNSUBSCRIBE message sent or if (msgContext.Flow != MqttMsgFlow.ToPublish) { return(null); } if (!connection.InternalQueue.TryPeek(out MqttMsgBase msgReceived)) { return(null); } var acknowledge = false; InternalEvent internalEvent; // PUBACK message or SUBACK/UNSUBACK message for the current message if (msgReceived.Type == MqttMsgBase.MQTT_MSG_PUBACK_TYPE && msgInflight.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE && msgReceived.MessageId == msgInflight.MessageId || msgReceived.Type == MqttMsgBase.MQTT_MSG_SUBACK_TYPE && msgInflight.Type == MqttMsgBase.MQTT_MSG_SUBSCRIBE_TYPE && msgReceived.MessageId == msgInflight.MessageId || msgReceived.Type == MqttMsgBase.MQTT_MSG_UNSUBACK_TYPE && msgInflight.Type == MqttMsgBase.MQTT_MSG_UNSUBSCRIBE_TYPE && msgReceived.MessageId == msgInflight.MessageId) { // received message processed connection.InternalQueue.TryDequeue(out MqttMsgBase dequeuedMsg); acknowledge = true; msgReceivedProcessed = true; // if PUBACK received, confirm published with flag if (msgReceived.Type == MqttMsgBase.MQTT_MSG_PUBACK_TYPE) { internalEvent = new PublishedInternalEvent(msgReceived, true); } else { internalEvent = new InternalEvent(msgReceived); } // notify received acknowledge from broker of a published message or subscribe/unsubscribe message connection.EnqueueInternalEvent(internalEvent); // PUBACK received for PUBLISH message with QoS Level 1, remove from session state if (msgInflight.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE && connection.Session != null && connection.Session.InflightMessages.ContainsKey(msgContext.Key)) { connection.Session.InflightMessages.TryRemove(msgContext.Key, out MqttMsgContext contextToBeRemoved); } } // current message not acknowledged, no PUBACK or SUBACK/UNSUBACK or not equal messageid if (!acknowledge) { var delta = Environment.TickCount - msgContext.Timestamp; // check timeout for receiving PUBACK since PUBLISH was sent or // for receiving SUBACK since SUBSCRIBE was sent or // for receiving UNSUBACK since UNSUBSCRIBE was sent if (delta >= connection.Settings.DelayOnRetry) { // max retry not reached, resend if (msgContext.Attempt < connection.Settings.AttemptsOnRetry) { msgContext.State = MqttMsgState.QueuedQos1; // re-enqueue message connection.EnqueueInflight(msgContext); // update timeout (0 -> reanalyze queue immediately) timeout = 0; } else { // if PUBACK for a PUBLISH message not received after retries, raise event for not published if (msgInflight.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE) { // PUBACK not received in time, PUBLISH retries failed, need to remove from session inflight messages too if (connection.Session != null && connection.Session.InflightMessages.ContainsKey(msgContext.Key)) { connection.Session.InflightMessages.TryRemove( msgContext.Key, out MqttMsgContext contextToBeRemoved); } internalEvent = new PublishedInternalEvent(msgInflight, false); // notify not received acknowledge from broker and message not published connection.EnqueueInternalEvent(internalEvent); } // NOTE : not raise events for SUBACK or UNSUBACK not received // for the user no event raised means subscribe/unsubscribe failed } } else { // re-enqueue message (I have to re-analyze for receiving PUBACK, SUBACK or UNSUBACK) connection.EnqueueInflight(msgContext); // update timeout var msgTimeout = connection.Settings.DelayOnRetry - delta; timeout = msgTimeout < timeout ? msgTimeout : timeout; } } return(msgReceived); }