/// <summary>
        /// Gets the peers from DHT and generates AnnounceParameters of a list of
        /// peers and puts the peer info of this announcing peer to DHT
        /// </summary>
        /// <param name="parameters">
        /// AnnounceParameters from the requesting client. It's modified in method.
        /// </param>
        internal void HandleAnnounceRequest(AnnounceParameters parameters)
        {
            IEnumerable <PeerEntry> entries = _proxy.GetPeers(parameters.InfoHash.ToArray());

            foreach (PeerEntry entry in entries)
            {
                AnnounceParameters par = GenerateAnnounceParameters(
                    parameters.InfoHash.ToArray(), entry);
                if (par.IsValid)
                {
                    // Tracker will write to the par.Response but we don't use it
                    // ListenerBase.Handle use par.ClientAddress to generate
                    // AnnounceParameters again.
                    Handle(par.Parameters, par.ClientAddress.Address, false);
                }
                else
                {
                    Logger.WriteLineIf(LogLevel.Error, _log_props, string.Format(
                                           "Parameters invalid!"));
                    continue;
                }

                //Logger.WriteLineIf(LogLevel.Verbose, _log_props,
                //    string.Format("Tracker's response for this peer {0} from DHT: {1}",
                //    par.ClientAddress, par.Response.ToString()));
            }

            // Got all I need, now announce myself to DHT.
            try {
                _proxy.AnnouncePeer(parameters.InfoHash.ToArray(), parameters);
            } catch (Exception ex) {
                // It is OK to temporarily unable to announce to Dht. We can try the next time.
                Logger.WriteLineIf(LogLevel.Error, _log_props, string.Format(
                                       "Unable to reach Dht. We can try the next time.\n{0}", ex));
            }

            Logger.WriteLineIf(LogLevel.Info, _log_props,
                               string.Format("DhtListener finished handling annoucement from {0}",
                                             parameters.RemoteAddress));
        }
        /// <summary>
        /// Downloads the torrent of the piece and write it to
        /// </summary>
        /// <param name="nameSpace">The name space.</param>
        /// <param name="name">The name.</param>
        /// <param name="wholeTorrent">The whole torrent.</param>
        /// <param name="pieceIndex">Index of the piece.</param>
        byte[] DownloadPieceTorrent(string nameSpace, string name, Torrent wholeTorrent,
                                    int pieceIndex)
        {
            var wholeTorrentDhtKeyStr = ServiceUtil.GetDictKeyString(nameSpace, name);
            var pieceKeyStr           = MakePieceTorrentKey(wholeTorrentDhtKeyStr, pieceIndex);
            var torrentFilePath       = _bittorrentCache.GetTorrentFilePath(nameSpace, name);

            byte[] pieceTorrentBytes;
            bool   succ = _torrentHelper.TryReadOrDownloadTorrent(
                nameSpace, name, _dhtProxy, out pieceTorrentBytes);

            if (succ)
            {
                // Somebody else already requested this piece.
                return(pieceTorrentBytes);
            }
            else
            {
                // We need to request the seeder to make a new piece.
                IEnumerable <PeerEntry> peers = _dhtProxy.GetPeers(wholeTorrent.InfoHash.ToArray());
                bool foundCompletePeer        = false;

                // The list most likely has only one entry.
                var trackerIPs = new List <string>();
                foreach (var tiers in wholeTorrent.AnnounceUrls)
                {
                    foreach (var trackerUrl in tiers)
                    {
                        // Given that we don't use domain names for hosts.
                        trackerIPs.Add(new Uri(trackerUrl).Host);
                    }
                }

                // Completed -> Downloader has the whole data.
                // PeerIP == Tracker -> Original Seeder(s).
                var peerIPs = (from peer in peers
                               where (peer.PeerState == TorrentEvent.Completed ||
                                      trackerIPs.Contains(peer.PeerIP))
                               select peer.PeerIP).Distinct();
                Logger.WriteLineIf(LogLevel.Verbose, _log_props, string.Format(
                                       "{0} peers to try for piece info.", peerIPs.Count()));

                foreach (var peerIP in peerIPs)
                {
                    foundCompletePeer = true;
                    // The first complete peer should be able to serve the piece we want.
                    bool succPieceInfo = TryDownloadPieceTorrentFromPeer(nameSpace,
                                                                         name, pieceIndex, peerIP, _pieceInfoServerPort,
                                                                         out pieceTorrentBytes);
                    if (succPieceInfo)
                    {
                        Logger.WriteLineIf(LogLevel.Verbose, _log_props,
                                           string.Format("Piece info downloaded from {0}", peerIP));
                        return(pieceTorrentBytes);
                    }
                    else
                    {
                        // Try the next completePeer
                        continue;
                    }
                }

                if (!foundCompletePeer)
                {
                    throw new DictionaryKeyNotFoundException("No peer is complete.");
                }
                else if (pieceTorrentBytes == null)
                {
                    throw new DictionaryKeyNotFoundException(
                              "No complete peer(s) can return the piece Info");
                }
                else
                {
                    // This shouldn't happen
                    return(pieceTorrentBytes);
                }
            }
        }