📄 torrentclient.cpp
字号:
if (client->peerWireState() & PeerWireClient::ChokedByPeer) return; // Make a list of all the client's pending pieces, and count how // many blocks have been requested. 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; } // Skip clients that already have too many blocks in progress. if (client->incomingBlocks().size() >= ((d->state == Endgame || d->state == WarmingUp) ? MaxBlocksInMultiMode : MaxBlocksInProgress)) return; // If all pieces are in progress, but we haven't filled up our // block requesting quota, then we need to schedule another piece. if (!somePiecesAreNotInProgress || client->incomingBlocks().size() > 0) lastPendingPiece = 0; TorrentPiece *piece = lastPendingPiece; // In warmup state, all clients request blocks from the same pieces. if (d->state == WarmingUp && d->pendingPieces.size() >= 4) { piece = d->payloads.value(client); if (!piece) { QList<TorrentPiece *> values = d->pendingPieces.values(); piece = values.value(rand() % values.size()); piece->inProgress = true; d->payloads.insert(client, piece); } if (piece->completedBlocks.count(false) == client->incomingBlocks().size()) return; } // If no pieces are currently in progress, schedule a new one. if (!piece) { // Build up a list of what pieces that we have not completed // are available to this client. QBitArray incompletePiecesAvailableToClient = d->incompletePieces; // Remove all pieces that are marked as being in progress // already (i.e., pieces that this or other clients are // already waiting for). A special rule applies to warmup and // endgame mode; there, we allow several clients to request // the same piece. In endgame mode, this only applies to // clients that are currently uploading (more than 1.0KB/s). 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; } } // Remove all pieces that the client cannot download. incompletePiecesAvailableToClient &= client->availablePieces(); // Remove all pieces that this client has already requested. foreach (int i, currentPieces) incompletePiecesAvailableToClient.clearBit(i); // Only continue if more pieces can be scheduled. If no pieces // are available and no blocks are in progress, just leave // the connection idle; it might become interesting later. if (incompletePiecesAvailableToClient.count(true) == 0) return; // Check if any of the partially completed pieces can be // recovered, and if so, pick a random one of them. 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(rand() % partialPieces.size()); if (!piece) { // Pick a random piece 3 out of 4 times; otherwise, pick either // one of the most common or the least common pieces available, // depending on the state we're in. int pieceIndex = 0; if (d->state == WarmingUp || (rand() & 4) == 0) { int *occurrances = new int[d->pieceCount]; memset(occurrances, 0, d->pieceCount * sizeof(int)); // Count how many of each piece are available. 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]; } } // Find the rarest or most common pieces. int numOccurrances = d->state == WarmingUp ? 0 : 99999; QList<int> piecesReadyForDownload; for (int i = 0; i < d->pieceCount; ++i) { if (d->state == WarmingUp) { // Add common pieces if (occurrances[i] >= numOccurrances && incompletePiecesAvailableToClient.testBit(i)) { if (occurrances[i] > numOccurrances) piecesReadyForDownload.clear(); piecesReadyForDownload.append(i); numOccurrances = occurrances[i]; } } else { // Add rare pieces 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(rand() % piecesReadyForDownload.size()); delete [] occurrances; } else { // Make up a list of available piece indices, and pick // a random one. QList<int> values; int incompletePiecesAvailableToClientSize = incompletePiecesAvailableToClient.size(); for (int i = 0; i < incompletePiecesAvailableToClientSize; ++i) { if (incompletePiecesAvailableToClient.testBit(i)) values << i; } pieceIndex = values.at(rand() % values.size()); } // Create a new TorrentPiece and fill in all initial // properties. 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); } // Request more blocks from all pending pieces. requestMore(client);}void TorrentClient::requestMore(PeerWireClient *client){ // Make a list of all pieces this client is currently waiting for, // and count the number of blocks in progress. 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 no pieces are in progress, call the scheduler. if (piecesInProgress.isEmpty() && d->incompletePieces.count(true)) { d->callScheduler(); return; } // If too many pieces are in progress, there's nothing to do. int maxInProgress = ((d->state == Endgame || d->state == WarmingUp) ? MaxBlocksInMultiMode : MaxBlocksInProgress); if (numBlocksInProgress == maxInProgress) return; // Starting with the first piece that we're waiting for, request // blocks until the quota is filled up. foreach (TorrentPiece *piece, piecesInProgress) { numBlocksInProgress += requestBlocks(client, piece, maxInProgress - numBlocksInProgress); if (numBlocksInProgress == maxInProgress) break; } // If we still didn't fill up the quota, we need to schedule more // pieces. if (numBlocksInProgress < maxInProgress && d->state != WarmingUp) d->callScheduler();}int TorrentClient::requestBlocks(PeerWireClient *client, TorrentPiece *piece, int maxBlocks){ // Generate the list of incomplete blocks for this piece. 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; } // Nothing more to request. 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) { // By randomizing the list of blocks to request, we // significantly speed up the warmup and endgame modes, where // the same blocks are requested from multiple peers. The // speedup comes from an increased chance of receiving // different blocks from the different peers. for (int i = 0; i < bits.size(); ++i) { int a = rand() % bits.size(); int b = rand() % bits.size(); int tmp = bits[a]; bits[a] = bits[b]; bits[b] = tmp; } } // Request no more blocks than we've been asked to. int blocksToRequest = qMin(maxBlocks, bits.size()); // Calculate the offset and size of each block, and send requests. 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; // When the peer chokes us, we immediately forget about all blocks // we've requested from it. We also remove the piece from out // payload, making it available to other clients. 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; // We got unchoked, which means we can request more blocks. if (d->state != Seeding) d->callScheduler();}void TorrentClient::addToPeerList(const QList<TorrentPeer> &peerList){ // Add peers we don't already know of to our list of peers. foreach (TorrentPeer peer, peerList) { if (peer.address == TorrentServer::instance()->serverAddress() && 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; } } // If we've got more peers than we can connect to, we remove some // of the peers that have no (or low) activity. int maxPeers = ConnectionManager::instance()->maxConnections() * 3; if (d->peers.size() > maxPeers) { // Find what peers are currently connected & active QSet<TorrentPeer *> activePeers; foreach (TorrentPeer *peer, d->peers) { foreach (PeerWireClient *client, d->connections) { if (client->peer() == peer && (client->downloadSpeed() + client->uploadSpeed()) > 1024) activePeers << peer; } } // Remove inactive peers from the peer list until we're below // the max connections count. 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()); // If we still have too many peers, remove the oldest ones. 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); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -