📄 torrentclient.cpp
字号:
/******************************************************************************** Copyright (C) 2004-2006 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 version 2.0 as published by the Free Software Foundation** and appearing in the file LICENSE.GPL included in the packaging of** this file. Please review the following information to ensure GNU** General Public Licensing requirements will be met:** http://www.trolltech.com/products/qt/opensource.html**** If you are unsure which license is appropriate for your use, please** review the following information:** http://www.trolltech.com/products/qt/licensing.html or contact the** sales department at sales@trolltech.com.**** 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>extern "C" {#include "3rdparty/sha1.h"}// These constants could also be configurable by the user.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); // State / error void setError(TorrentClient::Error error); void setState(TorrentClient::State state); TorrentClient::Error error; TorrentClient::State state; QString errorString; QString stateString; // Where to save data QString destinationFolder; MetaInfo metaInfo; // Announce tracker and file manager QByteArray peerId; QByteArray infoHash; TrackerClient trackerClient; FileManager fileManager; // Connections QList<PeerWireClient *> connections; QList<TorrentPeer *> peers; bool schedulerCalled; void callScheduler(); bool connectingToClients; void callPeerConnector(); int uploadScheduleTimer; // Pieces QMap<int, PeerWireClient *> readIds; QMultiMap<PeerWireClient *, TorrentPiece *> payloads; QMap<int, TorrentPiece *> pendingPieces; QBitArray completedPieces; QBitArray incompletePieces; int pieceCount; // Progress 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 the file manager 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 the tracker client 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; } // Calculate SHA1 hash of the "info" section in the torrent 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){ // Recover partially completed pieces 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; // Save the state of all partially downloaded pieces into a format // suitable for storing in settings. 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; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -