예제 #1
0
        // This updates routing table when receiving a message.
        // if corresponding bucket for remote peer is not full, just adds remote peer.
        // otherwise check whether if the least recently used (LRU) peer
        // is alive to determine evict LRU peer or discard remote peer.
        private async Task UpdateAsync(
            Peer rawPeer,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            if (rawPeer is null)
            {
                throw new ArgumentNullException(nameof(rawPeer));
            }

            if (!(rawPeer is BoundPeer peer) || rawPeer.AppProtocolVersion != _appProtocolVersion)
            {
                // Don't update peer without endpoint or with different appProtocolVersion.
                return;
            }

            if (cancellationToken.IsCancellationRequested)
            {
                throw new TaskCanceledException();
            }

            bool      contains          = _routing.Contains(peer);
            BoundPeer evictionCandidate = await _routing.AddPeerAsync(peer);

            if (evictionCandidate is null)
            {
                // added successfully since there was empty space in the bucket
                if (!contains)
                {
                    _routing.BucketOf(peer).ReplacementCache.Remove(peer);
                    _logger.Debug($"Added [{peer.Address.ToHex()}] to table");
                }
            }
            else
            {
                _logger.Verbose(
                    "Need to evict {Candidate}; trying...",
                    evictionCandidate);
                try
                {
                    if (!_routing.BucketOf(peer).ReplacementCache.Contains(peer))
                    {
                        _routing.BucketOf(peer).ReplacementCache.Add(peer);
                    }

                    await PingAsync(
                        evictionCandidate,
                        _requestTimeout,
                        cancellationToken);
                }
                catch (TimeoutException)
                {
                    await RemovePeerAsync(evictionCandidate);

                    _logger.Verbose(
                        "Peer ({Candidate}) has been evicted.",
                        evictionCandidate);
                }
            }
        }
예제 #2
0
        /// <summary>
        /// Process <see cref="Peer"/>s that is replied by sending <see cref="FindNeighbors"/>
        /// request.
        /// </summary>
        /// <param name="found"><see cref="Peer"/>s that found.</param>
        /// <param name="target">The target <see cref="Address"/> to search.</param>
        /// <param name="depth">Target depth of recursive operation. If -1 is given,
        /// it runs until the closest peer is found.</param>
        /// <param name="timeout"><see cref="TimeSpan"/> for next depth's
        /// <see cref="FindPeerAsync"/> operation.</param>
        /// <param name="cancellationToken">A cancellation token used to propagate notification
        /// that this operation should be canceled.</param>
        /// <returns>An awaitable task without value.</returns>
        /// <exception cref="TimeoutException">Thrown when all peers that found are
        /// not online.</exception>
        private async Task ProcessFoundAsync(
            ImmutableList <BoundPeer> found,
            Address target,
            int depth,
            TimeSpan?timeout,
            CancellationToken cancellationToken)
        {
            List <BoundPeer> peers = found.Where(
                peer => !peer.Address.Equals(_address) && !_routing.Contains(peer)).ToList();

            if (peers.Count == 0)
            {
                _logger.Debug("No any neighbor received.");
                return;
            }

            peers = Kademlia.SortByDistance(peers, target);

            List <BoundPeer> closestCandidate = _routing.Neighbors(target, _bucketSize).ToList();

            Task[] awaitables = peers.Select(peer =>
                                             PingAsync(peer, _requestTimeout, cancellationToken)
                                             ).ToArray();
            try
            {
                await Task.WhenAll(awaitables);
            }
            catch (AggregateException e)
            {
                if (e.InnerExceptions.All(ie => ie is TimeoutException) &&
                    e.InnerExceptions.Count == awaitables.Length)
                {
                    throw new TimeoutException(
                              $"All neighbors found do not respond in {_requestTimeout}."
                              );
                }

                _logger.Error(
                    e,
                    "Some responses from neighbors found unexpectedly terminated: {Exception}",
                    e
                    );
            }

            var  findNeighboursTasks = new List <Task>();
            Peer closestKnown        = closestCandidate.Count == 0 ? null : closestCandidate[0];

            for (int i = 0; i < Kademlia.FindConcurrency && i < peers.Count; i++)
            {
                if (closestKnown is null ||
                    string.CompareOrdinal(
                        Kademlia.CalculateDistance(peers[i].Address, target).ToHex(),
                        Kademlia.CalculateDistance(closestKnown.Address, target).ToHex()
                        ) < 1)
                {
                    findNeighboursTasks.Add(FindPeerAsync(
                                                target,
                                                peers[i],
                                                (depth == -1) ? depth : depth + 1,
                                                timeout,
                                                cancellationToken));
                }
            }

            try
            {
                await Task.WhenAll(findNeighboursTasks);
            }
            catch (TimeoutException)
            {
                if (findNeighboursTasks.All(findPeerTask => findPeerTask.IsFaulted))
                {
                    throw new TimeoutException(
                              "Timeout exception occurred during ProcessFoundAsync().");
                }
            }
        }
