📄 peerwireclient.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 "peerwireclient.h"#include <QTimerEvent>static const int PendingRequestTimeout = 60 * 1000;static const int ClientTimeout = 120 * 1000;static const int ConnectTimeout = 60 * 1000;static const int KeepAliveInterval = 30 * 1000;static const int RateControlTimerDelay = 2000;static const int MinimalHeaderSize = 48;static const int FullHeaderSize = 68;static const char ProtocolId[] = "BitTorrent protocol";static const char ProtocolIdSize = 19;// Reads a 32bit unsigned int from data in network order.static inline quint32 fromNetworkData(const char *data){ const unsigned char *udata = (const unsigned char *)data; return (quint32(udata[0]) << 24) | (quint32(udata[1]) << 16) | (quint32(udata[2]) << 8) | (quint32(udata[3]));}// Writes a 32bit unsigned int from num to data in network order.static inline void toNetworkData(quint32 num, char *data){ unsigned char *udata = (unsigned char *)data; udata[3] = (num & 0xff); udata[2] = (num & 0xff00) >> 8; udata[1] = (num & 0xff0000) >> 16; udata[0] = (num & 0xff000000) >> 24;}// Constructs an unconnected PeerWire client and starts the connect timer.PeerWireClient::PeerWireClient(const QByteArray &peerId, QObject *parent) : QTcpSocket(parent), pendingBlockSizes(0), pwState(ChokingPeer | ChokedByPeer), receivedHandShake(false), gotPeerId(false), sentHandShake(false), nextPacketLength(-1), pendingRequestTimer(0), invalidateTimeout(false), keepAliveTimer(0), torrentPeer(0){ memset(uploadSpeedData, 0, sizeof(uploadSpeedData)); memset(downloadSpeedData, 0, sizeof(downloadSpeedData)); transferSpeedTimer = startTimer(RateControlTimerDelay); timeoutTimer = startTimer(ConnectTimeout); peerIdString = peerId; connect(this, SIGNAL(readyRead()), this, SIGNAL(readyToTransfer())); connect(this, SIGNAL(connected()), this, SIGNAL(readyToTransfer()));}// Registers the peer ID and SHA1 sum of the torrent, and initiates// the handshake.void PeerWireClient::initialize(const QByteArray &infoHash, int pieceCount){ this->infoHash = infoHash; peerPieces.resize(pieceCount); if (!sentHandShake) sendHandShake();}void PeerWireClient::setPeer(TorrentPeer *peer){ torrentPeer = peer;}TorrentPeer *PeerWireClient::peer() const{ return torrentPeer;}QBitArray PeerWireClient::availablePieces() const{ return peerPieces;}QList<TorrentBlock> PeerWireClient::incomingBlocks() const{ return incoming;}// Sends a "choke" message, asking the peer to stop requesting blocks.void PeerWireClient::chokePeer(){ const char message[] = {0, 0, 0, 1, 0}; write(message, sizeof(message)); pwState |= ChokingPeer; // After receiving a choke message, the peer will assume all // pending requests are lost. pendingBlocks.clear(); pendingBlockSizes = 0;}// Sends an "unchoke" message, allowing the peer to start/resume// requesting blocks.void PeerWireClient::unchokePeer(){ const char message[] = {0, 0, 0, 1, 1}; write(message, sizeof(message)); pwState &= ~ChokingPeer; if (pendingRequestTimer) killTimer(pendingRequestTimer);}// Sends a "keep-alive" message to prevent the peer from closing// the connection when there's no activityvoid PeerWireClient::sendKeepAlive(){ const char message[] = {0, 0, 0, 0}; write(message, sizeof(message));}// Sends an "interested" message, informing the peer that it has got// pieces that we'd like to download.void PeerWireClient::sendInterested(){ const char message[] = {0, 0, 0, 1, 2}; write(message, sizeof(message)); pwState |= InterestedInPeer; // After telling the peer that we're interested, we expect to get // unchoked within a certain timeframe; otherwise we'll drop the // connection. if (pendingRequestTimer) killTimer(pendingRequestTimer); pendingRequestTimer = startTimer(PendingRequestTimeout);}// Sends a "not interested" message, informing the peer that it does// not have any pieces that we'd like to download.void PeerWireClient::sendNotInterested(){ const char message[] = {0, 0, 0, 1, 3}; write(message, sizeof(message)); pwState &= ~InterestedInPeer;}// Sends a piece notification / a "have" message, informing the peer// that we have just downloaded a new piece.void PeerWireClient::sendPieceNotification(int piece){ if (!sentHandShake) sendHandShake(); char message[] = {0, 0, 0, 5, 4, 0, 0, 0, 0}; toNetworkData(piece, &message[5]); write(message, sizeof(message));}// Sends the complete list of pieces that we have downloaded.void PeerWireClient::sendPieceList(const QBitArray &bitField){ // The bitfield message may only be sent immediately after the // handshaking sequence is completed, and before any other // messages are sent. if (!sentHandShake) sendHandShake(); // Don't send the bitfield if it's all zeros. if (bitField.count(true) == 0) return; int bitFieldSize = bitField.size(); int size = (bitFieldSize + 7) / 8; QByteArray bits(size, '\0'); for (int i = 0; i < bitFieldSize; ++i) { if (bitField.testBit(i)) { quint32 byte = quint32(i) / 8; quint32 bit = quint32(i) % 8; bits[byte] = uchar(bits.at(byte)) | (1 << (7 - bit)); } } char message[] = {0, 0, 0, 1, 5}; toNetworkData(bits.size() + 1, &message[0]); write(message, sizeof(message)); write(bits);}// Sends a request for a block.void PeerWireClient::requestBlock(int piece, int offset, int length){ char message[] = {0, 0, 0, 1, 6}; toNetworkData(13, &message[0]); write(message, sizeof(message)); char numbers[4 * 3]; toNetworkData(piece, &numbers[0]); toNetworkData(offset, &numbers[4]); toNetworkData(length, &numbers[8]); write(numbers, sizeof(numbers)); incoming << TorrentBlock(piece, offset, length); // After requesting a block, we expect the block to be sent by the // other peer within a certain number of seconds. Otherwise, we // drop the connection. if (pendingRequestTimer) killTimer(pendingRequestTimer); pendingRequestTimer = startTimer(PendingRequestTimeout);}// Cancels a request for a block.void PeerWireClient::cancelRequest(int piece, int offset, int length){ char message[] = {0, 0, 0, 1, 8}; toNetworkData(13, &message[0]); write(message, sizeof(message)); char numbers[4 * 3]; toNetworkData(piece, &numbers[0]); toNetworkData(offset, &numbers[4]); toNetworkData(length, &numbers[8]); write(numbers, sizeof(numbers)); incoming.removeAll(TorrentBlock(piece, offset, length));}// Sends a block to the peer.void PeerWireClient::sendBlock(int piece, int offset, const QByteArray &data){ QByteArray block; char message[] = {0, 0, 0, 1, 7}; toNetworkData(9 + data.size(), &message[0]); block += QByteArray(message, sizeof(message)); char numbers[4 * 2]; toNetworkData(piece, &numbers[0]); toNetworkData(offset, &numbers[4]); block += QByteArray(numbers, sizeof(numbers)); block += data; BlockInfo blockInfo; blockInfo.pieceIndex = piece; blockInfo.offset = offset; blockInfo.length = data.size(); blockInfo.block = block; pendingBlocks << blockInfo; pendingBlockSizes += block.size(); if (pendingBlockSizes > 32 * 16384) { chokePeer(); unchokePeer(); return; } emit readyToTransfer();}// Attempts to write 'bytes' bytes to the socket from the buffer.// This is used by RateController, which precisely controls how much// each client can write.qint64 PeerWireClient::writeToSocket(qint64 bytes){ qint64 totalWritten = 0; do { if (outgoingBuffer.isEmpty() && !pendingBlocks.isEmpty()) { BlockInfo block = pendingBlocks.takeFirst(); pendingBlockSizes -= block.length; outgoingBuffer += block.block; } qint64 written = QTcpSocket::writeData(outgoingBuffer.constData(), qMin<qint64>(bytes - totalWritten, outgoingBuffer.size())); if (written <= 0) return totalWritten ? totalWritten : written; totalWritten += written; uploadSpeedData[0] += written; outgoingBuffer.remove(0, written); } while (totalWritten < bytes && (!outgoingBuffer.isEmpty() || !pendingBlocks.isEmpty())); return totalWritten;}// Attempts to read at most 'bytes' bytes from the socket.qint64 PeerWireClient::readFromSocket(qint64 bytes)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -