예제 #1
0
/**
  * Get the fastest free peer, may remove dead peers.
  */
PM::IPeer* ChunkDownloader::getTheFastestFreePeer()
{
   QMutexLocker locker(&this->mutex);

   PM::IPeer* current = nullptr;
   bool isTheNmberOfPeersHasChanged = false;
   for (QMutableListIterator<PM::IPeer*> i(this->peers); i.hasNext();)
   {
      PM::IPeer* peer = i.next();
      if (!peer->isAvailable())
      {
         i.remove();
         this->linkedPeers.rmLink(peer);
         isTheNmberOfPeersHasChanged = true;
      }
      else if (this->occupiedPeersDownloadingChunk.isPeerFree(peer) && (!current || peer->getSpeed() > current->getSpeed()))
         current = peer;
   }

   if (isTheNmberOfPeersHasChanged)
      emit numberOfPeersChanged();

   return current;
}
예제 #2
0
void ChunkDownloader::run()
{
   int deltaRead = 0;
   QElapsedTimer timer;
   timer.start();
   this->lastTransferStatus = QUEUED;

   try
   {
      QSharedPointer<FM::IDataWriter> writer = this->chunk->getDataWriter();

      static const int SOCKET_TIMEOUT = SETTINGS.get<quint32>("socket_timeout");
      static const int TIME_PERIOD_CHOOSE_ANOTHER_PEER = 1000.0 * SETTINGS.get<double>("time_recheck_chunk_factor") * SETTINGS.get<quint32>("chunk_size") / SETTINGS.get<quint32>("lan_speed");

      static const int BUFFER_SIZE = SETTINGS.get<quint32>("buffer_size_writing");
      char buffer[BUFFER_SIZE];

      const int initialKnownBytes = this->chunk->getKnownBytes();
      int bytesToRead = this->chunkSize - initialKnownBytes;
      int bytesToWrite = 0;
      int bytesWritten = 0;

      forever
      {
         this->mutex.lock();
         if (!this->downloading)
         {
            L_DEBU(QString("Downloading aborted, chunk : %1%2").arg(this->chunk->toStringLog()).arg(this->chunk->isComplete() ? "" : " Not complete!"));
            this->closeTheSocket = true; // Because some garbage from the remote uploader will continue to come in this socket.
            this->mutex.unlock();
            break;
         }
         this->mutex.unlock();

         int bytesRead = this->socket->read(buffer + bytesToWrite, bytesToRead < BUFFER_SIZE - bytesToWrite ? bytesToRead : BUFFER_SIZE - bytesToWrite);
         bytesToRead -= bytesRead;

         if (bytesRead == 0)
         {
            if (!this->socket->waitForReadyRead(SOCKET_TIMEOUT))
            {
               L_WARN(QString("Connection dropped, error = %1, bytesAvailable = %2").arg(socket->errorString()).arg(socket->bytesAvailable()));
               this->closeTheSocket = true;
               this->lastTransferStatus = TRANSFER_ERROR;
               break;
            }
            continue;
         }
         else if (bytesRead == -1)
         {
            L_WARN(QString("Socket : cannot receive data : %1").arg(this->chunk->toStringLog()));
            this->closeTheSocket = true;
            this->lastTransferStatus = TRANSFER_ERROR;
            break;
         }

         deltaRead += bytesRead;
         bytesToWrite += bytesRead;

         if (timer.elapsed() > TIME_PERIOD_CHOOSE_ANOTHER_PEER)
         {
            this->currentDownloadingPeer->setSpeed(deltaRead / timer.elapsed() * 1000);
            L_DEBU(QString("Check for a better peer for the chunk: %1, current peer: %2 . . .").arg(this->chunk->toStringLog()).arg(this->currentDownloadingPeer->toStringLog()));
            timer.start();
            deltaRead = 0;

            // If a another peer exists and its speed is greater than our by a factor 'switch_to_another_peer_factor'
            // then we will try to switch to this peer.
            PM::IPeer* peer = this->getTheFastestFreePeer();
            if (
               peer &&
               peer != this->currentDownloadingPeer &&
               peer->getSpeed() / SETTINGS.get<double>("switch_to_another_peer_factor") > this->currentDownloadingPeer->getSpeed()
            )
            {
               L_DEBU(QString("Switch to a better peer: %1").arg(peer->toStringLog()));
               this->closeTheSocket = true; // We ask to close the socket to avoid to get garbage data.
               break;
            }
         }

         // If the buffer is full or there is no more byte to read.
         if (bytesToWrite == BUFFER_SIZE || bytesToRead == 0)
         {
            writer->write(buffer, bytesToWrite);
            bytesWritten += bytesToWrite;
            bytesToWrite = 0;
         }

         this->transferRateCalculator.addData(bytesRead);

         if (initialKnownBytes + bytesWritten >= this->chunkSize)
            break;
      }
   }
   catch(FM::FileResetException)
   {
      L_DEBU("FileResetException");
      this->closeTheSocket = true;
      this->lastTransferStatus = FILE_NON_EXISTENT;
   }
   catch(FM::ChunkDataUnknownException)
   {
      L_DEBU("ChunkDataUnknownException");
      this->closeTheSocket = true;
      this->lastTransferStatus = UNABLE_TO_OPEN_THE_FILE;
   }
   catch(FM::UnableToOpenFileInWriteModeException)
   {
      L_DEBU("UnableToOpenFileInWriteModeException");
      this->closeTheSocket = true;
      this->lastTransferStatus = UNABLE_TO_OPEN_THE_FILE;
   }
   catch(FM::IOErrorException&)
   {
      L_DEBU("IOErrorException");
      this->closeTheSocket = true;
      this->lastTransferStatus = FILE_IO_ERROR;
   }
   catch (FM::ChunkDeletedException&)
   {
      L_DEBU("ChunkDeletedException");
      this->closeTheSocket = true;
      this->lastTransferStatus = FILE_NON_EXISTENT;
   }
   catch (FM::TryToWriteBeyondTheEndOfChunkException&)
   {
      L_DEBU("TryToWriteBeyondTheEndOfChunkException");
      this->closeTheSocket = true;
      this->lastTransferStatus = GOT_TOO_MUCH_DATA;
   }
   catch (FM::hashMissmatchException)
   {
      static const quint32 BLOCK_DURATION = SETTINGS.get<quint32>("block_duration_corrupted_data");
      L_USER(QString(tr("Corrupted data received for the file \"%1\" from peer %2. Peer blocked for %3 ms")).arg(this->chunk->getFilePath()).arg(this->currentDownloadingPeer->getNick()).arg(BLOCK_DURATION));
      /*: A reason why the user has been blocked */
      this->currentDownloadingPeer->block(BLOCK_DURATION, tr("Has sent corrupted data"));
      this->closeTheSocket = true;
      this->lastTransferStatus = HASH_MISSMATCH;
   }

   if (timer.elapsed() > MINIMUM_DELTA_TIME_TO_COMPUTE_SPEED)
      this->currentDownloadingPeer->setSpeed(deltaRead / timer.elapsed() * 1000);

   this->socket->setReadBufferSize(0);
   this->socket->moveToThread(this->mainThread);
}