📄 torrentclient.cpp
字号:
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); // Initialize the file manager 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); // Update the state State oldState = d->state; d->setState(Stopping); // Stop the timer if (d->transferRateTimer) { killTimer(d->transferRateTimer); d->transferRateTimer = 0; } // Abort all existing connections foreach (PeerWireClient *client, d->connections) client->abort(); d->connections.clear(); // Perhaps stop the tracker if (oldState > Preparing) { d->trackerClient.stop(); } else { d->setState(Idle); emit stopped(); }}void TorrentClient::setPaused(bool paused){ if (paused) { // Abort all connections, and set the max number of // connections to 0. Keep the list of peers, so we can quickly // resume later. d->setState(Paused); foreach (PeerWireClient *client, d->connections) client->abort(); d->connections.clear(); TorrentServer::instance()->removeClient(this); } else { // Restore the max number of connections, and start the peer // connector. We should also quickly start receiving incoming // connections. 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) { // Update the state of who's choked and who's not scheduleUploads(); return; } if (event->timerId() != d->transferRateTimer) { QObject::timerEvent(event); return; } // Calculate average upload/download rate 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)); // Stop the timer if there is no activity. if (downloadBytesPerSecond == 0 && uploadBytesPerSecond == 0) { killTimer(d->transferRateTimer); d->transferRateTimer = 0; }}void TorrentClient::sendToPeer(int readId, int pieceIndex, int begin, const QByteArray &data){ // Send the requested block to the peer if the client connection // still exists; otherwise do nothing. This slot is called by the // file manager after it has read a block of 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(){ // Update our list of completed and incomplete pieces. 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(); // If the checksums show that what the dumped state thought was // partial was in fact complete, then we trust the checksums. 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); // Start the server TorrentServer *server = TorrentServer::instance(); if (!server->isListening()) { // Set up the peer wire server 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); // Start the tracker client 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); // Remove this piece from all payloads 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) { // If a piece did not pass the SHA1 check, we'll simply clear // its state, and the scheduler will re-request it piece->inProgress = false; piece->completedBlocks.fill(false); piece->requestedBlocks.fill(false); d->callScheduler(); return; } // Update the peer list so we know who's still interesting. 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 the piece and update our structures. delete piece; d->pendingPieces.remove(pieceIndex); d->completedPieces.setBit(pieceIndex); d->incompletePieces.clearBit(pieceIndex); // Notify connected peers. foreach (PeerWireClient *client, d->connections) { if (client->state() == QAbstractSocket::ConnectedState && !client->availablePieces().testBit(pieceIndex)) { client->sendPieceNotification(pieceIndex); } } // Notify the tracker if we've entered Seeding status; otherwise // call the scheduler. 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); // Find the list of peers we are not currently connected to, where // the more interesting peers are listed more than once. QList<TorrentPeer *> weighedPeers = weighedFreePeers(); // Start as many connections as we can while (!weighedPeers.isEmpty() && ConnectionManager::instance()->canAddConnection() && (rand() % (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; // Pick a random peer from the list of weighed peers. TorrentPeer *peer = weighedPeers.takeAt(rand() % weighedPeers.size()); weighedPeers.removeAll(peer); peer->connectStart = QDateTime::currentDateTime().toTime_t(); peer->lastVisited = peer->connectStart; // Connect to the peer. client->setPeer(peer); client->connectToHost(peer->address, peer->port); }}QList<TorrentPeer *> TorrentClient::weighedFreePeers() const{ QList<TorrentPeer *> weighedPeers; // Generate a list of peers that we want to connect to. 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; } // Nothing to connect to if (freePeers.isEmpty()) return weighedPeers; // Assign points based on connection speed and pieces available. 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) // An unknown peer is as interesting as a seed tmp += d->pieceCount; // 1/5 of the total score for each second below 5 it takes to // connect. if (peer->connectTime < 5) tmp += (d->pieceCount / 10) * (5 - peer->connectTime); } points << QPair<int, TorrentPeer *>(tmp, peer); } qSort(points); // Minimize the list so the point difference is never more than 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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -