/// <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); } }
/// <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); } } } } }