Esempio n. 1
0
        /// <summary>
        /// Returns at most 2 * <paramref name="k"/> (2 * <paramref name="k"/> + 1 if
        /// <paramref name="includeTarget"/> is <c>true</c>) nearest peers to given parameter peer
        /// from routing table. Return value is sorted with respect to target.
        /// <seealso cref="Kademlia.SortByDistance(IEnumerable{BoundPeer}, Address)"/>
        /// </summary>
        /// <param name="target"><see cref="Address"/> to look up.</param>
        /// <param name="k">Number of peers to return.</param>
        /// <param name="includeTarget">A boolean value indicates to include a peer with
        /// <see cref="Address"/> of <paramref name="target"/> in return value or not.</param>
        /// <returns>An enumerable of <see cref="BoundPeer"/>.</returns>
        public IEnumerable <BoundPeer> Neighbors(Address target, int k, bool includeTarget)
        {
            var sorted = _buckets
                         .Where(b => !b.IsEmpty())
                         .SelectMany(b => b.Peers)
                         .ToList();

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

            // Select maximum k * 2 peers excluding the target itself.
            bool containsTarget = sorted.Any(peer => peer.Address.Equals(target));
            int  maxCount       = (includeTarget && containsTarget) ? k * 2 + 1 : k * 2;

            IEnumerable <BoundPeer> peers = includeTarget
                ? sorted
                : sorted.Where(peer => !peer.Address.Equals(target));

            return(peers.Take(maxCount));
        }
Esempio n. 2
0
        // returns k nearest peers to given parameter peer from routing table.
        // return value is already sorted with respect to target.
        public IEnumerable <BoundPeer> Neighbors(Address target, int k)
        {
            var sorted = _buckets
                         .Where(b => !b.IsEmpty())
                         .SelectMany(b => b.Peers)
                         .ToList();

            sorted = Kademlia.SortByDistance(sorted, target);
            var peers = new List <BoundPeer>();

            foreach (var peer in sorted.Where(peer => !peer.Address.Equals(target)))
            {
                peers.Add(peer);
                if (peers.Count >= k * 2)
                {
                    break;
                }
            }

            return(peers);
        }
Esempio n. 3
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().");
                }
            }
        }
Esempio n. 4
0
        internal int GetBucketIndexOf(Address addr)
        {
            int plength = Kademlia.CommonPrefixLength(addr, _address);

            return(Math.Min(plength, TableSize - 1));
        }
Esempio n. 5
0
        private int GetBucketIndexOf(Peer peer)
        {
            int plength = Kademlia.CommonPrefixLength(peer.Address, _address);

            return(Math.Min(plength, _tableSize - 1));
        }
Esempio n. 6
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
                    );
            }
        }
Esempio n. 7
0
        private async Task <BoundPeer> ProcessFoundForSpecificAsync(
            ConcurrentBag <BoundPeer> history,
            IEnumerable <BoundPeer> found,
            Address target,
            int depth,
            TimeSpan?timeout,
            CancellationToken cancellationToken,
            Address searchAddress)
        {
            BoundPeer        peerFound = null;
            List <BoundPeer> peers     = found.Where(
                peer => !peer.Address.Equals(_address)).ToList();

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

            peers = Kademlia.SortByDistance(peers, target);

            List <BoundPeer> closestNeighbors = _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
                    );
            }

            for (int i = 0; i < closestNeighbors.Count; i++)
            {
                if (string.CompareOrdinal(
                        closestNeighbors[i].Address.ToHex(),
                        searchAddress.ToHex()
                        ) == 0 && _routing.Contains(closestNeighbors[i]))
                {
                    peerFound = closestNeighbors[i];
                    return(peerFound);
                }
            }

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

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

            var foundSpecificPeer = new List <BoundPeer>();

            try
            {
                foundSpecificPeer.AddRange(await Task.WhenAll(findNeighboursTasks));
            }
            catch (TimeoutException)
            {
                if (findNeighboursTasks.All(findPeerTask => findPeerTask.IsFaulted))
                {
                    throw new TimeoutException(
                              "Timeout exception occurred during " +
                              $"{nameof(ProcessFoundForSpecificAsync)}.");
                }
            }

            for (int i = 0; i < foundSpecificPeer.Count; i++)
            {
                if (string.CompareOrdinal(
                        foundSpecificPeer[i].Address.ToHex(),
                        searchAddress.ToHex()
                        ) == 0 && _routing.Contains(foundSpecificPeer[i]))
                {
                    peerFound = foundSpecificPeer[i];
                    return(peerFound);
                }
            }

            return(peerFound);
        }
Esempio n. 8
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
                    );
            }
        }
Esempio n. 9
0
        private async Task ProcessFoundAsync(
            ImmutableList <BoundPeer> found,
            Address target,
            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();

            bool foundPingTimeout = true;

            foreach (BoundPeer peer in peers)
            {
                try
                {
                    // This timeout should be request timeout?
                    await PingAsync(peer, RequestTimeout, cancellationToken);

                    foundPingTimeout = false;
                }
                catch (TimeoutException)
                {
                    continue;
                }
            }

            if (foundPingTimeout)
            {
                _logger.Debug("All neighbors found are invalid.");
                throw new TimeoutException();
            }

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

            for (int i = 0; i < 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], timeout, cancellationToken));
                }
            }

            try
            {
                await Task.WhenAll(findNeighboursTasks);
            }
            catch (TimeoutException)
            {
                if (findNeighboursTasks.All(findPeerTask => findPeerTask.IsFaulted))
                {
                    throw new TimeoutException(
                              "Timeout exception occurred during ProcessFoundAsync().");
                }
            }
        }