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);
                }
            }
        }
        public void OnConnectionClosed(MqttClientConnection clientConnection)
        {
            // if client is connected
            MqttClientConnection connectedClient;

            if (clientConnection.IsConnected && MqttBroker.TryRemoveClientConnection(clientConnection.ClientId, out connectedClient))
            {
                // client has a will message
                if (clientConnection.WillFlag)
                {
                    // create the will PUBLISH message
                    MqttMsgPublish publish = new MqttMsgPublish(clientConnection.WillTopic, Encoding.UTF8.GetBytes(clientConnection.WillMessage), false, clientConnection.WillQosLevel, false);

                    // publish message through publisher manager
                    this.publishManager.Publish(publish);
                }

                // if not clean session
                if (!clientConnection.CleanSession)
                {
                    List <MqttSubscription> subscriptions = clientConnection.Subscriptions.Values.ToList();

                    if (subscriptions.Count > 0)
                    {
                        MqttSessionManager.SaveSession(clientConnection.ClientId, clientConnection.Session, subscriptions);

                        // TODO : persist client session if broker close
                    }
                }

                foreach (var topic in clientConnection.Subscriptions.Keys)
                {
                    // delete client from runtime subscription
                    MqttSubscriberManager.Unsubscribe(topic, clientConnection);
                }

                // close the client
                CloseClientConnection(clientConnection);
                clientConnectionManager.ReturnConnection(clientConnection);
                Interlocked.Decrement(ref numberOfConnectedClients);
            }
        }
        private void SessionPublishThread()
        {
            while (this.isRunning)
            {
                try
                {
                    var publish = (MqttMsgPublish)this.sessionPublishQueue.Take();
                    if (publish != null)
                    {
                        // get all sessions
                        List <MqttBrokerSession> sessions = MqttSessionManager.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.ClientConnection == null)
                                {
                                    foreach (MqttSubscription subscription in query.Distinct(comparer))
                                    {
                                        // save PUBLISH message for client disconnected (not online)
                                        session.OutgoingMessages.Enqueue(publish);
                                    }
                                }
                            }
                        }
                    }
                }
                catch (Exception exception)
                {
                    logger.LogException(this, exception);
                }
            }
        }
        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);
            }
        }