예제 #3
0
        /// <summary>
        /// Process <see cref="Peer"/>s that is replied by sending <see cref="FindNeighbors"/>
        /// request.
        /// </summary>
        /// <param name="history"><see cref="Peer"/>s that already searched.</param>
        /// <param name="dialHistory"><see cref="Peer"/>s that ping sent.</param>
        /// <param name="found"><see cref="Peer"/>s that found.</param>
        /// <param name="target">The target <see cref="Address"/> to search.</param>
        /// <param name="depth">Target depth of recursive operation. If -1 is given,
        /// it runs until the closest peer is found.</param>
        /// <param name="timeout"><see cref="TimeSpan"/> for next depth's
        /// <see cref="FindPeerAsync"/> operation.</param>
        /// <param name="cancellationToken">A cancellation token used to propagate notification
        /// that this operation should be canceled.</param>
        /// <returns>An awaitable task without value.</returns>
        /// <exception cref="TimeoutException">Thrown when all peers that found are
        /// not online.</exception>
        private async Task ProcessFoundAsync(
            ConcurrentBag <BoundPeer> history,
            ConcurrentBag <BoundPeer> dialHistory,
            IEnumerable <BoundPeer> found,
            Address target,
            int depth,
            TimeSpan?timeout,
            CancellationToken cancellationToken)
        {
            List <BoundPeer> peers = found.Where(
                peer =>
                !peer.Address.Equals(_address) &&
                !_table.Contains(peer) &&
                !history.Contains(peer)).ToList();

            if (peers.Count == 0)
            {
                _logger.Verbose("No any neighbor received.");
                return;
            }

            peers = Kademlia.SortByDistance(peers, target).ToList();

            List <BoundPeer> closestCandidate =
                _table.Neighbors(target, _table.BucketSize, false).ToList();

            Task[] awaitables = peers
                                .Where(peer => !dialHistory.Contains(peer))
                                .Select(
                peer =>
            {
                dialHistory.Add(peer);
                return(PingAsync(peer, _requestTimeout, cancellationToken));
            }
                ).ToArray();
            try
            {
                await Task.WhenAll(awaitables);
            }
            catch (Exception)
            {
                IEnumerable <AggregateException> exceptions = awaitables
                                                              .Where(t => t.IsFaulted)
                                                              .Select(t => t.Exception);
                foreach (var ae in exceptions)
                {
                    var isTimeout = false;
                    foreach (var ie in ae.InnerExceptions)
                    {
                        if (ie is PingTimeoutException pte)
                        {
                            peers.Remove(pte.Target);
                            isTimeout = true;
                            break;
                        }
                    }

                    if (isTimeout)
                    {
                        break;
                    }

                    _logger.Warning(
                        ae,
                        "Some responses from neighbors found unexpectedly terminated: {ae}",
                        ae
                        );
                }
            }

            var  findPeerTasks = new List <Task>();
            Peer closestKnown  = closestCandidate.FirstOrDefault();
            var  count         = 0;

            foreach (var peer in peers)
            {
                if (!(closestKnown is null) &&
                    string.CompareOrdinal(
                        Kademlia.CalculateDistance(peer.Address, target).ToHex(),
                        Kademlia.CalculateDistance(closestKnown.Address, target).ToHex()
                        ) >= 1)
                {
                    break;
                }

                if (history.Contains(peer))
                {
                    continue;
                }

                findPeerTasks.Add(FindPeerAsync(
                                      history,
                                      dialHistory,
                                      target,
                                      peer,
                                      depth == -1 ? depth : depth - 1,
                                      timeout,
                                      cancellationToken));
                if (count++ >= _findConcurrency)
                {
                    break;
                }
            }

            try
            {
                await Task.WhenAll(findPeerTasks);
            }
            catch (Exception e)
            {
                _logger.Error(
                    e,
                    "Some FindPeer tasks were unexpectedly terminated: {Exception}",
                    e
                    );
            }
        }
