Esempio n. 1
0
        /// <summary>
        /// Request piece blocks from passed in peer list.
        /// </summary>
        /// <param name="tc"></param>
        /// <param name="pieceNumber"></param>
        /// <param name="remotePeers"></param>
        /// <param name="stopwatch"></param>
        private bool GetMoreBlocks(TorrentContext tc, UInt32 pieceNumber, Peer[] remotePeers)
        {
            bool success = true;

            tc.assemblyData.guardMutex.WaitOne();
            try
            {
                UInt32 blockOffset = 0;
                int    currentPeer = 0;
                tc.assemblyData.currentBlockRequests = 0;
                foreach (var blockThere in tc.assemblyData.pieceBuffer.BlocksPresent)
                {
                    if (!blockThere)
                    {
                        PWP.Request(remotePeers[currentPeer], pieceNumber, blockOffset, Math.Min(Constants.BlockSize, tc.GetPieceLength(pieceNumber) - blockOffset));
                        remotePeers[currentPeer].OutstandingRequestsCount++;
                        if (++tc.assemblyData.currentBlockRequests == _maximumBlockRequests)
                        {
                            break;
                        }
                        currentPeer = (currentPeer + 1) % (int)remotePeers.Length;
                    }
                    blockOffset += Constants.BlockSize;
                }
            }
            catch (Exception ex)
            {
                Log.Logger.Error(ex);
                success = false;
            }
            tc.assemblyData.guardMutex.ReleaseMutex();
            return(success);
        }
Esempio n. 2
0
        internal BlockingCollection <PieceRequest> pieceRequestQueue;  // Piece request read queue
        /// <summary>
        /// Read/Write piece buffers to/from torrent on disk.
        /// </summary>
        /// <param name="tc"></param>
        /// <param name="transferBuffer"></param>
        /// <param name="read"></param>
        /// <summary>
        private void TransferPiece(TorrentContext tc, PieceBuffer transferBuffer, bool read)
        {
            int    bytesTransferred = 0;
            UInt64 startOffset      = transferBuffer.Number * tc.pieceLength;
            UInt64 endOffset        = startOffset + transferBuffer.Length;

            foreach (var file in tc.filesToDownload)
            {
                if ((startOffset <= (file.offset + file.length)) && (file.offset <= endOffset))
                {
                    UInt64 startTransfer = Math.Max(startOffset, file.offset);
                    UInt64 endTransfer   = Math.Min(endOffset, file.offset + file.length);
                    using (Stream stream = new FileStream(file.name, FileMode.Open))
                    {
                        stream.Seek((Int64)(startTransfer - file.offset), SeekOrigin.Begin);
                        if (read)
                        {
                            stream.Read(transferBuffer.Buffer, (Int32)(startTransfer % tc.pieceLength), (Int32)(endTransfer - startTransfer));
                        }
                        else
                        {
                            stream.Write(transferBuffer.Buffer, (Int32)(startTransfer % tc.pieceLength), (Int32)(endTransfer - startTransfer));
                        }
                    }
                    bytesTransferred += (Int32)(endTransfer - startTransfer);
                    if (bytesTransferred == tc.GetPieceLength(transferBuffer.Number))
                    {
                        break;
                    }
                }
            }
        }
Esempio n. 3
0
        /// <summary>
        /// Creates the torrent bitfield and piece information structures from the current disc
        /// which details the state of the torrent. ie. what needs to be downloaded still.
        /// </summary>
        /// <param name="tc"></param>
        internal void CreateTorrentBitfield(TorrentContext tc)
        {
            if (tc is null)
            {
                throw new ArgumentNullException(nameof(tc));
            }
            byte[] pieceBuffer   = new byte[tc.pieceLength];
            UInt32 pieceNumber   = 0;
            UInt32 bytesInBuffer = 0;
            int    bytesRead     = 0;

            Log.Logger.Debug("Generate pieces downloaded map from local files ...");
            foreach (var file in tc.filesToDownload)
            {
                Log.Logger.Debug($"File: {file.name}");
                using (var inFileSteam = new FileStream(file.name, FileMode.Open))
                {
                    while ((bytesRead = inFileSteam.Read(pieceBuffer, (Int32)bytesInBuffer, pieceBuffer.Length - (Int32)bytesInBuffer)) > 0)
                    {
                        bytesInBuffer += (UInt32)bytesRead;
                        if (bytesInBuffer == tc.pieceLength)
                        {
                            UpdateBitfieldFromBuffer(tc, pieceNumber, pieceBuffer, bytesInBuffer);
                            bytesInBuffer = 0;
                            pieceNumber++;
                        }
                    }
                }
            }
            if (bytesInBuffer > 0)
            {
                UpdateBitfieldFromBuffer(tc, pieceNumber, pieceBuffer, bytesInBuffer);
            }
            Log.Logger.Debug("Finished generating downloaded map.");
        }
