/// <summary> /// Constructor /// </summary> /// <param name="messageId">Message Id</param> /// <param name="flow">Message flow inside inflight queue</param> internal MqttMsgContextFinder(ushort messageId, MqttMsgFlow flow) { this.MessageId = messageId; this.Flow = flow; }
/// <summary> /// Constructor /// </summary> /// <param name="messageId">Message Id</param> /// <param name="flow">Message flow inside inflight queue</param> internal MqttMsgContextFinder(ushort messageId, MqttMsgFlow flow) { MessageId = messageId; Flow = flow; }
/// <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 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); } } } } }