/// <summary> /// Handles the publish complete, for messages that are undergoing Qos ExactlyOnce processing. /// </summary> /// <param name="msg">The MSG.</param> /// <returns>Boolean value indicating whether the message was successfull processed.</returns> private bool HandlePublishRelease(MqttMessage msg) { var pubRelMsg = (MqttPublishReleaseMessage)msg; var publishSuccess = true; try { MqttPublishMessage pubMsg; receivedMessages.TryGetValue(pubRelMsg.VariableHeader.MessageIdentifier, out pubMsg); if (pubMsg != null) { receivedMessages.Remove(pubRelMsg.VariableHeader.MessageIdentifier); // send the message for processing to whoever is waiting. var topic = new PublicationTopic(pubMsg.VariableHeader.TopicName); OnMessageReceived(topic, pubMsg); var compMsg = new MqttPublishCompleteMessage() .WithMessageIdentifier(pubMsg.VariableHeader.MessageIdentifier); connectionHandler.SendMessage(compMsg); } } catch (ArgumentException ex) { Log.Warn(m => m("Message recieved which contained topic ({0}) that was not valid according to MQTT Spec was suppressed.", pubRelMsg.VariableHeader.TopicName), ex); publishSuccess = false; } catch (Exception ex) { Log.Error(m => m("An error occurred while processing a publish release message received from a broker ({0}).", msg), ex); publishSuccess = false; } return(publishSuccess); }
/// <summary> /// Raises the MessageReceived event. /// </summary> /// <param name="topic">The topic the message belongs to.</param> /// <param name="msg">The message received.</param> private void OnMessageReceived(PublicationTopic topic, MqttPublishMessage msg) { var handler = MessageReceived; if (handler != null) { handler(this, new PublishEventArgs(topic, msg)); } }
// ReSharper restore NotResolvedInText /// <summary> /// Checks if the rawTopic matches the supplied rawTopic using the MQTT rawTopic matching rules. /// </summary> /// <param name="matcheeTopic">The rawTopic to match.</param> /// <returns>True if the rawTopic matches based on the MQTT rawTopic matching rules, otherwise false.</returns> public bool Matches(PublicationTopic matcheeTopic) { // of the left rawTopic is just a multi wildcard then we have a match without // needing to check any further. if (this.RawTopic.Equals(MultiWildcard)) { return(true); } // if the topics are an exact match, bail early with a cheap comparison if (Equals(matcheeTopic)) { return(true); } // no match yet so we need to check each fragment for (var i = 0; i < this.TopicFragments.Length; i++) { var lhsFragment = TopicFragments[i]; // if we've reached a multi wildcard in the lhs rawTopic, // we have a match. // (this is the mqtt spec rule finance matches finance or finance/#) if (lhsFragment.Equals(MultiWildcard)) { return(true); } var isLhsWildcard = lhsFragment.Equals(Wildcard); // if we've reached a wildcard match but the matchee does not have anything at // this fragment level then it's not a match. // (this is the mqtt spec rule finance does not match finance/+ if (isLhsWildcard && matcheeTopic.TopicFragments.Length <= i) { return(false); } // if lhs is not a wildcard we need to check whether the // two fragments match each other. if (!isLhsWildcard) { var rhsFragment = matcheeTopic.TopicFragments[i]; // if lhs fragment is not wildcard then we need an exact match if (!lhsFragment.Equals(rhsFragment)) { return(false); } } // if we're at the last fragment of the lhs rawTopic but there are // more fragments in the in the matchee then the matchee rawTopic // is too specific to be a match. if (i + 1 == this.TopicFragments.Length && matcheeTopic.TopicFragments.Length > this.TopicFragments.Length) { return(false); } // if we're here the current fragment matches so check the next } // if we exit out of the loop without a return then we have a full match rawTopic/rawTopic which would // have been caught by the original exact match check at the top anyway. return(true); }
/// <summary> /// Handles the receipt of publish messages from a message broker. /// </summary> /// <param name="msg">The message that was published.</param> /// <returns></returns> private bool HandlePublish(MqttMessage msg) { var pubMsg = (MqttPublishMessage)msg; var publishSuccess = true; try { var topic = new PublicationTopic(pubMsg.VariableHeader.TopicName); if (pubMsg.Header.Qos == MqttQos.AtMostOnce) { // QOS AtMostOnce 0 require no response. // send the message for processing to whoever is waiting. OnMessageReceived(topic, pubMsg); } else if (pubMsg.Header.Qos == MqttQos.AtLeastOnce) { // QOS AtLeastOnce 1 require an acknowledgement // send the message for processing to whoever is waiting. OnMessageReceived(topic, pubMsg); var ackMsg = new MqttPublishAckMessage() .WithMessageIdentifier(pubMsg.VariableHeader.MessageIdentifier); connectionHandler.SendMessage(ackMsg); } else if (pubMsg.Header.Qos == MqttQos.ExactlyOnce) { // QOS ExactlyOnce means we can't give it away yet, we gotta do a handshake // to make sure the broker knows we got it, and we know he knows we got it. // if we've already got it thats ok, it just means its being republished because // of a handshake breakdown, overwrite our existing one for the sake of it if (!receivedMessages.ContainsKey(pubMsg.VariableHeader.MessageIdentifier)) { receivedMessages[pubMsg.VariableHeader.MessageIdentifier] = pubMsg; } var pubRecv = new MqttPublishReceivedMessage() .WithMessageIdentifier(pubMsg.VariableHeader.MessageIdentifier); connectionHandler.SendMessage(pubRecv); } } catch (ArgumentException ex) { Log.Warn(m => m("Message recieved which contained topic ({0}) that was not valid according to MQTT Spec was suppressed.", pubMsg.VariableHeader.TopicName), ex); publishSuccess = false; } catch (Exception ex) { Log.Error(m => m("An error occurred while processing a message received from a broker ({0}).", msg), ex); publishSuccess = false; } return(publishSuccess); }
/// <summary> /// Publishes a message to the message broker. /// </summary> /// <typeparam name="TPayloadConverter">The type of the data converter to use.</typeparam> /// <typeparam name="T">The Type of the data being published</typeparam> /// <param name="topic">The topic to publish the message to.</param> /// <param name="qualityOfService">The quality of service to attach to the message.</param> /// <param name="data">The message to publish.</param> /// <returns> /// The message identiier assigned to the message. /// </returns> /// <exception cref="InvalidTopicException">Thrown if the topic supplied violates the MQTT topic format rules.</exception> public short PublishMessage <T, TPayloadConverter>(string topic, MqttQos qualityOfService, T data) where TPayloadConverter : IPayloadConverter <T>, new() { if (connectionHandler.State != ConnectionState.Connected) { throw new ConnectionException(connectionHandler.State); } try { var pubTopic = new PublicationTopic(topic); return(publishingManager.Publish <T, TPayloadConverter>(pubTopic, qualityOfService, data)); } catch (ArgumentException ex) { throw new InvalidTopicException(ex.Message, topic, ex); } }
// ReSharper restore NotResolvedInText /// <summary> /// Checks if the rawTopic matches the supplied rawTopic using the MQTT rawTopic matching rules. /// </summary> /// <param name="matcheeTopic">The rawTopic to match.</param> /// <returns>True if the rawTopic matches based on the MQTT rawTopic matching rules, otherwise false.</returns> public bool Matches(PublicationTopic matcheeTopic) { // of the left rawTopic is just a multi wildcard then we have a match without // needing to check any further. if (this.RawTopic.Equals(MultiWildcard)) { return true; } // if the topics are an exact match, bail early with a cheap comparison if (Equals(matcheeTopic)) { return true; } // no match yet so we need to check each fragment for (var i = 0; i < this.TopicFragments.Length; i++) { var lhsFragment = TopicFragments[i]; // if we've reached a multi wildcard in the lhs rawTopic, // we have a match. // (this is the mqtt spec rule finance matches finance or finance/#) if (lhsFragment.Equals(MultiWildcard)) { return true; } var isLhsWildcard = lhsFragment.Equals(Wildcard); // if we've reached a wildcard match but the matchee does not have anything at // this fragment level then it's not a match. // (this is the mqtt spec rule finance does not match finance/+ if (isLhsWildcard && matcheeTopic.TopicFragments.Length <= i) { return false; } // if lhs is not a wildcard we need to check whether the // two fragments match each other. if (!isLhsWildcard) { var rhsFragment = matcheeTopic.TopicFragments[i]; // if lhs fragment is not wildcard then we need an exact match if (!lhsFragment.Equals(rhsFragment)) { return false; } } // if we're at the last fragment of the lhs rawTopic but there are // more fragments in the in the matchee then the matchee rawTopic // is too specific to be a match. if (i + 1 == this.TopicFragments.Length && matcheeTopic.TopicFragments.Length > this.TopicFragments.Length) { return false; } // if we're here the current fragment matches so check the next } // if we exit out of the loop without a return then we have a full match rawTopic/rawTopic which would // have been caught by the original exact match check at the top anyway. return true; }
/// <summary> /// Publish a message to the broker on the specified topic. /// </summary> /// <param name="topic">The topic to send the message to.</param> /// <param name="qualityOfService">The QOS to use when publishing the message.</param> /// <param name="data">The message to send.</param> /// <returns>The message identifier assigned to the message.</returns> public short Publish <T, TPayloadConverter>(PublicationTopic topic, MqttQos qualityOfService, T data) where TPayloadConverter : IPayloadConverter <T>, new() { var msgId = messageIdentifierDispenser.GetNextMessageIdentifier(String.Format("topic:{0}", topic)); Log.DebugFormat("Publishing message ID {0} on topic {1} using QOS {2}", msgId, topic, qualityOfService); var converter = GetPayloadConverter <TPayloadConverter>(); var msg = new MqttPublishMessage() .ToTopic(topic.ToString()) .WithMessageIdentifier(msgId) .WithQos(qualityOfService) .PublishData(converter.ConvertToBytes(data)); // QOS level 1 or 2 messages need to be saved so we can do the ack processes if (qualityOfService == MqttQos.AtLeastOnce || qualityOfService == MqttQos.ExactlyOnce) { publishedMessages.Add(msgId, msg); } connectionHandler.SendMessage(msg); return(msgId); }
/// <summary> /// Creates a new instance of a PublishEventArgs class. /// </summary> /// <param name="topic">The parsed topic.</param> /// <param name="publishMessage">The MQTT Publish Message that's been published.</param> public PublishEventArgs(PublicationTopic topic, MqttPublishMessage publishMessage) { Topic = topic; PublishMessage = publishMessage; }