Главная · Все классы · Основные классы · Классы по группам · Модули · Функции

Файл примера torrentclient.cpp
network/torrent/torrentclient.cpp

 /****************************************************************************
 **
 ** Copyright (C) 2004-2008 Trolltech ASA. All rights reserved.
 **
 ** This file is part of the example classes of the Qt Toolkit.
 **
 ** This file may be used under the terms of the GNU General Public
 ** License versions 2.0 or 3.0 as published by the Free Software
 ** Foundation and appearing in the files LICENSE.GPL2 and LICENSE.GPL3
 ** included in the packaging of this file.  Alternatively you may (at
 ** your option) use any later version of the GNU General Public
 ** License if such license has been publicly approved by Trolltech ASA
 ** (or its successors, if any) and the KDE Free Qt Foundation. In
 ** addition, as a special exception, Trolltech gives you certain
 ** additional rights. These rights are described in the Trolltech GPL
 ** Exception version 1.2, which can be found at
 ** http://www.trolltech.com/products/qt/gplexception/ and in the file
 ** GPL_EXCEPTION.txt in this package.
 **
 ** Please review the following information to ensure GNU General
 ** Public Licensing requirements will be met:
 ** http://trolltech.com/products/qt/licenses/licensing/opensource/. If
 ** you are unsure which license is appropriate for your use, please
 ** review the following information:
 ** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
 ** or contact the sales department at sales@trolltech.com.
 **
 ** In addition, as a special exception, Trolltech, as the sole
 ** copyright holder for Qt Designer, grants users of the Qt/Eclipse
 ** Integration plug-in the right for the Qt/Eclipse Integration to
 ** link to functionality provided by Qt Designer and its related
 ** libraries.
 **
 ** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
 ** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
 ** A PARTICULAR PURPOSE. Trolltech reserves all rights not expressly
 ** granted herein.
 **
 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 **
 ****************************************************************************/

 #include "connectionmanager.h"
 #include "filemanager.h"
 #include "metainfo.h"
 #include "torrentclient.h"
 #include "torrentserver.h"
 #include "trackerclient.h"
 #include "peerwireclient.h"
 #include "ratecontroller.h"

 #include <QtCore>
 #include <QNetworkInterface>

 extern "C" {
 #include "3rdparty/sha1.h"
 }

 // Эти константы могут быть также настроены пользователем.
 static const int ServerMinPort = 6881;
 static const int ServerMaxPort = /* 6889 */ 7000;
 static const int BlockSize = 16384;
 static const int MaxBlocksInProgress = 5;
 static const int MaxBlocksInMultiMode = 2;
 static const int MaxConnectionPerPeer = 1;
 static const int RateControlWindowLength = 10;
 static const int RateControlTimerDelay = 1000;
 static const int MinimumTimeBeforeRevisit = 30;
 static const int MaxUploads = 4;
 static const int UploadScheduleInterval = 10000;
 static const int EndGamePieces = 5;

 class TorrentPiece {
 public:
     int index;
     int length;
     QBitArray completedBlocks;
     QBitArray requestedBlocks;
     bool inProgress;
 };

 class TorrentClientPrivate
 {
 public:
     TorrentClientPrivate(TorrentClient *qq);

     // Состояние/ошибка
     void setError(TorrentClient::Error error);
     void setState(TorrentClient::State state);
     TorrentClient::Error error;
     TorrentClient::State state;
     QString errorString;
     QString stateString;

     // Где сохранять данные
     QString destinationFolder;
     MetaInfo metaInfo;

     // Аннонс трекера и файловый менеджер
     QByteArray peerId;
     QByteArray infoHash;
     TrackerClient trackerClient;
     FileManager fileManager;

     // Соединения
     QList<PeerWireClient *> connections;
     QList<TorrentPeer *> peers;
     bool schedulerCalled;
     void callScheduler();
     bool connectingToClients;
     void callPeerConnector();
     int uploadScheduleTimer;

     // Куски
     QMap<int, PeerWireClient *> readIds;
     QMultiMap<PeerWireClient *, TorrentPiece *> payloads;
     QMap<int, TorrentPiece *> pendingPieces;
     QBitArray completedPieces;
     QBitArray incompletePieces;
     int pieceCount;

     // Прогресс
     int lastProgressValue;
     qint64 downloadedBytes;
     qint64 uploadedBytes;
     int downloadRate[RateControlWindowLength];
     int uploadRate[RateControlWindowLength];
     int transferRateTimer;

     TorrentClient *q;
 };

 TorrentClientPrivate::TorrentClientPrivate(TorrentClient *qq)
     : trackerClient(qq), q(qq)
 {
     error = TorrentClient::UnknownError;
     state = TorrentClient::Idle;
     errorString = QT_TRANSLATE_NOOP(TorrentClient, "Unknown error");
     stateString = QT_TRANSLATE_NOOP(TorrentClient, "Idle");
     schedulerCalled = false;
     connectingToClients = false;
     uploadScheduleTimer = 0;
     lastProgressValue = -1;
     pieceCount = 0;
     downloadedBytes = 0;
     uploadedBytes = 0;
     memset(downloadRate, 0, sizeof(downloadRate));
     memset(uploadRate, 0, sizeof(uploadRate));
     transferRateTimer = 0;
 }

 void TorrentClientPrivate::setError(TorrentClient::Error errorCode)
 {
     this->error = errorCode;
     switch (error) {
     case TorrentClient::UnknownError:
         errorString = QT_TRANSLATE_NOOP(TorrentClient, "Unknown error");
         break;
     case TorrentClient::TorrentParseError:
         errorString = QT_TRANSLATE_NOOP(TorrentClient, "Invalid torrent data");
         break;
     case TorrentClient::InvalidTrackerError:
         errorString = QT_TRANSLATE_NOOP(TorrentClient, "Unable to connect to tracker");
         break;
     case TorrentClient::FileError:
         errorString = QT_TRANSLATE_NOOP(TorrentClient, "File error");
         break;
     case TorrentClient::ServerError:
         errorString = QT_TRANSLATE_NOOP(TorrentClient, "Unable to initialize server");
         break;
     }
     emit q->error(errorCode);
 }

 void TorrentClientPrivate::setState(TorrentClient::State state)
 {
     this->state = state;
     switch (state) {
     case TorrentClient::Idle:
         stateString = QT_TRANSLATE_NOOP(TorrentClient, "Idle");
         break;
     case TorrentClient::Paused:
         stateString = QT_TRANSLATE_NOOP(TorrentClient, "Paused");
         break;
     case TorrentClient::Stopping:
         stateString = QT_TRANSLATE_NOOP(TorrentClient, "Stopping");
         break;
     case TorrentClient::Preparing:
         stateString = QT_TRANSLATE_NOOP(TorrentClient, "Preparing");
         break;
     case TorrentClient::Searching:
         stateString = QT_TRANSLATE_NOOP(TorrentClient, "Searching");
         break;
     case TorrentClient::Connecting:
         stateString = QT_TRANSLATE_NOOP(TorrentClient, "Connecting");
         break;
     case TorrentClient::WarmingUp:
         stateString = QT_TRANSLATE_NOOP(TorrentClient, "Warming up");
         break;
     case TorrentClient::Downloading:
         stateString = QT_TRANSLATE_NOOP(TorrentClient, "Downloading");
         break;
     case TorrentClient::Endgame:
         stateString = QT_TRANSLATE_NOOP(TorrentClient, "Finishing");
         break;
     case TorrentClient::Seeding:
         stateString = QT_TRANSLATE_NOOP(TorrentClient, "Seeding");
         break;
     }
     emit q->stateChanged(state);
 }

 void TorrentClientPrivate::callScheduler()
 {
     if (!schedulerCalled) {
         schedulerCalled = true;
         QMetaObject::invokeMethod(q, "scheduleDownloads", Qt::QueuedConnection);
     }
 }

 void TorrentClientPrivate::callPeerConnector()
 {
     if (!connectingToClients) {
         connectingToClients = true;
         QTimer::singleShot(10000, q, SLOT(connectToPeers()));
     }
 }

 TorrentClient::TorrentClient(QObject *parent)
     : QObject(parent), d(new TorrentClientPrivate(this))
 {
     // Подключаем файловый менеджер
     connect(&d->fileManager, SIGNAL(dataRead(int, int, int, const QByteArray &)),
             this, SLOT(sendToPeer(int, int, int, const QByteArray &)));
     connect(&d->fileManager, SIGNAL(verificationProgress(int)),
             this, SLOT(updateProgress(int)));
     connect(&d->fileManager, SIGNAL(verificationDone()),
             this, SLOT(fullVerificationDone()));
     connect(&d->fileManager, SIGNAL(pieceVerified(int, bool)),
             this, SLOT(pieceVerified(int, bool)));
     connect(&d->fileManager, SIGNAL(error()),
             this, SLOT(handleFileError()));

     // Подключаем клиент трекера
     connect(&d->trackerClient, SIGNAL(peerListUpdated(const QList<TorrentPeer> &)),
             this, SLOT(addToPeerList(const QList<TorrentPeer> &)));
     connect(&d->trackerClient, SIGNAL(stopped()),
             this, SIGNAL(stopped()));
 }

 TorrentClient::~TorrentClient()
 {
     qDeleteAll(d->peers);
     qDeleteAll(d->pendingPieces);
     delete d;
 }

 bool TorrentClient::setTorrent(const QString &fileName)
 {
     QFile file(fileName);
     if (!file.open(QIODevice::ReadOnly) || !setTorrent(file.readAll())) {
         d->setError(TorrentParseError);
         return false;
     }
     return true;
 }

 bool TorrentClient::setTorrent(const QByteArray &torrentData)
 {
     if (!d->metaInfo.parse(torrentData)) {
         d->setError(TorrentParseError);
         return false;
     }

     // Вычисление хеша SHA1 секции "info" торрента
     QByteArray infoValue = d->metaInfo.infoValue();
     SHA1Context sha;
     SHA1Reset(&sha);
     SHA1Input(&sha, (const unsigned char *)infoValue.constData(), infoValue.size());
     SHA1Result(&sha);
     unsigned char *digest = (unsigned char *)sha.Message_Digest;
     d->infoHash.resize(20);

     for (int i = 0; i < 5; ++i) {
 #if Q_BYTE_ORDER == Q_BIG_ENDIAN
         d->infoHash[i * 4 + 3] = digest[i * 4 + 3];
         d->infoHash[i * 4 + 2] = digest[i * 4 + 2];
         d->infoHash[i * 4 + 1] = digest[i * 4 + 1];
         d->infoHash[i * 4 + 0] = digest[i * 4 + 0];
 #else
         d->infoHash[i * 4 + 0] = digest[i * 4 + 3];
         d->infoHash[i * 4 + 1] = digest[i * 4 + 2];
         d->infoHash[i * 4 + 2] = digest[i * 4 + 1];
         d->infoHash[i * 4 + 3] = digest[i * 4 + 0];
 #endif
     }

     return true;
 }

 MetaInfo TorrentClient::metaInfo() const
 {
     return d->metaInfo;
 }

 void TorrentClient::setDestinationFolder(const QString &directory)
 {
     d->destinationFolder = directory;
 }

 QString TorrentClient::destinationFolder() const
 {
     return d->destinationFolder;
 }

 void TorrentClient::setDumpedState(const QByteArray &dumpedState)
 {
     // Восстановление частично завершенных кусков
     QDataStream stream(dumpedState);

     quint16 version = 0;
     stream >> version;
     if (version != 2)
         return;

     stream >> d->completedPieces;

     while (!stream.atEnd()) {
         int index;
         int length;
         QBitArray completed;
         stream >> index >> length >> completed;
         if (stream.status() != QDataStream::Ok) {
             d->completedPieces.clear();
             break;
         }

         TorrentPiece *piece = new TorrentPiece;
         piece->index = index;
         piece->length = length;
         piece->completedBlocks = completed;
         piece->requestedBlocks.resize(completed.size());
         piece->inProgress = false;
         d->pendingPieces[index] = piece;
     }
 }

 QByteArray TorrentClient::dumpedState() const
 {
     QByteArray partials;
     QDataStream stream(&partials, QIODevice::WriteOnly);

     stream << quint16(2);
     stream << d->completedPieces;

     // Сохраняем состояние всех частично скачанных кусков в формат
     // удобный для хранения в настройках.
     QMap<int, TorrentPiece *>::ConstIterator it = d->pendingPieces.constBegin();
     while (it != d->pendingPieces.constEnd()) {
         TorrentPiece *piece = it.value();
         if (blocksLeftForPiece(piece) > 0 && blocksLeftForPiece(piece) < piece->completedBlocks.size()) {
             stream << piece->index;
             stream << piece->length;
             stream << piece->completedBlocks;
         }
         ++it;
     }

     return partials;
 }

 qint64 TorrentClient::progress() const
 {
     return d->lastProgressValue;
 }

 void TorrentClient::setDownloadedBytes(qint64 bytes)
 {
     d->downloadedBytes = bytes;
 }

 qint64 TorrentClient::downloadedBytes() const
 {
     return d->downloadedBytes;
 }

 void TorrentClient::setUploadedBytes(qint64 bytes)
 {
     d->uploadedBytes = bytes;
 }

 qint64 TorrentClient::uploadedBytes() const
 {
     return d->uploadedBytes;
 }

 int TorrentClient::connectedPeerCount() const
 {
     int tmp = 0;
     foreach (PeerWireClient *client, d->connections) {
         if (client->state() == QAbstractSocket::ConnectedState)
             ++tmp;
     }
     return tmp;
 }

 int TorrentClient::seedCount() const
 {
     int tmp = 0;
     foreach (PeerWireClient *client, d->connections) {
         if (client->availablePieces().count(true) == d->pieceCount)
             ++tmp;
     }
     return tmp;
 }

 TorrentClient::State TorrentClient::state() const
 {
     return d->state;
 }

 QString TorrentClient::stateString() const
 {
     return d->stateString;
 }

 TorrentClient::Error TorrentClient::error() const
 {
     return d->error;
 }

 QString TorrentClient::errorString() const
 {
     return d->errorString;
 }

 QByteArray TorrentClient::peerId() const
 {
     return d->peerId;
 }

 QByteArray TorrentClient::infoHash() const
 {
     return d->infoHash;
 }

 void TorrentClient::start()
 {
     if (d->state != Idle)
         return;

     TorrentServer::instance()->addClient(this);

     // Инициализация файлового менеджера
     d->setState(Preparing);
     d->fileManager.setMetaInfo(d->metaInfo);
     d->fileManager.setDestinationFolder(d->destinationFolder);
     d->fileManager.setCompletedPieces(d->completedPieces);
     d->fileManager.start(QThread::LowestPriority);
     d->fileManager.startDataVerification();
 }

 void TorrentClient::stop()
 {
     if (d->state == Stopping)
         return;

     TorrentServer::instance()->removeClient(this);

     // Обновляем состояние
     State oldState = d->state;
     d->setState(Stopping);

     // Останавливаем таймер
     if (d->transferRateTimer) {
         killTimer(d->transferRateTimer);
         d->transferRateTimer = 0;
     }

     // Отменяем все существующие соединения
     foreach (PeerWireClient *client, d->connections) {
         RateController::instance()->removeSocket(client);
         ConnectionManager::instance()->removeConnection(client);
         client->abort();
     }
     d->connections.clear();

     // Возможно останавливаем трекер
     if (oldState > Preparing) {
         d->trackerClient.stop();
     } else {
         d->setState(Idle);
         emit stopped();
     }
 }

 void TorrentClient::setPaused(bool paused)
 {
     if (paused) {
         // Отключаем все соединения и устанавливаем максимальное число
         // соединений равное 0. Сохраняем список пиров что бы мы могли быстро
         // восстановить позже.
         d->setState(Paused);
         foreach (PeerWireClient *client, d->connections)
             client->abort();
         d->connections.clear();
         TorrentServer::instance()->removeClient(this);
     } else {
         // Восстанавливаем максимальное число соединений и запускаем
         // подключение к пиру. Мы должны также быстро начинать получать входящие
         // соединения.
         d->setState(d->completedPieces.count(true) == d->fileManager.pieceCount()
                     ? Seeding : Searching);
         connectToPeers();
         TorrentServer::instance()->addClient(this);
     }
 }

 void TorrentClient::timerEvent(QTimerEvent *event)
 {
     if (event->timerId() == d->uploadScheduleTimer) {
         // Обновляем состояние кто заглушен и кто нет
         scheduleUploads();
         return;
     }

     if (event->timerId() != d->transferRateTimer) {
         QObject::timerEvent(event);
         return;
     }

     // Вычисляем средную скорость скачки/отдачи
     qint64 uploadBytesPerSecond = 0;
     qint64 downloadBytesPerSecond = 0;
     for (int i = 0; i < RateControlWindowLength; ++i) {
         uploadBytesPerSecond += d->uploadRate[i];
         downloadBytesPerSecond += d->downloadRate[i];
     }
     uploadBytesPerSecond /= qint64(RateControlWindowLength);
     downloadBytesPerSecond /= qint64(RateControlWindowLength);
     for (int i = RateControlWindowLength - 2; i >= 0; --i) {
         d->uploadRate[i + 1] = d->uploadRate[i];
         d->downloadRate[i + 1] = d->downloadRate[i];
     }
     d->uploadRate[0] = 0;
     d->downloadRate[0] = 0;
     emit uploadRateUpdated(int(uploadBytesPerSecond));
     emit downloadRateUpdated(int(downloadBytesPerSecond));

     // Останавливаем таймер если нет активности.
     if (downloadBytesPerSecond == 0 && uploadBytesPerSecond == 0) {
         killTimer(d->transferRateTimer);
         d->transferRateTimer = 0;
     }
 }

 void TorrentClient::sendToPeer(int readId, int pieceIndex, int begin, const QByteArray &data)
 {
     // Отсылаем запрошенный блок пиру если соединение
     // клиента все еще есть; в противном случае ничего не делаем. Этот  слот вызывается
     // менеджером файлов после того как он прочитает блок данных.
     PeerWireClient *client = d->readIds.value(readId);
     if (client) {
         if ((client->peerWireState() & PeerWireClient::ChokingPeer) == 0)
             client->sendBlock(pieceIndex, begin, data);
     }
     d->readIds.remove(readId);
 }

 void TorrentClient::fullVerificationDone()
 {
     // Обновляем наш список завершённых и незавершённых кусков.
     d->completedPieces = d->fileManager.completedPieces();
     d->incompletePieces.resize(d->completedPieces.size());
     d->pieceCount = d->completedPieces.size();
     for (int i = 0; i < d->fileManager.pieceCount(); ++i) {
         if (!d->completedPieces.testBit(i))
             d->incompletePieces.setBit(i);
     }

     updateProgress();

     // Если контрольная сумма показывает что то что в сохраненном состоянии было
     // частичным, но фактически завершенным, тогда мы верим контрольной сумме.
     QMap<int, TorrentPiece *>::Iterator it = d->pendingPieces.begin();
     while (it != d->pendingPieces.end()) {
         if (d->completedPieces.testBit(it.key()))
             it = d->pendingPieces.erase(it);
         else
             ++it;
     }

     d->uploadScheduleTimer = startTimer(UploadScheduleInterval);

     // Запускаем сервер
     TorrentServer *server = TorrentServer::instance();
     if (!server->isListening()) {
         // Устанавливаем соединение пира и сервера
         for (int i = ServerMinPort; i <= ServerMaxPort; ++i) {
             if (server->listen(QHostAddress::Any, i))
                 break;
         }
         if (!server->isListening()) {
             d->setError(ServerError);
             return;
         }
     }

     d->setState(d->completedPieces.count(true) == d->pieceCount ? Seeding : Searching);

     // Запускаем клиент торрента
     d->trackerClient.setUploadCount(d->uploadedBytes);
     d->trackerClient.setDownloadCount(d->downloadedBytes);
     d->trackerClient.start(d->metaInfo);
 }

 void TorrentClient::pieceVerified(int pieceIndex, bool ok)
 {
     TorrentPiece *piece = d->pendingPieces.value(pieceIndex);

     // Удаляем этот кусок из полезной нагрузки
     QMultiMap<PeerWireClient *, TorrentPiece *>::Iterator it = d->payloads.begin();
     while (it != d->payloads.end()) {
         if (it.value()->index == pieceIndex)
             it = d->payloads.erase(it);
         else
             ++it;
     }

     if (!ok) {
         // если кусок не прошел проверку SHA1, мы просто очищаем
         // его состояние и планировщик запросит его повторно
         piece->inProgress = false;
         piece->completedBlocks.fill(false);
         piece->requestedBlocks.fill(false);
         d->callScheduler();
         return;
     }

     // Обновляем список пиров что бы мы знали кто все ещё заинтересован.
     foreach (TorrentPeer *peer, d->peers) {
         if (!peer->interesting)
             continue;
         bool interesting = false;
         for (int i = 0; i < d->pieceCount; ++i) {
             if (peer->pieces.testBit(i) && d->incompletePieces.testBit(i)) {
                 interesting = true;
                 break;
             }
         }
         peer->interesting = interesting;
     }

     // Удаляем кусок и обновляем нашу структуру.
     delete piece;
     d->pendingPieces.remove(pieceIndex);
     d->completedPieces.setBit(pieceIndex);
     d->incompletePieces.clearBit(pieceIndex);

     // Уведомляем подключенных пиров.
     foreach (PeerWireClient *client, d->connections) {
         if (client->state() == QAbstractSocket::ConnectedState
             && !client->availablePieces().testBit(pieceIndex)) {
             client->sendPieceNotification(pieceIndex);
         }
     }

     // Уведовляем трекер если мы перешли в режим сида; в противном случае
     // вызываем планировщик.
     int completed = d->completedPieces.count(true);
     if (completed == d->pieceCount) {
         if (d->state != Seeding) {
             d->setState(Seeding);
             d->trackerClient.start(d->metaInfo);
         }
     } else {
         if (completed == 1)
             d->setState(Downloading);
         else if (d->incompletePieces.count(true) < 5 && d->pendingPieces.size() > d->incompletePieces.count(true))
             d->setState(Endgame);
         d->callScheduler();
     }

     updateProgress();
 }

 void TorrentClient::handleFileError()
 {
     if (d->state == Paused)
         return;
     setPaused(true);
     emit error(FileError);
 }

 void TorrentClient::connectToPeers()
 {
     d->connectingToClients = false;

     if (d->state == Stopping || d->state == Idle || d->state == Paused)
         return;

     if (d->state == Searching)
         d->setState(Connecting);

     // Находим список пиров с которыми мы ещё не соединились где
     // более заинтересованный пир указан более одного раза.
     QList<TorrentPeer *> weighedPeers = weighedFreePeers();

     // Запускаем столько соединений сколько мы можем
     while (!weighedPeers.isEmpty() && ConnectionManager::instance()->canAddConnection()
            && (qrand() % (ConnectionManager::instance()->maxConnections() / 2))) {
         PeerWireClient *client = new PeerWireClient(ConnectionManager::instance()->clientId(), this);
         RateController::instance()->addSocket(client);
         ConnectionManager::instance()->addConnection(client);

         initializeConnection(client);
         d->connections << client;

         // Выбираем случайного пира и списка взвешенных пиров.
         TorrentPeer *peer = weighedPeers.takeAt(qrand() % weighedPeers.size());
         weighedPeers.removeAll(peer);
         peer->connectStart = QDateTime::currentDateTime().toTime_t();
         peer->lastVisited = peer->connectStart;

         // Подключаемся к пиру.
         client->setPeer(peer);
         client->connectToHost(peer->address, peer->port);
     }
 }

 QList<TorrentPeer *> TorrentClient::weighedFreePeers() const
 {
     QList<TorrentPeer *> weighedPeers;

     // Генерируем список пиров с которыми мы хотим соединиться.
     uint now = QDateTime::currentDateTime().toTime_t();
     QList<TorrentPeer *> freePeers;
     QMap<QString, int> connectionsPerPeer;
     foreach (TorrentPeer *peer, d->peers) {
         bool busy = false;
         foreach (PeerWireClient *client, d->connections) {
             if (client->state() == PeerWireClient::ConnectedState
                 && client->peerAddress() == peer->address
                 && client->peerPort() == peer->port) {
                 if (++connectionsPerPeer[peer->address.toString()] >= MaxConnectionPerPeer) {
                     busy = true;
                     break;
                 }
             }
         }
         if (!busy && (now - peer->lastVisited) > uint(MinimumTimeBeforeRevisit))
             freePeers << peer;
     }

     // Некуда соединяться
     if (freePeers.isEmpty())
         return weighedPeers;

     // Назначаем очки основанные на скорости соединения и доступных кусках.
     QList<QPair<int, TorrentPeer *> > points;
     foreach (TorrentPeer *peer, freePeers) {
         int tmp = 0;
         if (peer->interesting) {
             tmp += peer->numCompletedPieces;
             if (d->state == Seeding)
                 tmp = d->pieceCount - tmp;
             if (!peer->connectStart) // Неизвестный пир интересует в качестве сида
                 tmp += d->pieceCount;

             // 1/5 общего счета для каждой секунды ниже 5 отводится на
             // соединение.
             if (peer->connectTime < 5)
                 tmp += (d->pieceCount / 10) * (5 - peer->connectTime);
         }
         points << QPair<int, TorrentPeer *>(tmp, peer);
     }
     qSort(points);

     // Минимизируем список что бы разница в очках никогда не была больше 1.
     typedef QPair<int,TorrentPeer*> PointPair;
     QMultiMap<int, TorrentPeer *> pointMap;
     int lowestScore = 0;
     int lastIndex = 0;
     foreach (PointPair point, points) {
         if (point.first > lowestScore) {
             lowestScore = point.first;
             ++lastIndex;
         }
         pointMap.insert(lastIndex, point.second);
     }

     // Теперь делаем список пиров где те у кого больше очков
     // указаны несколько раз.
     QMultiMap<int, TorrentPeer *>::ConstIterator it = pointMap.constBegin();
     while (it != pointMap.constEnd()) {
         for (int i = 0; i < it.key() + 1; ++i)
             weighedPeers << it.value();
         ++it;
     }

     return weighedPeers;
 }

 void TorrentClient::setupIncomingConnection(PeerWireClient *client)
 {
     // Сигналы подключения
     initializeConnection(client);

     // Инициализация клиента
     RateController::instance()->addSocket(client);
     d->connections << client;

     client->initialize(d->infoHash, d->pieceCount);
     client->sendPieceList(d->completedPieces);

     emit peerInfoUpdated();

     if (d->state == Searching || d->state == Connecting) {
         int completed = d->completedPieces.count(true);
         if (completed == 0)
             d->setState(WarmingUp);
         else if (d->incompletePieces.count(true) < 5 && d->pendingPieces.size() > d->incompletePieces.count(true))
             d->setState(Endgame);
     }

     if (d->connections.isEmpty())
         scheduleUploads();
 }

 void TorrentClient::setupOutgoingConnection()
 {
     PeerWireClient *client = qobject_cast<PeerWireClient *>(sender());

     // Обновляем статистики соединения.
     foreach (TorrentPeer *peer, d->peers) {
         if (peer->port == client->peerPort() && peer->address == client->peerAddress()) {
             peer->connectTime = peer->lastVisited - peer->connectStart;
             break;
         }
     }

     // Посылаем приветствие и список частей
     client->initialize(d->infoHash, d->pieceCount);
     client->sendPieceList(d->completedPieces);

     emit peerInfoUpdated();

     if (d->state == Searching || d->state == Connecting) {
         int completed = d->completedPieces.count(true);
         if (completed == 0)
             d->setState(WarmingUp);
         else if (d->incompletePieces.count(true) < 5 && d->pendingPieces.size() > d->incompletePieces.count(true))
             d->setState(Endgame);
     }
 }

 void TorrentClient::initializeConnection(PeerWireClient *client)
 {
     connect(client, SIGNAL(connected()),
             this, SLOT(setupOutgoingConnection()));
     connect(client, SIGNAL(disconnected()),
             this, SLOT(removeClient()));
     connect(client, SIGNAL(error(QAbstractSocket::SocketError)),
             this, SLOT(removeClient()));
     connect(client, SIGNAL(piecesAvailable(const QBitArray &)),
             this, SLOT(peerPiecesAvailable(const QBitArray &)));
     connect(client, SIGNAL(blockRequested(int, int, int)),
             this, SLOT(peerRequestsBlock(int, int, int)));
     connect(client, SIGNAL(blockReceived(int, int, const QByteArray &)),
             this, SLOT(blockReceived(int, int, const QByteArray &)));
     connect(client, SIGNAL(choked()),
             this, SLOT(peerChoked()));
     connect(client, SIGNAL(unchoked()),
             this, SLOT(peerUnchoked()));
     connect(client, SIGNAL(bytesWritten(qint64)),
             this, SLOT(peerWireBytesWritten(qint64)));
     connect(client, SIGNAL(bytesReceived(qint64)),
             this, SLOT(peerWireBytesReceived(qint64)));
 }

 void TorrentClient::removeClient()
 {
     PeerWireClient *client = static_cast<PeerWireClient *>(sender());

     // Удаляем узел из нашего списка известных пиров если соединение
     // провалилось.
     if (client->peer() && client->error() == QAbstractSocket::ConnectionRefusedError)
         d->peers.removeAll(client->peer());

     // Удаляем клиента из RateController и всех структур.
     RateController::instance()->removeSocket(client);
     d->connections.removeAll(client);
     QMultiMap<PeerWireClient *, TorrentPiece *>::Iterator it = d->payloads.find(client);
     while (it != d->payloads.end() && it.key() == client) {
         TorrentPiece *piece = it.value();
         piece->inProgress = false;
         piece->requestedBlocks.fill(false);
         it = d->payloads.erase(it);
     }

     // Удаляем планируемые запросы чтения.
     QMapIterator<int, PeerWireClient *> it2(d->readIds);
     while (it2.findNext(client))
         d->readIds.remove(it2.key());

     // Удаляем клиента позже.
     disconnect(client, SIGNAL(disconnected()), this, SLOT(removeClient()));
     client->deleteLater();
     ConnectionManager::instance()->removeConnection(client);

     emit peerInfoUpdated();
     d->callPeerConnector();
 }

 void TorrentClient::peerPiecesAvailable(const QBitArray &pieces)
 {
     PeerWireClient *client = qobject_cast<PeerWireClient *>(sender());

     // Находим пира в нашем списке анонсированных пиров. Если он там,
     // тогда мы можем использовать список кусков что бы получить статистику, которая
     // поможет нам выбрать к какому пиру подключаться.
     TorrentPeer *peer = 0;
     QList<TorrentPeer *>::Iterator it = d->peers.begin();
     while (it != d->peers.end()) {
         if ((*it)->address == client->peerAddress() && (*it)->port == client->peerPort()) {
             peer = *it;
             break;
         }
         ++it;
     }

     // Если пир является сидом и мы в режиме сида, тогда
     // этот пир нас не интересует.
     if (pieces.count(true) == d->pieceCount) {
         if (peer)
             peer->seed = true;
         emit peerInfoUpdated();
         if (d->state == Seeding) {
             client->abort();
             return;
         } else {
             if (peer)
                 peer->interesting = true;
             if ((client->peerWireState() & PeerWireClient::InterestedInPeer) == 0)
                 client->sendInterested();
             d->callScheduler();
             return;
         }
     }

     // Обновляем наш список доступных кусков.
     if (peer) {
         peer->pieces = pieces;
         peer->numCompletedPieces = pieces.count(true);
     }

     // Проверяем интересующие куски и говорим пиру
     // заинтересованы мы или нет.
     bool interested = false;
     int piecesSize = pieces.size();
     for (int pieceIndex = 0; pieceIndex < piecesSize; ++pieceIndex) {
         if (!pieces.testBit(pieceIndex))
             continue;
         if (!d->completedPieces.testBit(pieceIndex)) {
             interested = true;
             if ((client->peerWireState() & PeerWireClient::InterestedInPeer) == 0) {
                 if (peer)
                     peer->interesting = true;
                 client->sendInterested();
             }

             QMultiMap<PeerWireClient *, TorrentPiece *>::Iterator it = d->payloads.find(client);
             int inProgress = 0;
             while (it != d->payloads.end() && it.key() == client) {
                 if (it.value()->inProgress)
                     inProgress += it.value()->requestedBlocks.count(true);
                 ++it;
             }
             if (!inProgress)
                 d->callScheduler();
             break;
         }
     }
     if (!interested && (client->peerWireState() & PeerWireClient::InterestedInPeer)) {
         if (peer)
             peer->interesting = false;
         client->sendNotInterested();
     }
 }

 void TorrentClient::peerRequestsBlock(int pieceIndex, int begin, int length)
 {
     PeerWireClient *client = qobject_cast<PeerWireClient *>(sender());

     // Тихо игнорируем запросы от заглушенных клиентов
     if (client->peerWireState() & PeerWireClient::ChokingPeer)
         return;

     // Тихо игнорируем запросы кусков, которых у нас нет.
     if (!d->completedPieces.testBit(pieceIndex))
         return;

     // Запрашиваем блок из файлового менеджера
     d->readIds.insert(d->fileManager.read(pieceIndex, begin, length),
                       qobject_cast<PeerWireClient *>(sender()));
 }

 void TorrentClient::blockReceived(int pieceIndex, int begin, const QByteArray &data)
 {
     PeerWireClient *client = qobject_cast<PeerWireClient *>(sender());
     if (data.size() == 0) {
         client->abort();
         return;
     }

     // Игнорируем блок, если он у нас уже есть.
     int blockBit = begin / BlockSize;
     TorrentPiece *piece = d->pendingPieces.value(pieceIndex);
     if (!piece || piece->completedBlocks.testBit(blockBit)) {
         // Отменяем блок если он у нас уже есть и заполняем магистраль.
         requestMore(client);
         return;
     }

     // Если мы уже в разогретом или завершающем режиме, отключаем все дупликаты
     // запросов данного блока.
     if (d->state == WarmingUp || d->state == Endgame) {
         QMultiMap<PeerWireClient *, TorrentPiece *>::Iterator it = d->payloads.begin();
         while (it != d->payloads.end()) {
             PeerWireClient *otherClient = it.key();
             if (otherClient != client && it.value()->index == pieceIndex) {
                 if (otherClient->incomingBlocks().contains(TorrentBlock(pieceIndex, begin, data.size())))
                     it.key()->cancelRequest(pieceIndex, begin, data.size());
             }
             ++it;
         }
     }

     if (d->state != Downloading && d->state != Endgame && d->completedPieces.count(true) > 0)
         d->setState(Downloading);

     // Сохраняем этот блок
     d->fileManager.write(pieceIndex, begin, data);
     piece->completedBlocks.setBit(blockBit);
     piece->requestedBlocks.clearBit(blockBit);

     if (blocksLeftForPiece(piece) == 0) {
         // Просим менеджер файлов проверить заново скачанный кусок
         d->fileManager.verifyPiece(piece->index);

         // Удаляем этот кусок из всех загрузок
         QMultiMap<PeerWireClient *, TorrentPiece *>::Iterator it = d->payloads.begin();
         while (it != d->payloads.end()) {
             if (!it.value() || it.value()->index == piece->index)
                 it = d->payloads.erase(it);
             else
                 ++it;
         }
     }

     // Заполняем магистраль.
     requestMore(client);
 }

 void TorrentClient::peerWireBytesWritten(qint64 size)
 {
     if (!d->transferRateTimer)
         d->transferRateTimer = startTimer(RateControlTimerDelay);

     d->uploadRate[0] += size;
     d->uploadedBytes += size;
     emit dataSent(size);
 }

 void TorrentClient::peerWireBytesReceived(qint64 size)
 {
     if (!d->transferRateTimer)
         d->transferRateTimer = startTimer(RateControlTimerDelay);

     d->downloadRate[0] += size;
     d->downloadedBytes += size;
     emit dataSent(size);
 }

 int TorrentClient::blocksLeftForPiece(const TorrentPiece *piece) const
 {
     int blocksLeft = 0;
     int completedBlocksSize = piece->completedBlocks.size();
     for (int i = 0; i < completedBlocksSize; ++i) {
         if (!piece->completedBlocks.testBit(i))
             ++blocksLeft;
     }
     return blocksLeft;
 }

 void TorrentClient::scheduleUploads()
 {
     // Генерируем список клиентов отсортированный по
     // их скорости передачи.  Когда скачиваем, мы сортируем по скоростям скачки, а когда
     // сидируем, мы сортируем по скоростям отдачи. Сиды в нем не участвуют;
     // нет необходимости разблокировать их.
     QList<PeerWireClient *> allClients = d->connections;
     QMultiMap<int, PeerWireClient *> transferSpeeds;
     foreach (PeerWireClient *client, allClients) {
         if (client->state() == QAbstractSocket::ConnectedState
             && client->availablePieces().count(true) != d->pieceCount) {
             if (d->state == Seeding) {
                 transferSpeeds.insert(client->uploadSpeed(), client);
             } else {
                 transferSpeeds.insert(client->downloadSpeed(), client);
             }
         }
     }

     // Разблокируем главного 'MaxUploads' качальщика (пир которому
     // мы закачиваем) и блокируем всех остальных.
     int maxUploaders = MaxUploads;
     QMapIterator<int, PeerWireClient *> it(transferSpeeds);
     it.toBack();
     while (it.hasPrevious()) {
         PeerWireClient *client = it.previous().value();
         bool interested = (client->peerWireState() & PeerWireClient::PeerIsInterested);

         if (maxUploaders) {
             allClients.removeAll(client);
             if (client->peerWireState() & PeerWireClient::ChokingPeer)
                 client->unchokePeer();
             --maxUploaders;
             continue;
         }

         if ((client->peerWireState() & PeerWireClient::ChokingPeer) == 0) {
             if ((qrand() % 10) == 0)
                 client->abort();
             else
                 client->chokePeer();
             allClients.removeAll(client);
         }
         if (!interested)
             allClients.removeAll(client);
     }

     // Только заинтересованные пиры остались в  allClients. Разблокируем
     // случайного пира что бы позволить ему соревноваться за позицию
     // среди качальщиков.  (Это известно как "оптимистичное разблокирование".)
     if (!allClients.isEmpty()) {
         PeerWireClient *client = allClients[qrand() % allClients.size()];
         if (client->peerWireState() & PeerWireClient::ChokingPeer)
             client->unchokePeer();
     }
 }

 void TorrentClient::scheduleDownloads()
 {
     d->schedulerCalled = false;

     if (d->state == Stopping || d->state == Paused || d->state == Idle)
         return;

     // Проверяем что каждый клиент делает и назначаем нагрузку тем
     // кто простаивает или закончил.
     foreach (PeerWireClient *client, d->connections)
         schedulePieceForClient(client);
 }

 void TorrentClient::schedulePieceForClient(PeerWireClient *client)
 {
     // Распределяем только подключенных клиентов.
     if (client->state() != QTcpSocket::ConnectedState)
         return;

     // Пир нас заблокировал; попробуем позже.
     if (client->peerWireState() & PeerWireClient::ChokedByPeer)
         return;

     // Делаем список кусков клиента ждущих обработки и считаем
     // сколько блоков было запрошено.
     QList<int> currentPieces;
     bool somePiecesAreNotInProgress = false;
     TorrentPiece *lastPendingPiece = 0;
     QMultiMap<PeerWireClient *, TorrentPiece *>::Iterator it = d->payloads.find(client);
     while (it != d->payloads.end() && it.key() == client) {
         lastPendingPiece = it.value();
         if (lastPendingPiece->inProgress) {
             currentPieces << lastPendingPiece->index;
         } else {
             somePiecesAreNotInProgress = true;
         }
         ++it;
     }

     // Пропускаем клиентов которые уже имеют слишком много блоков в обработке.
     if (client->incomingBlocks().size() >= ((d->state == Endgame || d->state == WarmingUp)
                                             ? MaxBlocksInMultiMode : MaxBlocksInProgress))
         return;

     // Если все куски в обработке, но мы не заполнили нашу
     // квоту запроса блоков, тогда нам надо спланировать другой кусок.
     if (!somePiecesAreNotInProgress || client->incomingBlocks().size() > 0)
         lastPendingPiece = 0;
     TorrentPiece *piece = lastPendingPiece;

     // В разогретом состоянии все клиенты запрашивают блоки из  одинаковых кусков.
     if (d->state == WarmingUp && d->pendingPieces.size() >= 4) {
         piece = d->payloads.value(client);
         if (!piece) {
             QList<TorrentPiece *> values = d->pendingPieces.values();
             piece = values.value(qrand() % values.size());
             piece->inProgress = true;
             d->payloads.insert(client, piece);
         }
         if (piece->completedBlocks.count(false) == client->incomingBlocks().size())
             return;
     }

     // Если нет кусков которые сейчас обрабатываются, планируем новый.
     if (!piece) {
         // Строим список еще не завершённых кусков
         // доступных для данного клиента.
         QBitArray incompletePiecesAvailableToClient = d->incompletePieces;

         // Удаляем все куски которые помечены уже находящимися в обработке
         // (т.е., которые этот или другой клиент
         // уже ждут). Специальное правило применяется к разогретому и
         //  завершающему состоянию; в нём мы позволяем нескольким клиентам запрашивать
         // один и тот же кусок. В режиме завершения оно применяется к
         // клиентам которые отдают (больше чем 1.0КБ/с).
         if ((d->state == Endgame && client->uploadSpeed() < 1024) || d->state != WarmingUp) {
             QMap<int, TorrentPiece *>::ConstIterator it = d->pendingPieces.constBegin();
             while (it != d->pendingPieces.constEnd()) {
                 if (it.value()->inProgress)
                     incompletePiecesAvailableToClient.clearBit(it.key());
                 ++it;
             }
         }

         // Удаляем все куски которые клиент не может скачать.
         incompletePiecesAvailableToClient &= client->availablePieces();

         // Удаляем все куски которые этот клиент уже запросил.
         foreach (int i, currentPieces)
             incompletePiecesAvailableToClient.clearBit(i);

         // Продолжаем только в случае когда еще больше кусков может быть спланировано. Если нет
         // доступных кусков и нет блоков в обработке, просто оставляем
         // соединение простаивать;  оно может заинтересовать позже.
         if (incompletePiecesAvailableToClient.count(true) == 0)
             return;

         // Проверяем какие частично завершенные куски могут быть
         // восстановлены и, если такие есть, берем случайный из них.
         QList<TorrentPiece *> partialPieces;
         QMap<int, TorrentPiece *>::ConstIterator it = d->pendingPieces.constBegin();
         while (it != d->pendingPieces.constEnd()) {
             TorrentPiece *tmp = it.value();
             if (incompletePiecesAvailableToClient.testBit(it.key())) {
                 if (!tmp->inProgress || d->state == WarmingUp || d->state == Endgame) {
                     partialPieces << tmp;
                     break;
                 }
             }
             ++it;
         }
         if (!partialPieces.isEmpty())
             piece = partialPieces.value(qrand() % partialPieces.size());

         if (!piece) {
             // Выбираем случайный кусок 3 из 4 раз; в противном случае берём или
             // один из наиболее общих или последний доступный общий кусок,
             // в зависимости от того в каком мы состоянии.
             int pieceIndex = 0;
             if (d->state == WarmingUp || (qrand() & 4) == 0) {
                 int *occurrances = new int[d->pieceCount];
                 memset(occurrances, 0, d->pieceCount * sizeof(int));

                 // Считаем сколько каждого куска доступно.
                 foreach (PeerWireClient *peer, d->connections) {
                     QBitArray peerPieces = peer->availablePieces();
                     int peerPiecesSize = peerPieces.size();
                     for (int i = 0; i < peerPiecesSize; ++i) {
                         if (peerPieces.testBit(i))
                             ++occurrances[i];
                     }
                 }

                 // Находим редчайшие или наиболее общие куски.
                 int numOccurrances = d->state == WarmingUp ? 0 : 99999;
                 QList<int> piecesReadyForDownload;
                 for (int i = 0; i < d->pieceCount; ++i) {
                     if (d->state == WarmingUp) {
                         // Добавляем общие куски
                         if (occurrances[i] >= numOccurrances
                             && incompletePiecesAvailableToClient.testBit(i)) {
                             if (occurrances[i] > numOccurrances)
                                 piecesReadyForDownload.clear();
                             piecesReadyForDownload.append(i);
                             numOccurrances = occurrances[i];
                         }
                     } else {
                         // Добавляем редкие куски
                         if (occurrances[i] <= numOccurrances
                             && incompletePiecesAvailableToClient.testBit(i)) {
                             if (occurrances[i] < numOccurrances)
                                 piecesReadyForDownload.clear();
                             piecesReadyForDownload.append(i);
                             numOccurrances = occurrances[i];
                         }
                     }
                 }

                 // Select one piece randomly
                 pieceIndex = piecesReadyForDownload.at(qrand() % piecesReadyForDownload.size());
                 delete [] occurrances;
             } else {
                 // Индексируем список доступных кусков и выбираем
                 // случайный.
                 QList<int> values;
                 int incompletePiecesAvailableToClientSize = incompletePiecesAvailableToClient.size();
                 for (int i = 0; i < incompletePiecesAvailableToClientSize; ++i) {
                     if (incompletePiecesAvailableToClient.testBit(i))
                         values << i;
                 }
                 pieceIndex = values.at(qrand() % values.size());
             }

             // создаём новый TorrentPiece и заполняем все начальные
             // свойства.
             piece = new TorrentPiece;
             piece->index = pieceIndex;
             piece->length = d->fileManager.pieceLengthAt(pieceIndex);
             int numBlocks = piece->length / BlockSize;
             if (piece->length % BlockSize)
                 ++numBlocks;
             piece->completedBlocks.resize(numBlocks);
             piece->requestedBlocks.resize(numBlocks);
             d->pendingPieces.insert(pieceIndex, piece);
         }

         piece->inProgress = true;
         d->payloads.insert(client, piece);
     }

     // Запрашиваем еще блоков из запланированных кусков.
     requestMore(client);
 }

 void TorrentClient::requestMore(PeerWireClient *client)
 {
     //Делаем список всех кусков которые ждёт этот клиент
     // и считаем число блоков в обработке.
     QMultiMap<PeerWireClient *, TorrentPiece *>::Iterator it = d->payloads.find(client);
     int numBlocksInProgress = client->incomingBlocks().size();
     QList<TorrentPiece *> piecesInProgress;
     while (it != d->payloads.end() && it.key() == client) {
         TorrentPiece *piece = it.value();
         if (piece->inProgress || (d->state == WarmingUp || d->state == Endgame))
             piecesInProgress << piece;
         ++it;
     }

     // Если нет кусков в обработке вызываем планировщик.
     if (piecesInProgress.isEmpty() && d->incompletePieces.count(true)) {
         d->callScheduler();
         return;
     }

     // Если слишком много кусков в обработке то здесь нечего делать.
     int maxInProgress = ((d->state == Endgame || d->state == WarmingUp)
                          ? MaxBlocksInMultiMode : MaxBlocksInProgress);
     if (numBlocksInProgress == maxInProgress)
         return;

     // Начинаем с первым куском который мы ждали, запрашиваем
     // блоки пока не заполним квоту.
     foreach (TorrentPiece *piece, piecesInProgress) {
         numBlocksInProgress += requestBlocks(client, piece, maxInProgress - numBlocksInProgress);
         if (numBlocksInProgress == maxInProgress)
             break;
     }

     // Если мы всё ещё не заполнили квоту, нам надо запланировать больше
     // кусков.
     if (numBlocksInProgress < maxInProgress && d->state != WarmingUp)
         d->callScheduler();
 }

 int TorrentClient::requestBlocks(PeerWireClient *client, TorrentPiece *piece, int maxBlocks)
 {
     // Генерируем список всех незавершённых блоков для этого куска.
     QVector<int> bits;
     int completedBlocksSize = piece->completedBlocks.size();
     for (int i = 0; i < completedBlocksSize; ++i) {
         if (!piece->completedBlocks.testBit(i) && !piece->requestedBlocks.testBit(i))
             bits << i;
     }

     // Нечего больше запрашивать.
     if (bits.size() == 0) {
         if (d->state != WarmingUp && d->state != Endgame)
             return 0;
         bits.clear();
         for (int i = 0; i < completedBlocksSize; ++i) {
             if (!piece->completedBlocks.testBit(i))
                 bits << i;
         }
     }

     if (d->state == WarmingUp || d->state == Endgame) {
         // Мешая список запрашиваемых блоков мы
         // значительно ускоряем режимы разогревания и завершения где
         // один и тот же блок запрашивается у нескольких пиров. 
         // Ускорение получается из-за увеличения шанса получения
         // различных блоков от различных пиров.
         for (int i = 0; i < bits.size(); ++i) {
             int a = qrand() % bits.size();
             int b = qrand() % bits.size();
             int tmp = bits[a];
             bits[a] = bits[b];
             bits[b] = tmp;
         }
     }

     // Запрашиваем не больше блоков чем попросили у нас.
     int blocksToRequest = qMin(maxBlocks, bits.size());

     // Вычисляем смещение и размер каждого блока и посылаем запросы.
     for (int i = 0; i < blocksToRequest; ++i) {
         int blockSize = BlockSize;
         if ((piece->length % BlockSize) && bits.at(i) == completedBlocksSize - 1)
             blockSize = piece->length % BlockSize;
         client->requestBlock(piece->index, bits.at(i) * BlockSize, blockSize);
         piece->requestedBlocks.setBit(bits.at(i));
     }

     return blocksToRequest;
 }

 void TorrentClient::peerChoked()
 {
     PeerWireClient *client = qobject_cast<PeerWireClient *>(sender());
     if (!client)
         return;

     // Когда пир глушит нас, мы мгновенно забываем обо всех блоках
     // Которые мы у него запрашивали. Мы так же удаляем кусок из
     // нагрузки, делая его доступным другим клиентам.
     QMultiMap<PeerWireClient *, TorrentPiece *>::Iterator it = d->payloads.find(client);
     while (it != d->payloads.end() && it.key() == client) {
         it.value()->inProgress = false;
         it.value()->requestedBlocks.fill(false);
         it = d->payloads.erase(it);
     }
 }

 void TorrentClient::peerUnchoked()
 {
     PeerWireClient *client = qobject_cast<PeerWireClient *>(sender());
     if (!client)
         return;

     // Мы разблокированы, что значит что мы можем запросить еще блоков.
     if (d->state != Seeding)
         d->callScheduler();
 }

 void TorrentClient::addToPeerList(const QList<TorrentPeer> &peerList)
 {
     // Добавляем пиров которых мы еще не знаем в наш список пиров.
     QList<QHostAddress> addresses =  QNetworkInterface::allAddresses();
     foreach (TorrentPeer peer, peerList) {
         if (addresses.contains(peer.address)
             && peer.port == TorrentServer::instance()->serverPort()) {
             // Пропускаем наш собственный сервер.
             continue;
         }

         bool known = false;
         foreach (TorrentPeer *knownPeer, d->peers) {
             if (knownPeer->port == peer.port
                 && knownPeer->address == peer.address) {
                 known = true;
                 break;
             }
         }
         if (!known) {
             TorrentPeer *newPeer = new TorrentPeer;
             *newPeer = peer;
             newPeer->interesting = true;
             newPeer->seed = false;
             newPeer->lastVisited = 0;
             newPeer->connectStart = 0;
             newPeer->connectTime = 999999;
             newPeer->pieces.resize(d->pieceCount);
             newPeer->numCompletedPieces = 0;
             d->peers << newPeer;
         }
     }

     // Если мы получаем больше пиров чем мы можем сконнектиться, мы удаляем несколько
     // пиров у которых нет (или низкая) активность.
     int maxPeers = ConnectionManager::instance()->maxConnections() * 3;
     if (d->peers.size() > maxPeers) {
         // Выясняем что пиры подключены и активны
         QSet<TorrentPeer *> activePeers;
         foreach (TorrentPeer *peer, d->peers) {
             foreach (PeerWireClient *client, d->connections) {
                 if (client->peer() == peer && (client->downloadSpeed() + client->uploadSpeed()) > 1024)
                     activePeers << peer;
             }
         }

         // Удаляем неактивных пиров из списка пока мы ниже
         // максимального числа соединений.
         QList<int> toRemove;
         for (int i = 0; i < d->peers.size() && (d->peers.size() - toRemove.size()) > maxPeers; ++i) {
             if (!activePeers.contains(d->peers.at(i)))
                 toRemove << i;
         }
         QListIterator<int> toRemoveIterator(toRemove);
         toRemoveIterator.toBack();
         while (toRemoveIterator.hasPrevious())
             d->peers.removeAt(toRemoveIterator.previous());

         // Если у нас всё ещё слишком много пиров, удаляем старейших.
         while (d->peers.size() > maxPeers)
             d->peers.takeFirst();
     }

     if (d->state != Paused && d->state != Stopping && d->state != Idle) {
         if (d->state == Searching || d->state == WarmingUp)
             connectToPeers();
         else
             d->callPeerConnector();
     }
 }

 void TorrentClient::trackerStopped()
 {
     d->setState(Idle);
     emit stopped();
 }

 void TorrentClient::updateProgress(int progress)
 {
     if (progress == -1 && d->pieceCount > 0) {
         int newProgress = (d->completedPieces.count(true) * 100) / d->pieceCount;
         if (d->lastProgressValue != newProgress) {
             d->lastProgressValue = newProgress;
             emit progressUpdated(newProgress);
         }
     } else if (d->lastProgressValue != progress) {
         d->lastProgressValue = progress;
         emit progressUpdated(progress);
     }
 }


Copyright © 2008 Trolltech Торговые марки
Qt 4.3.5