Esempio n. 4
0
        /// <summary>
        /// Request a piece block by block and wait for it to be assembled. If a timeout happens
        /// during assembly close the offending peers and start again with a different piece.
        /// </summary>
        /// <param name="tc"></param>
        /// <param name="pieceNumber"></param>
        /// <param name="waitHandles"></param>
        /// <returns></returns>
        private bool GetPieceFromPeers(TorrentContext tc, UInt32 pieceNumber, WaitHandle[] waitHandles)
        {
            Peer[] remotePeers = tc.selector.GetListOfPeers(tc, pieceNumber, _maximumBlockRequests);
            if (remotePeers.Length != 0)
            {
                var pieceAssemblyTimer = new Stopwatch();
                Log.Logger.Debug($"Piece {pieceNumber} being assembled by {remotePeers.Length} peers.");
                tc.assemblyData.pieceBuffer = new PieceBuffer(tc, tc.GetPieceLength(pieceNumber))
                {
                    Number = pieceNumber
                };
                tc.assemblyData.blockRequestsDone.Reset();
                tc.MarkPieceMissing(pieceNumber, false);
                while (GetMoreBlocks(tc, pieceNumber, remotePeers))
                {
                    //
                    // Wait for blocks to arrive
                    //
                    pieceAssemblyTimer.Start();
                    switch (WaitHandle.WaitAny(waitHandles, _assemberTimeout * 1000))
                    {
                    // Any outstanding requests have been completed
                    case 0:
                        tc.assemblyData.blockRequestsDone.Reset();
                        if (tc.assemblyData.pieceBuffer.AllBlocksThere)
                        {
                            pieceAssemblyTimer.Stop();
                            tc.assemblyData.averageAssemblyTime.Add(pieceAssemblyTimer.ElapsedMilliseconds);
                            Log.Logger.Info($"Download speed {tc.BytesPerSecond()} bytes/sec");
                            return(true);
                        }
                        break;

                    // Assembly has been cancelled by external source
                    case 1:
                        return(false);

                    // Timeout so bailout and try again
                    // Note: Remove any peers that are not choked and had outstanding requests
                    case WaitHandle.WaitTimeout:
                        Log.Logger.Debug($"Timeout assembling piece {pieceNumber}.");
                        tc.assemblyData.totalTimeouts++;
                        foreach (var peer in remotePeers)
                        {
                            if ((peer.OutstandingRequestsCount > 0) && peer.PeerChoking.WaitOne(0))
                            {
                                Log.Logger.Debug($"Closed peer {peer.Ip} with outstanding requests.");
                                peer.Close();
                            }
                            peer.OutstandingRequestsCount = 0;
                        }
                        return(false);
                    }
                }
                return(false);
            }
            Thread.Sleep(1000);
            Log.Logger.Debug($"Zero peers to assemble piece {pieceNumber}.");
            return(false);
        }
Esempio n. 5
0
 /// <summary>
 /// Piece assembler task. If/once downboad is complete then start seeding the torrent until
 /// a cancel request is sent. Note: For seeding we only send announce requests every 30 minutes
 /// to stop the local and remote peers from being swamped by requests.
 /// </summary>
 /// <param name="tc"></param>
 /// <param name="cancelAssemblerTask"></param>
 internal void AssemblePieces(TorrentContext tc)
 {
     if (tc is null)
     {
         throw new ArgumentNullException(nameof(tc));
     }
     Log.Logger.Debug($"Starting block assembler for InfoHash {Util.InfoHashToString(tc.infoHash)}.");
     try
     {
         tc.paused.WaitOne(tc.assemblyData.cancelTaskSource.Token);
         if (tc.MainTracker.Left != 0)
         {
             Log.Logger.Info("Torrent downloading...");
             tc.Status = TorrentStatus.Downloading;
             AssembleMissingPieces(tc, tc.assemblyData.cancelTaskSource.Token);
             tc.MainTracker.ChangeStatus(TrackerEvent.completed);
             Log.Logger.Info("Whole Torrent finished downloading.");
         }
         Log.Logger.Info("Torrent seeding...");
         tc.Status = TorrentStatus.Seeding;
         tc.MainTracker.SetSeedingInterval(60000 * 30);
         // Make sure get at least one more annouce before long wait
         tc.MainTracker.ChangeStatus(TrackerEvent.None);
         ProcessRemotePeerRequests(tc, tc.assemblyData.cancelTaskSource.Token);
     }
     catch (Exception ex)
     {
         Log.Logger.Error(ex);
     }
     Log.Logger.Debug($"Terminating block assembler for InfoHash {Util.InfoHashToString(tc.infoHash)}.");
 }
Esempio n. 6
0
 /// <summary>
 /// Setup data and resources needed by peer.
 /// </summary>
 /// <param name="ip">Ip.</param>
 /// <param name="port">Port.</param>
 /// <param name="infoHash">Info hash.</param>
 /// <param name="tc">torrent context.</param>
 ///
 public Peer(string ip, int port, TorrentContext tc, Socket socket) : this(ip, port, socket)
 {
     if (tc is null)
     {
         throw new ArgumentNullException(nameof(tc));
     }
     SetTorrentContext(tc);
 }
Esempio n. 7
0
 /// <summary>
 /// Remove torrent context for an infohash.
 /// </summary>
 /// <param name="tc"></param>
 /// <returns></returns>
 internal bool RemoveTorrentContext(TorrentContext tc)
 {
     if (tc is null)
     {
         throw new ArgumentNullException(nameof(tc));
     }
     return(_torrents.TryRemove(Util.InfoHashToString(tc.infoHash), out TorrentContext _));
 }
Esempio n. 8
0
 /// <summary>
 /// Add torrent context for infohash.
 /// </summary>
 /// <param name="tc"></param>
 internal bool AddTorrentContext(TorrentContext tc)
 {
     if (tc is null)
     {
         throw new ArgumentNullException(nameof(tc));
     }
     return(_torrents.TryAdd(Util.InfoHashToString(tc.infoHash), tc));
 }
Esempio n. 9
0
 /// <summary>
 /// Initialise BitTorrent Tracker.
 /// </summary>
 /// <param name="tc"></param>
 public Tracker(TorrentContext tc)
 {
     trackerStatus   = TrackerStatus.Stopped;
     PeerID          = BitTorrentLibrary.PeerID.Get();
     Ip              = Host.GetIP();
     _tc             = tc ?? throw new ArgumentNullException(nameof(tc));
     _tc.MainTracker = this;
     InfoHash        = tc.infoHash;
     TrackerURL      = tc.trackerURL;
     _announcer      = new AnnouncerFactory().Create(TrackerURL);
 }
Esempio n. 10
0
 /// <summary>
 /// Retrieve torrent context for infohash.
 /// </summary>
 /// <param name="infohash"></param>
 /// <returns></returns>
 internal bool GetTorrentContext(byte[] infohash, out TorrentContext tc)
 {
     if (infohash is null)
     {
         throw new ArgumentNullException(nameof(infohash));
     }
     if (_torrents.TryGetValue(Util.InfoHashToString(infohash), out tc))
     {
         return(true);
     }
     return(false);
 }
Esempio n. 11
0
 public bool[] BlocksPresent => _blockPresent;   // == true then block is present
 /// <summary>
 /// Create an empty piece buffer.
 /// </summary>
 /// <param name="length">Length.</param>
 public PieceBuffer(TorrentContext tc, UInt32 length)
 {
     Tc          = tc ?? throw new ArgumentNullException(nameof(tc));
     Number      = 0;
     Length      = length;
     Buffer      = new byte[Length];
     _blockCount = (int)Length / Constants.BlockSize;
     if (Length % Constants.BlockSize != 0)
     {
         _blockCount++;
     }
     _blockPresent = new bool[_blockCount];
 }
Esempio n. 12
0
        /// <summary>
        /// Select the next piece to be downloaded. It does this by randomly generating a start
        /// piece and sequentially moving a long until ot finds a missing piece.
        /// </summary>
        /// <param name="tc"></param>
        /// <param name="nextPiece"></param>
        /// <param name="startPiece"></param>
        /// <param name="_"></param>
        /// <returns></returns>
        internal bool NextPiece(TorrentContext tc, ref UInt32 nextPiece)
        {
            if (tc is null)
            {
                throw new ArgumentNullException(nameof(tc));
            }

            UInt32 startPiece = (UInt32)_pieceRandmizer.Next(0, tc.numberOfPieces - 1);

            (var pieceSuggested, var pieceNumber) = tc.FindNextMissingPiece(startPiece);
            nextPiece = pieceNumber;
            return(pieceSuggested);
        }
Esempio n. 13
0
        /// <summary>
        /// Update torrent piece information and bitfield from buffer.
        /// </summary>
        /// <param name="tc"></param>
        /// <param name="pieceNumber">Piece number.</param>
        /// <param name="pieceBuffer">Piece buffer.</param>
        /// <param name="numberOfBytes">Number of bytes in piece.</param>
        /// <summary>
        private void UpdateBitfieldFromBuffer(TorrentContext tc, UInt32 pieceNumber, byte[] pieceBuffer, UInt32 numberOfBytes)
        {
            bool pieceThere = tc.CheckPieceHash(pieceNumber, pieceBuffer, numberOfBytes);

            if (pieceThere)
            {
                tc.TotalBytesDownloaded += numberOfBytes;
            }
            tc.SetPieceLength(pieceNumber, numberOfBytes);
            tc.MarkPieceLocal(pieceNumber, pieceThere);
            if (!pieceThere)
            {
                tc.MarkPieceMissing(pieceNumber, true);
            }
        }
Esempio n. 14
0
 private readonly int _maximumBlockRequests;   // Maximum requests at a time
 /// <summary>
 /// Signal to all peers in swarm that we now have the piece local so
 /// that they can request it if they need.
 /// </summary>
 /// <param name="tc"></param>
 /// <param name="pieceNumber"></param>
 private void SignalHaveToSwarm(TorrentContext tc, UInt32 pieceNumber)
 {
     foreach (var remotePeer in tc.peerSwarm.Values)
     {
         try
         {
             PWP.Have(remotePeer, pieceNumber);
         }
         catch (Exception ex)
         {
             Log.Logger.Error(ex);
             remotePeer.Close();
         }
     }
 }
Esempio n. 15
0
 /// <summary>
 // Wait and process remote peer requests until cancelled.
 /// </summary>
 /// <param name="tc"></param>
 /// <param name="cancelTask"></param>
 private void ProcessRemotePeerRequests(TorrentContext tc, CancellationToken cancelAssemblerTask)
 {
     WaitHandle[] waitHandles = new WaitHandle[] { cancelAssemblerTask.WaitHandle };
     foreach (var remotePeer in tc.peerSwarm.Values)
     {
         try
         {
             PWP.Uninterested(remotePeer);
             PWP.Unchoke(remotePeer);
         }
         catch (Exception ex)
         {
             Log.Logger.Error(ex);
             remotePeer.Close();
         }
     }
     WaitHandle.WaitAll(waitHandles);
 }
Esempio n. 16
0
        /// <summary>
        /// Return list of peers connected that are not choked and have the piece. They
        /// are sorted in ascending order of average reponse time for a peer request packet
        /// reponse and limited by the value of maxPeers.
        /// </summary>
        /// <param name="tc"></param>
        /// <param name="pieceNumber"></param>
        /// <param name="maxPeers"></param>
        /// <returns></returns>
        internal Peer[] GetListOfPeers(TorrentContext tc, UInt32 pieceNumber, int maxPeers)
        {
            if (tc is null)
            {
                throw new ArgumentNullException(nameof(tc));
            }

            List <Peer> peers = new List <Peer>();

            foreach (var peer in tc.peerSwarm.Values)
            {
                if (peer.Connected &&
                    peer.PeerChoking.WaitOne(0) &&
                    peer.IsPieceOnRemotePeer(pieceNumber))
                {
                    peers.Add(peer);
                }
            }
            return(peers.OrderBy(peer => peer.AveragePacketResponse.Get()).ToList().Take(maxPeers).ToArray());
        }
Esempio n. 17
0
 /// <summary>
 /// Creates the empty files on disk as place holders of files to be downloaded.
 /// </summary>>
 /// <param name="tc"></param>
 internal void CreateLocalTorrentStructure(TorrentContext tc)
 {
     if (tc is null)
     {
         throw new ArgumentNullException(nameof(tc));
     }
     Log.Logger.Debug("Creating empty files as placeholders for downloading ...");
     foreach (var file in tc.filesToDownload)
     {
         if (!System.IO.File.Exists(file.name))
         {
             Log.Logger.Debug($"File: {file.name}");
             Directory.CreateDirectory(Path.GetDirectoryName(file.name));
             using (var fs = new FileStream(file.name, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None))
             {
                 fs.SetLength((Int64)file.length);
             }
         }
     }
 }