예제 #4
0
        /// <summary>
        /// Process <see cref="Peer"/>s that is replied by sending <see cref="FindNeighbors"/>
        /// request.
        /// </summary>
        /// <param name="history"><see cref="Peer"/>s that already searched.</param>
        /// <param name="found"><see cref="Peer"/>s that found.</param>
        /// <param name="target">The target <see cref="Address"/> to search.</param>
        /// <param name="depth">Target depth of recursive operation. If -1 is given,
        /// it runs until the closest peer is found.</param>
        /// <param name="timeout"><see cref="TimeSpan"/> for next depth's
        /// <see cref="FindPeerAsync"/> operation.</param>
        /// <param name="cancellationToken">A cancellation token used to propagate notification
        /// that this operation should be canceled.</param>
        /// <returns>An awaitable task without value.</returns>
        /// <exception cref="TimeoutException">Thrown when all peers that found are
        /// not online.</exception>
        private async Task ProcessFoundAsync(
            ConcurrentBag <BoundPeer> history,
            IEnumerable <BoundPeer> found,
            Address target,
            int depth,
            TimeSpan?timeout,
            CancellationToken cancellationToken)
        {
            List <BoundPeer> peers = found.Where(
                peer =>
                !peer.Address.Equals(_address) &&
                !_routing.Contains(peer) &&
                !history.Contains(peer)).ToList();

            if (peers.Count == 0)
            {
                _logger.Debug("No any neighbor received.");
                return;
            }

            peers = Kademlia.SortByDistance(peers, target);

            List <BoundPeer> closestCandidate = _routing.Neighbors(target, _bucketSize).ToList();

            Task[] awaitables = peers.Select(peer =>
                                             PingAsync(peer, _requestTimeout, cancellationToken)
                                             ).ToArray();
            try
            {
                await Task.WhenAll(awaitables);
            }
            catch (Exception e)
            {
                _logger.Error(
                    e,
                    "Some responses from neighbors found unexpectedly terminated: {Exception}",
                    e
                    );
            }

            var  findPeerTasks = new List <Task>();
            Peer closestKnown  = closestCandidate.Count == 0 ? null : closestCandidate[0];
            var  count         = 0;

            foreach (var peer in peers)
            {
                if (!(closestKnown is null) &&
                    string.CompareOrdinal(
                        Kademlia.CalculateDistance(peer.Address, target).ToHex(),
                        Kademlia.CalculateDistance(closestKnown.Address, target).ToHex()
                        ) >= 1)
                {
                    break;
                }

                if (history.Contains(peer))
                {
                    continue;
                }

                findPeerTasks.Add(FindPeerAsync(
                                      history,
                                      target,
                                      peer,
                                      depth == -1 ? depth : depth - 1,
                                      timeout,
                                      cancellationToken));
                if (count++ >= Kademlia.FindConcurrency)
                {
                    break;
                }
            }

            try
            {
                await Task.WhenAll(findPeerTasks);
            }
            catch (Exception e)
            {
                _logger.Error(
                    e,
                    "Some FindPeer tasks were unexpectedly terminated: {Exception}",
                    e
                    );
            }
        }