private void ClientsForSessionThread() { while (isRunning) { try { string clientId = this.clientsForSession.Take(); MqttBrokerSession session = MqttSessionManager.GetSession(clientId); MqttMsgPublish outgoingMsg; while (session.OutgoingMessages.TryDequeue(out outgoingMsg)) { var query = from s in session.Subscriptions where (new Regex(s.Topic)).IsMatch(outgoingMsg.Topic) // check for topics based also on wildcard with regex select s; MqttSubscription subscription = query.FirstOrDefault(); if (subscription != null) { var qosLevel = (subscription.QosLevel < outgoingMsg.QosLevel) ? subscription.QosLevel : outgoingMsg.QosLevel; MqttMessageToClientConnectionManager.Publish(subscription.ClientConnection, outgoingMsg.Topic, outgoingMsg.Message, qosLevel, outgoingMsg.Retain); } } } catch (Exception exception) { logger.LogException(this, exception); } } }
/// <summary> /// Save session for a client (all related subscriptions) /// </summary> /// <param name="clientId">Client Id to save subscriptions</param> /// <param name="clientSession">Client session with inflight messages</param> /// <param name="subscriptions">Subscriptions to save</param> public void SaveSession(string clientId, MqttClientSession clientSession, List <MqttSubscription> subscriptions) { MqttBrokerSession session = null; lock (this.sessions) { // session doesn't exist if (!this.sessions.ContainsKey(clientId)) { // create new session session = new MqttBrokerSession(); session.ClientId = clientId; // add to sessions list this.sessions.Add(clientId, session); } else { // get existing session session = this.sessions[clientId]; } } // null reference to disconnected client session.Client = null; // update subscriptions session.Subscriptions = new List <MqttSubscription>(); lock (session.Subscriptions) { foreach (MqttSubscription subscription in subscriptions) { session.Subscriptions.Add(new MqttSubscription(subscription.ClientId, subscription.Topic, subscription.QosLevel, null)); } } // update inflight messages session.InflightMessages = new Hashtable(); lock (session.InflightMessages) { foreach (MqttMsgContext msgContext in clientSession.InflightMessages.Values) { session.InflightMessages.Add(msgContext.Key, msgContext); } } }
void Client_MqttMsgConnected(object sender, MqttMsgConnectEventArgs e) { // [v3.1.1] session present flag bool sessionPresent = false; // [v3.1.1] generated client id for client who provides client id zero bytes length string clientId = null; MqttClient client = (MqttClient)sender; // verify message to determine CONNACK message return code to the client byte returnCode = this.MqttConnectVerify(e.Message); // [v3.1.1] if client id is zero length, the broker assigns a unique identifier to it clientId = (e.Message.ClientId.Length != 0) ? e.Message.ClientId : Guid.NewGuid().ToString(); // connection "could" be accepted if (returnCode == MqttMsgConnack.CONN_ACCEPTED) { // check if there is a client already connected with same client Id MqttClient clientConnected = this.GetClient(clientId); // force connection close to the existing client (MQTT protocol) if (clientConnected != null) { this.CloseClient(clientConnected); } } try { // connection accepted, load (if exists) client session if (returnCode == MqttMsgConnack.CONN_ACCEPTED) { // check if not clean session and try to recovery a session if (!e.Message.CleanSession) { // create session for the client MqttClientSession clientSession = new MqttClientSession(clientId); // get session for the connected client MqttBrokerSession session = this.sessionManager.GetSession(clientId); // set inflight queue into the client session if (session != null) { clientSession.InflightMessages = session.InflightMessages; // [v3.1.1] session present flag if (client.ProtocolVersion == MqttProtocolVersion.Version_3_1_1) { sessionPresent = true; } } // send CONNACK message to the client client.Connack(e.Message, returnCode, clientId, sessionPresent); // load/inject session to the client client.LoadSession(clientSession); if (session != null) { // set reference to connected client into the session session.Client = client; // there are saved subscriptions if (session.Subscriptions != null) { // register all subscriptions for the connected client foreach (MqttSubscription subscription in session.Subscriptions) { this.subscriberManager.Subscribe(subscription.Topic, subscription.QosLevel, client); // publish retained message on the current subscription this.publisherManager.PublishRetaind(subscription.Topic, clientId); } } // there are saved outgoing messages if (session.OutgoingMessages.Count > 0) { // publish outgoing messages for the session this.publisherManager.PublishSession(session.ClientId); } } //signal EventHandler <MqttClientEventArgs> handler = DidAcceptNewClient; if (handler != null) { MqttClientEventArgs arg = new MqttClientEventArgs(); arg.Client = client; handler(this, arg); } } // requested clean session else { // send CONNACK message to the client client.Connack(e.Message, returnCode, clientId, sessionPresent); this.sessionManager.ClearSession(clientId); } } else { // send CONNACK message to the client client.Connack(e.Message, returnCode, clientId, sessionPresent); } } catch (MqttCommunicationException) { this.CloseClient(client); } }
/// <summary> /// Process the message queue to publish /// </summary> public void PublishThread() { int count; byte qosLevel; MqttMsgPublish publish; // create event to signal that current thread is ended this.publishEventEnd = new AutoResetEvent(false); while (this.isRunning) { // wait on message queueud to publish this.publishQueueWaitHandle.WaitOne(); // first check new subscribers to send retained messages ... lock (this.subscribersForRetained) { count = this.subscribersForRetained.Count; // publish retained messages to subscribers (new or reconnected) while (count > 0) { count--; MqttSubscription subscription = this.subscribersForRetained.Dequeue(); var query = from p in this.retainedMessages where (new Regex(subscription.Topic)).IsMatch(p.Key) // check for topics based also on wildcard with regex select p.Value; if (query.Count() > 0) { // reverse loop to allow for changes in "this.retainedMessages" for (int i = query.Count() - 1; i >= 0; i--) { MqttMsgPublish retained = query.ElementAt(i); qosLevel = (subscription.QosLevel < retained.QosLevel) ? subscription.QosLevel : retained.QosLevel; // send PUBLISH message to the current subscriber subscription.Client.Publish(retained.Topic, retained.Message, qosLevel, retained.Retain); } } } } // ... then check clients to send outgoing session messages lock (this.clientsForSession) { count = this.clientsForSession.Count; // publish outgoing session messages to clients (reconnected) while (count > 0) { count--; string clientId = this.clientsForSession.Dequeue(); MqttBrokerSession session = this.sessionManager.GetSession(clientId); while (session.OutgoingMessages.Count > 0) { MqttMsgPublish outgoingMsg = session.OutgoingMessages.Dequeue(); var query = from s in session.Subscriptions where (new Regex(s.Topic)).IsMatch(outgoingMsg.Topic) // check for topics based also on wildcard with regex select s; MqttSubscription subscription = query.First(); if (subscription != null) { qosLevel = (subscription.QosLevel < outgoingMsg.QosLevel) ? subscription.QosLevel : outgoingMsg.QosLevel; session.Client.Publish(outgoingMsg.Topic, outgoingMsg.Message, qosLevel, outgoingMsg.Retain); } } } } // ... then pass to process publish queue lock (this.publishQueue) { publish = null; count = this.publishQueue.Count; // publish all queued messages while (count > 0) { count--; publish = (MqttMsgPublish)this.publishQueue.Dequeue(); if (publish != null) { // get all subscriptions for a topic List <MqttSubscription> subscriptions = this.subscriberManager.GetSubscriptionsByTopic(publish.Topic); if ((subscriptions != null) && (subscriptions.Count > 0)) { foreach (MqttSubscription subscription in subscriptions) { qosLevel = (subscription.QosLevel < publish.QosLevel) ? subscription.QosLevel : publish.QosLevel; // send PUBLISH message to the current subscriber subscription.Client.Publish(publish.Topic, publish.Message, qosLevel, publish.Retain); } } // get all sessions List <MqttBrokerSession> sessions = this.sessionManager.GetSessions(); if ((sessions != null) && (sessions.Count > 0)) { foreach (MqttBrokerSession session in sessions) { var query = from s in session.Subscriptions where (new Regex(s.Topic)).IsMatch(publish.Topic) select s; MqttSubscriptionComparer comparer = new MqttSubscriptionComparer(MqttSubscriptionComparer.MqttSubscriptionComparerType.OnClientId); // consider only session active for client disconnected (not online) if (session.Client == null) { foreach (MqttSubscription subscription in query.Distinct(comparer)) { qosLevel = (subscription.QosLevel < publish.QosLevel) ? subscription.QosLevel : publish.QosLevel; // save PUBLISH message for client disconnected (not online) session.OutgoingMessages.Enqueue(publish); } } } } } } } } // signal thread end this.publishEventEnd.Set(); }
public void OnMqttMsgConnected(MqttClientConnection clientConnection, MqttMsgConnect message) { clientConnection.ProtocolVersion = (MqttProtocolVersion)message.ProtocolVersion; // verify message to determine CONNACK message return code to the client byte returnCode = MqttConnectVerify(message); // [v3.1.1] if client id is zero length, the broker assigns a unique identifier to it var clientId = (message.ClientId.Length != 0) ? message.ClientId : Guid.NewGuid().ToString(); // connection "could" be accepted if (returnCode == MqttMsgConnack.CONN_ACCEPTED) { // check if there is a client already connected with same client Id MqttClientConnection clientConnectionConnected = MqttBroker.GetClientConnection(clientId); // force connection close to the existing client (MQTT protocol) if (clientConnectionConnected != null) { OnConnectionClosed(clientConnectionConnected); } // add client to the collection MqttBroker.TryAddClientConnection(clientId, clientConnection); Interlocked.Increment(ref numberOfConnectedClients); } // connection accepted, load (if exists) client session if (returnCode == MqttMsgConnack.CONN_ACCEPTED) { // check if not clean session and try to recovery a session if (!message.CleanSession) { // create session for the client MqttClientSession clientSession = new MqttClientSession(clientId); // get session for the connected client MqttBrokerSession session = MqttSessionManager.GetSession(clientId); // [v3.1.1] session present flag bool sessionPresent = false; // set inflight queue into the client session if (session != null) { clientSession.InflightMessages = session.InflightMessages; // [v3.1.1] session present flag if (clientConnection.ProtocolVersion == MqttProtocolVersion.Version_3_1_1) { sessionPresent = true; } } // send CONNACK message to the client MqttOutgoingMessageManager.Connack(clientConnection, message, returnCode, clientId, sessionPresent); // load/inject session to the client clientConnection.LoadSession(clientSession); if (session != null) { // set reference to connected client into the session session.ClientConnection = clientConnection; // there are saved subscriptions if (session.Subscriptions != null) { // register all subscriptions for the connected client foreach (MqttSubscription subscription in session.Subscriptions) { MqttSubscriberManager.Subscribe( subscription.Topic, subscription.QosLevel, clientConnection); // publish retained message on the current subscription RetainedMessageManager.PublishRetaind(subscription.Topic, clientConnection); } } // there are saved outgoing messages if (session.OutgoingMessages.Count > 0) { // publish outgoing messages for the session this.publishManager.PublishSession(session.ClientId); } } } // requested clean session else { // send CONNACK message to the client MqttOutgoingMessageManager.Connack(clientConnection, message, returnCode, clientId, false); MqttSessionManager.ClearSession(clientId); } } else { // send CONNACK message to the client MqttOutgoingMessageManager.Connack(clientConnection, message, returnCode, clientId, false); } }