/// <summary> /// Permette di ricevere una richiesta da un vicino. /// </summary> /// <param name="sourceNodeId">L'identificatore associato al vicino.</param> /// <param name="query">La richiesta inviata dal vicino.</param> public void Query(string sourceNodeId, QueryData query) { if (m_KnownNeighbors.Contains(sourceNodeId)) { WriteToLog("The received query from node {0} has been accepted: {1}.", sourceNodeId, query.MsgId); m_MessagesHandler.HandleReceivedQuery(sourceNodeId, query); } else { WriteToLog("The received query from node {0} has been dropped: {1}.", sourceNodeId, query.MsgId); } }
/// <summary> /// Avvia una nuova ricerca in base ai dati di ricerca specificati, creando un nuovo messaggio di richiesta /// con un valore di TimeToLive impostato secondo la configurazione e inviandolo a tutti gli attuali vicini /// di questo nodo della rete. Nell'eventualità remota che l'identificativo generato per il nuovo messaggio /// corrisponda ad uno degli identificativi attualmente presenti nella tabella di inoltro, il messaggio non /// viene inviato e questo metodo restituisce false; in caso contrario, restituisce true. /// </summary> /// <param name="data">I dati che rappresentano la ricerca da effettuare.</param> /// <returns>true se richiesta relativa alla ricerca viene inviata ai vicini; in caso contrario, false.</returns> public bool CreateNewSearch(SearchData data) { Guid msgId = Guid.NewGuid(); lock (m_ForwardingTableLocker) { if (!m_ForwardingTable.Add(msgId, null, data)) { return false; // GUID duplicato } } WriteToLog("Sending query {0}: search data = {1}...", msgId, data); QueryData query = new QueryData(msgId, m_MessageInitialTtl, 0) { Options = data.GetSearchOptions() }; SendQuery(query, null); return true; }
/// <summary> /// Invia la richiesta specificata a tutte le connessioni attive relative ai vicini, escludendo l'eventuale /// connessione specificata come destinazione della richiesta. /// </summary> /// <param name="query">I dati della richiesta da inviare.</param> /// <param name="excludedConnectionId">L'identificativo della connessione da escludere.</param> /// <exception cref="ArgumentNullException">query è null.</exception> /// <remarks> /// Per inviare la richiesta specificata a tutti i vicini, è sufficiente specificare null come identificativo /// della connessione da escludere: in tal modo non verrà esclusa nessuna connessione tra tutte quelle attive. /// </remarks> private void SendQuery(QueryData query, string excludedConnectionId) { m_NeighborhoodManager.SendQuery(query, excludedConnectionId); WriteToLog("Query {0} sent to all connections. Excluded connection: {1}.", query.MsgId, (excludedConnectionId != null ? excludedConnectionId : "none")); }
/// <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 la richiesta ricevuta da un vicino, verificando se deve essere inoltrata ai restanti vicini /// di questo nodo, oppure se deve essere scartata perché scaduta. La procedura prevede l'aggiornamento /// preliminare di due proprietà relative alla richiesta: il TimeToLive viene decrementato di 1, mentre /// HopsCount viene incrementato di 1. Se il TimeToLive si è azzerato, la richiesta non viene inoltrata /// ai restanti vicini, altrimenti verifica che l'identificativo del messaggio non sia già nella tabella /// di inoltro, prima di inserirlo in essa ed inoltrare la richiesta agli altri vicini. Invece, qualora /// l'identificativo sia già presente nella tabella di inoltro, la richiesta non viene inoltrata. /// </summary> /// <param name="sourceConnectionId">L'identificativo della connessione di provenienza della richiesta.</param> /// <param name="query">I dati della richiesta ricevuta da un vicino.</param> /// <remarks> /// Se si specifica null per almeno uno dei due parametri, questo metodo non esegue alcuna azione, ignorando /// i parametri specificati. /// </remarks> private void ForwardReceivedQuery(string sourceConnectionId, QueryData query) { Thread.Sleep(100); // simula ritardo di rete if (string.IsNullOrWhiteSpace(sourceConnectionId) || query == null || query.TimeToLive == 0) return; query.TimeToLive--; query.HopsCount++; if (query.TimeToLive > 0) // verifica se la query deve essere inoltrata { WriteToLog("Forwarding query {0}...", query.MsgId); lock (m_ForwardingTableLocker) { if (!m_ForwardingTable.Add(query.MsgId, sourceConnectionId, null)) { return; // ignora messaggio duplicato } } SendQuery(query, sourceConnectionId); // inoltra messaggio ai restanti vicini } else { lock (m_ForwardingTableLocker) { if (m_ForwardingTable.ContainsEntry(query.MsgId)) { return; // ignora messaggio duplicato } } WriteToLog("Query {0} from connection {1} has expired.", query.MsgId, sourceConnectionId); } }
/// <summary> /// Gestisce una richiesta ricevuta da un vicino, verificando se deve essere inoltrata ai restanti vicini ed /// elaborandola per fornire un'eventuale risposta destinata alla connessione di provenienza della richiesta. /// La procedura prevede l'aggiornamento preliminare di due proprietà relative alla richiesta: il TimeToLive /// viene decrementato di 1, mentre HopsCount viene incrementato di 1. Se il TimeToLive è diventato zero, la /// richiesta non viene inoltrata ai restanti vicini, altrimenti verifica che l'identificativo del messaggio /// non sia già presente nella tabella di inoltro, prima di inserirlo in essa ed inoltrare la richiesta agli /// altri vicini, per poi iniziare l'elaborazione in background della richiesta. Invece, se l'identificativo /// è già presente nella tabella di inoltro, la richiesta non viene né inoltrata né elaborata. /// </summary> /// <param name="sourceConnectionId">L'identificativo della connessione di provenienza della richiesta.</param> /// <param name="query">I dati della richiesta ricevuta da un vicino.</param> /// <remarks> /// Se si specifica null per almeno uno dei due parametri, questo metodo non esegue alcuna azione, ignorando /// i parametri specificati. /// </remarks> public void HandleReceivedQuery(string sourceConnectionId, QueryData query) { if (query.TimeToLive > 0) { Task.Factory.StartNew(() => ForwardReceivedQuery(sourceConnectionId, query), CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default); // Inizia l'elaborazione in background della richiesta. Task.Factory.StartNew(() => ReplyToQuery(query, sourceConnectionId), CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default); } }