/// <summary>
        /// Prova ad inviare il messaggio specificato al servizio configurato con la specifica istanza di questa classe.
        /// </summary>
        /// <param name="message">La query o la reply da inviare al servizio di un nodo vicino.</param>
        /// <param name="detection">L'eventuale istante di rilevamento del vicino.</param>
        /// <returns>true se non si sono vericati errori durante l'invio della query; in caso contrario, false.</returns>
        /// <remarks>
        /// Se si verificano errori durante la trasmissione del messaggio al servizio, questo metodo restituisce false
        /// e imposta il valore predefinito di un oggetto DateTime come istante di rilevazione, pertanto il valore non
        /// deve essere considerato. Invece, se il metodo restituisce true, vuol dire che non è avvenuto nessun errore
        /// durante la trasmissione del messaggio e quindi l'istante di rilevazione del servizio è corretto.
        /// </remarks>
        private bool TrySendMessage(MessageData message, out DateTime detection)
        {
            lock (m_SyncLock)
            {
                if (m_ProxyClosed || m_BindingConfig == null || m_RemoteUri == null)
                {
                    detection = default(DateTime);
                    return false;
                }

                try
                {
                    if (m_InternalProxy == null)
                    {
                        ChannelFactory<IQueryReplyService> factory =
                            new ChannelFactory<IQueryReplyService>(m_BindingConfig, new EndpointAddress(m_RemoteUri));

                        m_InternalProxy = factory.CreateChannel();
                    }

                    if (message is QueryData)
                    {
                        WriteToLog("Dispatching query {0} to node {1}...", message.MsgId, m_TargetNodeId);

                        m_InternalProxy.Query(m_NodeId, message as QueryData);
                    }
                    else if (message is ReplyData)
                    {
                        WriteToLog("Dispatching reply {0} to node {1}...", message.MsgId, m_TargetNodeId);

                        m_InternalProxy.Reply(m_NodeId, message as ReplyData);
                    }
                    else
                    {
                        detection = default(DateTime);
                        return false;
                    }

                    detection = DateTime.Now;
                    return true;
                }
                catch
                {
                    if (m_InternalProxy != null)
                    {
                        ICommunicationObject proxy = m_InternalProxy as ICommunicationObject;

                        if (proxy.State == CommunicationState.Faulted)
                        {
                            proxy.Abort();
                            m_InternalProxy = null;
                        }
                    }

                    detection = default(DateTime);
                    return false;
                }
            }
        }
        /// <summary>
        /// Crea una nuova istanza della classe NeighborClient usando i parametri specificati per la configurazione
        /// del proxy interno e per l'allocazione della coda dedicata ai messaggi in uscita.
        /// </summary>
        /// <param name="bindingConfig">Le impostazioni di configurazione del proxy interno.</param>
        /// <param name="remoteUri">L'uri del servizio remoto con cui il proxy deve comunicare.</param>
        /// <param name="nodeId">L'identificatore univoco del nodo rappresentato da questa istanza dell'applicazione.</param>
        /// <param name="nodeId">L'identificatore univoco del nodo di destinazione dei messaggi inviati da questa applicazione.</param>
        /// <param name="outputQueueSize">Il massimo numero di messaggi che possono essere inseriti nella coda.</param>
        /// <exception cref="ArgumentNullException">bindingConfig e/o remoteUri e/o nodeId e/o targetNodeId sono sull.</exception>
        /// <exception cref="ArgumentOutOfRangeException">outputQueueSize è minore o uguale a zero.</exception>
        /// <remarks>
        /// La configurazione del proxy interno è posticipata e viene pertanto eseguita non appena verrà richiesta
        /// la prima comunicazione col servizio remoto.
        /// </remarks>
        public NeighborClient(Binding bindingConfig, Uri remoteUri, string nodeId, string targetNodeId, int outputQueueSize)
        {
            if (bindingConfig == null)
                throw new ArgumentNullException("bindingConfig");

            if (remoteUri == null)
                throw new ArgumentNullException("remoteUri");

            if (nodeId == null)
                throw new ArgumentNullException("nodeId");

            if (targetNodeId == null)
                throw new ArgumentNullException("targetNodeId");

            if (targetNodeId == nodeId)
                throw new ArgumentException("Target node identifier must be different from current node identifier.", "targetNodeId");

            if (outputQueueSize < 1)
                throw new ArgumentOutOfRangeException("outputQueueSize", "The size must be positive.");

            m_BindingConfig = bindingConfig;
            m_RemoteUri = remoteUri;

            m_NodeId = nodeId;
            m_TargetNodeId = targetNodeId;

            m_InternalProxy = null;
            m_ProxyClosed = false;

            m_OutputQueueSize = outputQueueSize;
            m_OutputQueue = new Queue<MessageData>(outputQueueSize);

            m_Shutdown = false;
            m_Dispatcher = new Thread(Send) { IsBackground = true };
        }
        /// <summary>
        /// Rilascia le risorse utilizzate per la comunicazione col servizio remoto di query/reply e richiede
        /// lo shutdown del thread dedicato all'invio dei messaggi. Questo metodo restituisce il controllo al
        /// chiamante dopo un tempo massimo pari al timeout specificato.
        /// </summary>
        /// <param name="millisecondsTimeout">Il tempo massimo di attesa per il termine di esecuzione del thread.</param>
        /// <exception cref="ArgumentOutOfRangeException">
        /// Il valore di millisecondsTimeout è negativo e non è uguale a Timeout.Infinite in millisecondi.
        /// </exception>
        public void Dispose(int millisecondsTimeout)
        {
            lock (m_SyncLock)
            {
                if (m_InternalProxy != null)
                {
                    ICommunicationObject proxy = m_InternalProxy as ICommunicationObject;

                    try
                    {
                        proxy.Close();
                    }
                    catch
                    {
                        if (proxy.State == CommunicationState.Faulted)
                        {
                            proxy.Abort();
                        }
                    }
                    finally
                    {
                        m_InternalProxy = null;
                        m_ProxyClosed = true;
                    }
                }

                if (!m_Shutdown)
                {
                    m_Shutdown = true;
                    Monitor.Pulse(m_SyncLock);
                }
            }

            m_Dispatcher.Join(millisecondsTimeout);
        }