/// <summary> /// Thread for receiving messages from broker /// </summary> private void ReceiveThread() { int readBytes; byte[] fixedHeaderFirstByte = new byte[1]; byte msgType; while (this.isRunning) { try { // read first byte (fixed header) readBytes = this.socket.Receive(fixedHeaderFirstByte); if (readBytes > 0) { // extract message type from received byte msgType = (byte)((fixedHeaderFirstByte[0] & MqttMsgBase.MSG_TYPE_MASK) >> MqttMsgBase.MSG_TYPE_OFFSET); switch (msgType) { // impossible, broker can't send CONNECT message case MqttMsgBase.MQTT_MSG_CONNECT_TYPE: throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage); // CONNACK message received from broker case MqttMsgBase.MQTT_MSG_CONNACK_TYPE: this.msgReceived = MqttMsgConnack.Parse(fixedHeaderFirstByte[0], this.socket); this.endReceiving.Set(); break; // impossible, broker can't send PINGREQ message case MqttMsgBase.MQTT_MSG_PINGREQ_TYPE: throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage); // CONNACK message received from broker case MqttMsgBase.MQTT_MSG_PINGRESP_TYPE: this.msgReceived = MqttMsgPingResp.Parse(fixedHeaderFirstByte[0], this.socket); this.endReceiving.Set(); break; // impossible, broker can't send SUBSCRIBE message case MqttMsgBase.MQTT_MSG_SUBSCRIBE_TYPE: throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage); // SUBACK message received from broker case MqttMsgBase.MQTT_MSG_SUBACK_TYPE: this.msgReceived = MqttMsgSuback.Parse(fixedHeaderFirstByte[0], this.socket); this.endReceiving.Set(); // raise subscribed topic event (SUBACK message received) this.OnMqttMsgSubscribed((MqttMsgSuback)this.msgReceived); break; // PUBLISH message received from broker case MqttMsgBase.MQTT_MSG_PUBLISH_TYPE: MqttMsgPublish msgReceived = MqttMsgPublish.Parse(fixedHeaderFirstByte[0], this.socket); // for QoS Level 1 and 2, client sends PUBACK message to broker if ((this.msgReceived.QosLevel == MqttMsgBase.QOS_LEVEL_AT_LEAST_ONCE) || (this.msgReceived.QosLevel == MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE)) { MqttMsgPuback puback = new MqttMsgPuback(); puback.MessageId = (msgReceived).MessageId; this.Send(puback.GetBytes()); } // raise PUBLISH message received event this.OnMqttMsgPublishReceived(msgReceived); break; // PUBACK message received from broker case MqttMsgBase.MQTT_MSG_PUBACK_TYPE: this.msgReceived = MqttMsgPuback.Parse(fixedHeaderFirstByte[0], this.socket); this.endReceiving.Set(); // raise published message event // (PUBACK received for QoS Level 1) this.OnMqttMsgPublished(((MqttMsgPuback)this.msgReceived).MessageId); break; // PUBREC message received from broker case MqttMsgBase.MQTT_MSG_PUBREC_TYPE: this.msgReceived = MqttMsgPubrec.Parse(fixedHeaderFirstByte[0], this.socket); this.endReceiving.Set(); break; // impossible, broker can't send PUBREL message case MqttMsgBase.MQTT_MSG_PUBREL_TYPE: throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage); // PUBCOMP message received from broker case MqttMsgBase.MQTT_MSG_PUBCOMP_TYPE: this.msgReceived = MqttMsgPubcomp.Parse(fixedHeaderFirstByte[0], this.socket); this.endReceiving.Set(); // raise published message event // (PUBCOMP received for QoS Level 2) this.OnMqttMsgPublished(((MqttMsgPuback)this.msgReceived).MessageId); break; // impossible, broker can't send UNSUBSCRIBE message case MqttMsgBase.MQTT_MSG_UNSUBSCRIBE_TYPE: throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage); // UNSUBACK message received from broker case MqttMsgBase.MQTT_MSG_UNSUBACK_TYPE: this.msgReceived = MqttMsgUnsuback.Parse(fixedHeaderFirstByte[0], this.socket); this.endReceiving.Set(); // raise unsubscribed topic event this.OnMqttMsgUnsubscribed(((MqttMsgUnsuback)this.msgReceived).MessageId); break; default: throw new MqttClientException(MqttClientErrorCode.WrongBrokerMessage); } this.exReceiving = null; } } catch (Exception) { this.exReceiving = new MqttCommunicationException(); } } }
/// <summary> /// Process inflight messages queue /// </summary> private void ProcessInflightThread() { MqttMsgContext msgContext = null; MqttMsgBase msgInflight = null; MqttMsgBase msgReceived = null; bool acknowledge = false; int timeout = Timeout.Infinite; try { while (this.isRunning) { #if (MF_FRAMEWORK_VERSION_V4_2 || MF_FRAMEWORK_VERSION_V4_3 || COMPACT_FRAMEWORK) // wait on message queueud to inflight this.inflightWaitHandle.WaitOne(timeout, false); #else // wait on message queueud to inflight this.inflightWaitHandle.WaitOne(timeout); #endif // it could be unblocked because Close() method is joining if (this.isRunning) { lock (this.inflightQueue) { // set timeout tu MaxValue instead of Infinte (-1) to perform // compare with calcultad current msgTimeout timeout = Int32.MaxValue; // a message inflight could be re-enqueued but we have to // analyze it only just one time for cycle int count = this.inflightQueue.Count; // process all inflight queued messages while (count > 0) { count--; acknowledge = false; msgReceived = null; // dequeue message context from queue msgContext = (MqttMsgContext)this.inflightQueue.Dequeue(); // get inflight message msgInflight = (MqttMsgBase)msgContext.Message; switch (msgContext.State) { case MqttMsgState.QueuedQos0: // QoS 0, PUBLISH message to send to broker, no state change, no acknowledge if (msgContext.Flow == MqttMsgFlow.ToPublish) { this.Send(msgInflight.GetBytes()); } // QoS 0, no need acknowledge else if (msgContext.Flow == MqttMsgFlow.ToAcknowledge) { // notify published message from broker (no need acknowledged) this.OnMqttMsgReceived(msgInflight); } break; case MqttMsgState.QueuedQos1: // QoS 1, PUBLISH or SUBSCRIBE/UNSUBSCRIBE message to send to broker, state change to wait PUBACK or SUBACK/UNSUBACK if (msgContext.Flow == MqttMsgFlow.ToPublish) { if (msgInflight.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE) // PUBLISH message to send, wait for PUBACK msgContext.State = MqttMsgState.WaitForPuback; 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; msgContext.Timestamp = Environment.TickCount; msgContext.Attempt++; // retry ? set dup flag if (msgContext.Attempt > 1) msgInflight.DupFlag = true; this.Send(msgInflight.GetBytes()); // update timeout int msgTimeout = (this.settings.DelayOnRetry - (Environment.TickCount - msgContext.Timestamp)); timeout = (msgTimeout < timeout) ? msgTimeout : timeout; // re-enqueue message (I have to re-analyze for receiving PUBACK, SUBACK or UNSUBACK) this.inflightQueue.Enqueue(msgContext); } // QoS 1, PUBLISH message received from broker to acknowledge, send PUBACK else if (msgContext.Flow == MqttMsgFlow.ToAcknowledge) { MqttMsgPuback puback = new MqttMsgPuback(); puback.MessageId = ((MqttMsgPublish)msgInflight).MessageId; this.Send(puback.GetBytes()); // notify published message from broker and acknowledged this.OnMqttMsgReceived(msgInflight); } break; case MqttMsgState.QueuedQos2: // QoS 2, PUBLISH message to send to broker, state change to wait PUBREC if (msgContext.Flow == MqttMsgFlow.ToPublish) { msgContext.State = MqttMsgState.WaitForPubrec; msgContext.Timestamp = Environment.TickCount; msgContext.Attempt++; // retry ? set dup flag if (msgContext.Attempt > 1) msgInflight.DupFlag = true; this.Send(msgInflight.GetBytes()); // update timeout int msgTimeout = (this.settings.DelayOnRetry - (Environment.TickCount - msgContext.Timestamp)); timeout = (msgTimeout < timeout) ? msgTimeout : timeout; // re-enqueue message (I have to re-analyze for receiving PUBREC) this.inflightQueue.Enqueue(msgContext); } // QoS 2, PUBLISH message received from broker to acknowledge, send PUBREC, state change to wait PUBREL else if (msgContext.Flow == MqttMsgFlow.ToAcknowledge) { MqttMsgPubrec pubrec = new MqttMsgPubrec(); pubrec.MessageId = ((MqttMsgPublish)msgInflight).MessageId; msgContext.State = MqttMsgState.WaitForPubrel; this.Send(pubrec.GetBytes()); // re-enqueue message (I have to re-analyze for receiving PUBREL) this.inflightQueue.Enqueue(msgContext); } break; case MqttMsgState.WaitForPuback: case MqttMsgState.WaitForSuback: case MqttMsgState.WaitForUnsuback: // 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) { acknowledge = false; lock (this.internalQueue) { if (this.internalQueue.Count > 0) msgReceived = (MqttMsgBase)this.internalQueue.Peek(); } // it is a PUBACK message or a SUBACK/UNSUBACK message if ((msgReceived != null) && ((msgReceived.Type == MqttMsgBase.MQTT_MSG_PUBACK_TYPE) || (msgReceived.Type == MqttMsgBase.MQTT_MSG_SUBACK_TYPE) || (msgReceived.Type == MqttMsgBase.MQTT_MSG_UNSUBACK_TYPE))) { // PUBACK message or SUBACK message for the current message if (((msgInflight.Type == MqttMsgBase.MQTT_MSG_PUBLISH_TYPE) && (((MqttMsgPuback)msgReceived).MessageId == ((MqttMsgPublish)msgInflight).MessageId)) || ((msgInflight.Type == MqttMsgBase.MQTT_MSG_SUBSCRIBE_TYPE) && (((MqttMsgSuback)msgReceived).MessageId == ((MqttMsgSubscribe)msgInflight).MessageId)) || ((msgInflight.Type == MqttMsgBase.MQTT_MSG_UNSUBSCRIBE_TYPE) && (((MqttMsgUnsuback)msgReceived).MessageId == ((MqttMsgUnsubscribe)msgInflight).MessageId))) { lock (this.internalQueue) { // received message processed this.internalQueue.Dequeue(); acknowledge = true; } // notify received acknowledge from broker of a published message or subscribe/unsubscribe message this.OnMqttMsgReceived(msgReceived); } } // current message not acknowledged, no PUBACK or SUBACK/UNSUBACK or not equal messageid if (!acknowledge) { // 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 ((Environment.TickCount - msgContext.Timestamp) >= this.settings.DelayOnRetry) { // max retry not reached, resend if (msgContext.Attempt <= this.settings.AttemptsOnRetry) { msgContext.State = MqttMsgState.QueuedQos1; // re-enqueue message this.inflightQueue.Enqueue(msgContext); // update timeout (0 -> reanalyze queue immediately) timeout = 0; } } else { // re-enqueue message (I have to re-analyze for receiving PUBACK, SUBACK or UNSUBACK) this.inflightQueue.Enqueue(msgContext); // update timeout int msgTimeout = (this.settings.DelayOnRetry - (Environment.TickCount - msgContext.Timestamp)); timeout = (msgTimeout < timeout) ? msgTimeout : timeout; } } } break; case MqttMsgState.WaitForPubrec: // QoS 2, waiting for PUBREC of a PUBLISH message sent if (msgContext.Flow == MqttMsgFlow.ToPublish) { acknowledge = false; lock (this.internalQueue) { if (this.internalQueue.Count > 0) msgReceived = (MqttMsgBase)this.internalQueue.Peek(); } // it is a PUBREC message if ((msgReceived != null) && (msgReceived.Type == MqttMsgBase.MQTT_MSG_PUBREC_TYPE)) { // PUBREC message for the current PUBLISH message, send PUBREL, wait for PUBCOMP if (((MqttMsgPubrec)msgReceived).MessageId == ((MqttMsgPublish)msgInflight).MessageId) { lock (this.internalQueue) { // received message processed this.internalQueue.Dequeue(); acknowledge = true; } MqttMsgPubrel pubrel = new MqttMsgPubrel(); pubrel.MessageId = ((MqttMsgPublish)msgInflight).MessageId; msgContext.State = MqttMsgState.WaitForPubcomp; msgContext.Timestamp = Environment.TickCount; msgContext.Attempt = 1; this.Send(pubrel.GetBytes()); // update timeout int msgTimeout = (this.settings.DelayOnRetry - (Environment.TickCount - msgContext.Timestamp)); timeout = (msgTimeout < timeout) ? msgTimeout : timeout; // re-enqueue message this.inflightQueue.Enqueue(msgContext); } } // current message not acknowledged if (!acknowledge) { // check timeout for receiving PUBREC since PUBLISH was sent if ((Environment.TickCount - msgContext.Timestamp) >= this.settings.DelayOnRetry) { // max retry not reached, resend if (msgContext.Attempt <= this.settings.AttemptsOnRetry) { msgContext.State = MqttMsgState.QueuedQos2; // re-enqueue message this.inflightQueue.Enqueue(msgContext); // update timeout (0 -> reanalyze queue immediately) timeout = 0; } } else { // re-enqueue message this.inflightQueue.Enqueue(msgContext); // update timeout int msgTimeout = (this.settings.DelayOnRetry - (Environment.TickCount - msgContext.Timestamp)); timeout = (msgTimeout < timeout) ? msgTimeout : timeout; } } } break; case MqttMsgState.WaitForPubrel: // QoS 2, waiting for PUBREL of a PUBREC message sent if (msgContext.Flow == MqttMsgFlow.ToAcknowledge) { lock (this.internalQueue) { if (this.internalQueue.Count > 0) msgReceived = (MqttMsgBase)this.internalQueue.Peek(); } // it is a PUBREL message if ((msgReceived != null) && (msgReceived.Type == MqttMsgBase.MQTT_MSG_PUBREL_TYPE)) { // PUBREL message for the current message, send PUBCOMP if (((MqttMsgPubrel)msgReceived).MessageId == ((MqttMsgPublish)msgInflight).MessageId) { lock (this.internalQueue) { // received message processed this.internalQueue.Dequeue(); } MqttMsgPubcomp pubcomp = new MqttMsgPubcomp(); pubcomp.MessageId = ((MqttMsgPublish)msgInflight).MessageId; this.Send(pubcomp.GetBytes()); // notify published message from broker and acknowledged this.OnMqttMsgReceived(msgInflight); } else { // re-enqueue message this.inflightQueue.Enqueue(msgContext); } } else { // re-enqueue message this.inflightQueue.Enqueue(msgContext); } } break; case MqttMsgState.WaitForPubcomp: // QoS 2, waiting for PUBCOMP of a PUBREL message sent if (msgContext.Flow == MqttMsgFlow.ToPublish) { acknowledge = false; lock (this.internalQueue) { if (this.internalQueue.Count > 0) msgReceived = (MqttMsgBase)this.internalQueue.Peek(); } // it is a PUBCOMP message if ((msgReceived != null) && (msgReceived.Type == MqttMsgBase.MQTT_MSG_PUBCOMP_TYPE)) { // PUBCOMP message for the current message if (((MqttMsgPubcomp)msgReceived).MessageId == ((MqttMsgPublish)msgInflight).MessageId) { lock (this.internalQueue) { // received message processed this.internalQueue.Dequeue(); acknowledge = true; } // notify received acknowledge from broker of a published message this.OnMqttMsgReceived(msgReceived); } } // current message not acknowledged if (!acknowledge) { // check timeout for receiving PUBCOMP since PUBREL was sent if ((Environment.TickCount - msgContext.Timestamp) >= this.settings.DelayOnRetry) { // max retry not reached, resend if (msgContext.Attempt < this.settings.AttemptsOnRetry) { msgContext.State = MqttMsgState.SendPubrel; // re-enqueue message this.inflightQueue.Enqueue(msgContext); // update timeout (0 -> reanalyze queue immediately) timeout = 0; } } else { // re-enqueue message this.inflightQueue.Enqueue(msgContext); // update timeout int msgTimeout = (this.settings.DelayOnRetry - (Environment.TickCount - msgContext.Timestamp)); timeout = (msgTimeout < timeout) ? msgTimeout : timeout; } } } break; case MqttMsgState.SendPubrec: // TODO : impossible ? --> QueuedQos2 ToAcknowledge break; case MqttMsgState.SendPubrel: // QoS 2, PUBREL message to send to broker, state change to wait PUBCOMP if (msgContext.Flow == MqttMsgFlow.ToPublish) { MqttMsgPubrel pubrel = new MqttMsgPubrel(); pubrel.MessageId = ((MqttMsgPublish)msgInflight).MessageId; msgContext.State = MqttMsgState.WaitForPubcomp; msgContext.Timestamp = Environment.TickCount; msgContext.Attempt++; // retry ? set dup flag if (msgContext.Attempt > 1) pubrel.DupFlag = true; this.Send(pubrel.GetBytes()); // update timeout int msgTimeout = (this.settings.DelayOnRetry - (Environment.TickCount - msgContext.Timestamp)); timeout = (msgTimeout < timeout) ? msgTimeout : timeout; // re-enqueue message this.inflightQueue.Enqueue(msgContext); } break; case MqttMsgState.SendPubcomp: // TODO : impossible ? break; case MqttMsgState.SendPuback: // TODO : impossible ? --> QueuedQos1 ToAcknowledge break; default: break; } } // if calculated timeout is MaxValue, it means that must be Infinite (-1) if (timeout == Int32.MaxValue) timeout = Timeout.Infinite; } } } } catch (MqttCommunicationException) { this.Close(); // raise disconnection client event this.OnMqttMsgDisconnected(); } }