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