⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 torrentclient.cpp

📁 基于QT4图形库的BT下载客户端源码
💻 CPP
📖 第 1 页 / 共 4 页
字号:
    // The peer has choked us; try again later.
    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(qrand() % 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(qrand() % 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 || (qrand() & 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(qrand() % 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(qrand() % 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 = qrand() % bits.size();
            int b = qrand() % 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 + -