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

📄 torrentclient.cpp

📁 基于QT4图形库的BT下载客户端源码
💻 CPP
📖 第 1 页 / 共 4 页
字号:
}

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()
           && (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;

        // Pick a random peer from the list of weighed peers.
        TorrentPeer *peer = weighedPeers.takeAt(qrand() % 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 + -