Esempio n. 18
0
        /// <summary>
        /// Mark torrent as fully downloaded for when in seeding from startup. This
        /// means that the whole of the disk image of the torrent isn't checked so
        /// vastly inceasing start time.
        /// </summary>
        /// <param name="tc"></param>
        internal void FullyDownloadedTorrentBitfield(TorrentContext tc)
        {
            if (tc is null)
            {
                throw new ArgumentNullException(nameof(tc));
            }
            Int64 totalBytesToDownload = (Int64)tc.TotalBytesToDownload;

            for (UInt32 pieceNumber = 0; pieceNumber < tc.numberOfPieces; pieceNumber++)
            {
                tc.MarkPieceLocal(pieceNumber, true);
                tc.MarkPieceMissing(pieceNumber, false);
                if (totalBytesToDownload / tc.pieceLength != 0)
                {
                    tc.SetPieceLength(pieceNumber, tc.pieceLength);
                }
                else
                {
                    tc.SetPieceLength(pieceNumber, (UInt32)totalBytesToDownload);
                }
                totalBytesToDownload -= tc.pieceLength;
            }
        }
Esempio n. 19
0
        /// <summary>
        /// Loop for all pieces assembling them block by block until the download is
        /// complete or has been interrupted. If an an assembled piece is found to be
        /// corrupt it is discarded and requested again.
        /// </summary>
        /// <param name="tc"></param>
        /// <param name="cancelAssemblerTask"></param>
        private void AssembleMissingPieces(TorrentContext tc, CancellationToken cancelAssemblerTask)
        {
            UInt32 nextPiece = 0;

            WaitHandle[] waitHandles = new WaitHandle[]
            {
                tc.assemblyData.blockRequestsDone,
                cancelAssemblerTask.WaitHandle
            };
            while (!tc.downloadFinished.WaitOne(0))
            {
                while ((tc.NumberOfUnchokedPeers() > 0) && tc.selector.NextPiece(tc, ref nextPiece))
                {
                    if (GetPieceFromPeers(tc, nextPiece, waitHandles))
                    {
                        if (tc.CheckPieceHash(nextPiece, tc.assemblyData.pieceBuffer.Buffer, tc.GetPieceLength(nextPiece)))
                        {
                            Log.Logger.Debug($"All blocks for piece {nextPiece} received");
                            tc.pieceWriteQueue.Add(tc.assemblyData.pieceBuffer);
                            tc.MarkPieceLocal(nextPiece, true);
                            SignalHaveToSwarm(tc, nextPiece);
                        }
                    }
                    // Signal piece to be requested in unsucessful download
                    if (!tc.IsPieceLocal(nextPiece))
                    {
                        tc.MarkPieceMissing(nextPiece, true);
                    }
                    cancelAssemblerTask.ThrowIfCancellationRequested();
                    tc.paused.WaitOne(cancelAssemblerTask);
                }
                // if we reach here then no eligable peers in swarm so sleep a bit.
                Log.Logger.Debug($"Waiting for eligable peers to download piece from.");
                cancelAssemblerTask.ThrowIfCancellationRequested();
                Thread.Sleep(1000);
            }
        }
Esempio n. 20
0
 /// <summary>
 /// Internal Tracker constructor for mock testing.
 /// </summary>
 /// <param name="tc"></param>
 internal Tracker(TorrentContext tc, IAnnouncerFactory announcerFactory) : this(tc)
 {
     _announcer = announcerFactory.Create(TrackerURL);
 }
Esempio n. 21
0
 /// <summary>
 /// Set torrent context and dependant fields.
 /// </summary>
 /// <param name="tc"></param>
 public void SetTorrentContext(TorrentContext tc)
 {
     Tc = tc ?? throw new ArgumentNullException(nameof(tc));
     NumberOfMissingPieces = (int)Tc.numberOfPieces;
     RemotePieceBitfield   = new byte[tc.Bitfield.Length];
 }
Esempio n. 22
0
 /// <summary>
 /// Create an empty piece buffer.
 /// </summary>
 /// <param name="length">Length.</param>
 public PieceBuffer(TorrentContext tc, UInt32 pieceNumber, UInt32 length) : this(tc, length)
 {
     Number = pieceNumber;
 }
Esempio n. 23
0
 public byte[] ReadBuffer => _network?.ReadBuffer;                // Network read buffer
 /// <summary>
 /// Internal constructor Setup data and resources needed by peer for unit tests.
 /// </summary>
 /// <param name="ip">Ip.</param>
 /// <param name="port">Port.</param>
 /// <param name="infoHash">Info hash.</param>
 /// <param name="tc">torrent context.</param>
 /// <param name="network">peer network layer.</param>
 ///
 internal Peer(string ip, int port, TorrentContext tc, Socket socket, IPeerNetwork network) : this(ip, port, tc, socket)
 {
     _network = network;
 }