private async Task <ImmutableList <BoundPeer> > QueryNeighborsAsync( Address target, TimeSpan?timeout, CancellationToken cancellationToken) { List <BoundPeer> neighbors = _routing.Neighbors(target, _bucketSize).ToList(); var found = new List <BoundPeer>(); int count = (neighbors.Count < Kademlia.FindConcurrency) ? neighbors.Count : Kademlia.FindConcurrency; bool timeoutOccurred = true; for (int i = 0; i < count; i++) { try { found.AddRange( await GetNeighbors(neighbors[i], target, timeout, cancellationToken)); timeoutOccurred = false; } catch (TimeoutException) { continue; } } if (count != 0 && timeoutOccurred) { _logger.Debug($"Timeout occurred during {nameof(QueryNeighborsAsync)}."); throw new TimeoutException( $"Timeout occurred during {nameof(QueryNeighborsAsync)}."); } return(found.ToImmutableList()); }
private async Task <IEnumerable <BoundPeer> > QueryNeighborsAsync( ConcurrentBag <BoundPeer> history, Address target, TimeSpan?timeout, CancellationToken cancellationToken) { List <BoundPeer> neighbors = _routing.Neighbors(target, _bucketSize).ToList(); var found = new List <BoundPeer>(); int count = neighbors.Count < Kademlia.FindConcurrency ? neighbors.Count : Kademlia.FindConcurrency; var timeoutOccurred = true; for (var i = 0; i < count; i++) { try { var peers = await GetNeighbors(neighbors[i], target, timeout, cancellationToken); history.Add(neighbors[i]); found.AddRange(peers.Where(peer => !found.Contains(peer))); timeoutOccurred = false; } catch (TimeoutException) { } } if (count != 0 && timeoutOccurred) { _logger.Debug($"Timeout occurred during {nameof(QueryNeighborsAsync)}."); throw new TimeoutException( $"Timeout occurred during {nameof(QueryNeighborsAsync)}."); } return(found); }
/// <summary> /// Use <see cref="FindNeighbors"/> messages to to find a <see cref="BoundPeer"/> with /// <see cref="Address"/> of <paramref name="target"/>. /// </summary> /// <param name="target">The <see cref="Address"/> to find.</param> /// <param name="depth">Target depth of recursive operation.</param> /// <param name="timeout"><see cref="TimeSpan"/> for waiting reply of /// <see cref="FindNeighbors"/>.</param> /// <param name="cancellationToken">A cancellation token used to propagate notification /// that this operation should be canceled.</param> /// <returns>A <see cref="BoundPeer"/> with <see cref="Address"/> of /// <paramref name="target"/>.</returns> public async Task <BoundPeer> FindSpecificPeerAsync( Address target, int depth, TimeSpan?timeout, CancellationToken cancellationToken) { _logger.Verbose( $"{nameof(FindSpecificPeerAsync)}() with {{Target}}. " + "(depth: {Depth})", target, depth); if (_table.GetPeer(target) is BoundPeer boundPeer) { try { await PingAsync(boundPeer, _requestTimeout, cancellationToken); } catch (PingTimeoutException) { var msg = "{BoundPeer}, a target peer, is in the routing table does not respond."; _logger.Verbose(msg, boundPeer); RemovePeer(boundPeer); return(null); } _logger.Verbose( "{BoundPeer}, a target peer, is in the routing table.", boundPeer); return(boundPeer); } var history = new ConcurrentBag <BoundPeer>(); var peersToFind = new ConcurrentQueue <Tuple <BoundPeer, int> >(); foreach (BoundPeer peer in _table.Neighbors(target, _findConcurrency, false)) { peersToFind.Enqueue(new Tuple <BoundPeer, int>(peer, 0)); } while (peersToFind.Any()) { cancellationToken.ThrowIfCancellationRequested(); if (!peersToFind.TryDequeue(out Tuple <BoundPeer, int> tuple)) { continue; } tuple.Deconstruct(out BoundPeer viaPeer, out int curDepth); _logger.Debug("ViaPeer: {Peer}, curDepth: {curDepth}", viaPeer, curDepth); if (depth != -1 && curDepth >= depth) { continue; } history.Add(viaPeer); IEnumerable <BoundPeer> foundPeers = await GetNeighbors(viaPeer, target, timeout, cancellationToken); IEnumerable <BoundPeer> filteredPeers = foundPeers .Where(peer => !history.Contains(peer) && !peersToFind.Any(t => t.Item1.Equals(peer)) && !peer.Address.Equals(_address)) .Take(_findConcurrency); int count = 0; foreach (var found in filteredPeers) { try { await PingAsync(found, _requestTimeout, cancellationToken); if (found.Address.Equals(target)) { return(found); } peersToFind.Enqueue(new Tuple <BoundPeer, int>(found, curDepth + 1)); if (count++ >= _findConcurrency) { break; } } catch (TaskCanceledException) { throw new TaskCanceledException( $"Task is cancelled during {nameof(FindSpecificPeerAsync)}()"); } catch (PingTimeoutException) { // Ignore peer not responding } finally { history.Add(found); } } } return(null); }