/// <summary>
        /// Permette di ricevere una risposta da un vicino.
        /// </summary>
        /// <param name="sourceNodeId">L'identificatore associato al vicino.</param>
        /// <param name="reply">La risposta inviata dal vicino.</param>
        public void Reply(string sourceNodeId, ReplyData reply)
        {
            if (m_KnownNeighbors.Contains(sourceNodeId))
            {
                WriteToLog("The received reply from node {0} has been accepted: {1}.", sourceNodeId, reply.MsgId);

                m_MessagesHandler.HandleReceivedReply(reply);
            }
            else
            {
                WriteToLog("The received reply from node {0} has been dropped: {1}.", sourceNodeId, reply.MsgId);
            }
        }
        /// <summary>
        /// Verifica se l'identificativo della connessione specificata esiste e se è associato ad una connessione
        /// attiva con un vicino ed in tal caso invia la risposta specificata con i risultati di una ricerca.
        /// </summary>
        /// <param name="reply">I dati della risposta da inviare.</param>
        /// <param name="connectionId">L'identificativo della connessione su cui inviare la risposta.</param>
        private void SendReply(ReplyData reply, string connectionId)
        {
            m_NeighborhoodManager.SendReply(reply, connectionId);

            WriteToLog("Reply {0} sent back to connection {1}. Found services: [{2}].",
                reply.MsgId, connectionId, string.Join(", ", reply.FoundServices));
        }
        /// <summary>
        /// Verifica quali, tra le risorse conosciute da questo nodo, sono compatibili con le opzioni di ricerca
        /// specificate nella query ricevuta, quindi invia l'eventuale risposta sulla connessione di provenienza
        /// della query.
        /// </summary>
        /// <param name="query">I dati della richiesta ricevuta.</param>
        /// <param name="sourceConnectionId">L'identificativo della connessione di provenienza della richiesta.</param>
        /// <remarks>
        /// Se si specifica null per almeno uno dei due parametri oppure se l'identificativo della connessione
        /// di provenienza della richiesta è una stringa vuota oppure formata da soli spazi, questo metodo non
        /// esegue alcuna azione, ignorando i parametri specificati.
        /// </remarks>
        private void ReplyToQuery(QueryData query, string sourceConnectionId)
        {
            Thread.Sleep(100);   // simula ritardo di rete

            if (string.IsNullOrWhiteSpace(sourceConnectionId) || query == null) return;

            WriteToLog("Processing query {0} ...", query.MsgId);

            List<Uri> found = m_ResourceCache.Search(
                delegate(Uri uri, IEnumerable<TaskPerformerInfo> resources)
                {
                    foreach (var resource in resources)
                    {
                        if (resource.Name == query.Options.Name && resource.Version == query.Options.Version)
                        {
                            return true;
                        }
                    }
                    return false;
                }
            ).ToList<Uri>();

            WriteToLog("Processed query {0}: {1} resources found.", query.MsgId, found.Count);

            if (found.Count > 0)
            {
                ReplyData reply = new ReplyData(query.MsgId, query.HopsCount, 0) { FoundServices = found };
                SendReply(reply, sourceConnectionId);
            }
        }
        /// <summary>
        /// Inoltra una risposta ricevuta da un vicino, verificando se occorre inviarla ad uno dei vicini successivi
        /// oppure se è giunta a destinazione, ovvero presso il nodo da cui è partita la richiesta a cui tale risposta
        /// si riferisce.
        /// La procedura prevede l'aggiornamento preliminare di due proprietà relative alla risposta: il TimeToLive
        /// viene decrementato di 1, mentre HopsCount viene incrementato di 1. Se il TimeToLive si mantiene maggiore
        /// di zero, la risposta viene inviata al vicino successivo, altrimenti vuol dire che la risposta è arrivata
        /// a destinazione: in quest'ultimo caso, recupera il riferimento alla ricerca inizialmente inviata e lo usa
        /// per aggiornare i risultati della ricerca con quelli contenuti nella risposta.
        /// </summary>
        /// <param name="reply">I dati della risposta ricevuta da un vicino.</param>
        /// <remarks>
        /// Se si specifica null per l'unico parametro previsto, questo metodo non esegue alcuna azione, ignorando
        /// il parametro specificato.
        /// </remarks>
        private void ForwardReceivedReply(ReplyData reply)
        {
            Thread.Sleep(100);   // simula ritardo di rete

            if (reply == null || reply.TimeToLive == 0) return;

            reply.TimeToLive--;
            reply.HopsCount++;

            if (reply.TimeToLive > 0)   // verifica che la risposta non sia giunta a destinazione
            {
                string targetConnectionId;

                lock (m_ForwardingTableLocker)
                {
                    if (!m_ForwardingTable.TryGetSourceConnection(reply.MsgId, out targetConnectionId) || targetConnectionId == null)
                    {
                        return;   // ignora messaggio con identificativo non trovato
                    }
                }

                WriteToLog("Sending received reply {0} back to connection {1}. Found services: [{2}].",
                    reply.MsgId, targetConnectionId, string.Join(", ", reply.FoundServices));

                SendReply(reply, targetConnectionId);   // inoltra messaggio al vicino di provenienza della richiesta
            }
            else   // TTL si è azzerato
            {
                WriteToLog("Reply {0} arrived to destination. Found services: [{1}].",
                    reply.MsgId, string.Join(", ", reply.FoundServices));

                SearchData searchReference = null;   // riferimento alla ricerca inizialmente inviata

                lock (m_ForwardingTableLocker)
                {
                    ForwardingTable.Entry entry = m_ForwardingTable.GetEntry(reply.MsgId);
                    if (entry != null && entry.SourceConnection == null)
                    {
                        searchReference = entry.SearchReference;
                    }
                }

                if (searchReference != null)
                {
                    bool updated = m_SearchManager.UpdateResult(searchReference, reply.FoundServices);
                    if (updated)
                    {
                        // aggiornamento completato correttamente
                        WriteToLog("Updated search results from the reply {0}.", reply.MsgId);
                    }
                    else
                    {
                        // ricerca scaduta
                        WriteToLog("Expired target search for the reply {0}.", reply.MsgId);
                    }
                }
            }
        }
 /// <summary>
 /// Gestisce una risposta ricevuta da un vicino, verificando se occorre inviarla ad uno dei vicini successivi
 /// oppure se è giunta a destinazione, ovvero presso il nodo da cui è partita la richiesta a cui tale risposta
 /// si riferisce.
 /// La procedura prevede l'aggiornamento preliminare di due proprietà relative alla risposta: il TimeToLive
 /// viene decrementato di 1, mentre HopsCount viene incrementato di 1. Se il TimeToLive si mantiene maggiore
 /// di zero, la risposta viene inviata al vicino successivo, altrimenti vuol dire che la risposta è arrivata
 /// a destinazione: in quest'ultimo caso, recupera il riferimento alla ricerca inizialmente inviata e lo usa
 /// per aggiornare i risultati della ricerca con quelli contenuti nella risposta.
 /// </summary>
 /// <param name="reply">I dati della risposta ricevuta da un vicino.</param>
 /// <remarks>
 /// Se si specifica null per l'unico parametro previsto, questo metodo non esegue alcuna azione, ignorando
 /// il parametro specificato.
 /// </remarks>
 public void HandleReceivedReply(ReplyData reply)
 {
     if (reply.TimeToLive > 0)
     {
         Task.Factory.StartNew(() => ForwardReceivedReply(reply),
             CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default);
     }
 }