aria2/src/DefaultBtInteractive.cc

308 lines
9.3 KiB
C++
Raw Normal View History

2006-12-24 Tatsuhiro Tsujikawa <tujikawa at rednoah dot com> Rewritten a portion of bittorrent implementation: * src/BtMessageValidator.h: New class. * src/BtBitfieldMessageValidator.h: New class. * src/BtHandshakeMessageValidator.h: New class. * src/BtRequestMessageValidator.h: New class. * src/BtSuggestPieceMessageValidator.h: New class. * src/BtAllowedFastMessageValidator.h: New class. * src/BtRejectMessageValidator.h: New class. * src/BtCancelMessageValidator.h: New class. * src/BtPieceMessageValidator.h: New class. * src/BtHaveMessageValidator.h: New class. * src/BtEventListener.h: New class. * src/AbstractBtEventListener.h: New class. * src/BtEvent.h: New class. * src/BtChokingEvent.h: New class. * src/BtChokedEvent.h: New class. * src/BtCancelSendingPieceEvent.h: New class. * src/BtAbortOutstandingRequestEvent.h: New class. * src/Randomizer.h: New class. * src/SimpleRandomizer.h: New class. * src/BtMessage.h: New class. * src/AbstractBtMessage.h: New class. * src/SimpleBtMessage.h: New class. * src/BtHaveMessage.h: New class. * src/BtInterestedMessage.h: New class. * src/BtAllowedFastMessage.h: New class. * src/BtUnchokeMessage.h: New class. * src/BtCancelMessage.h: New class. * src/BtNotInterestedMessage.h: New class. * src/BtChokeMessage.h: New class. * src/BtHaveNoneMessage.h: New class. * src/BtHandshakeMessage.h: New class. * src/BtSuggestPieceMessage.h: New class. * src/BtHaveMessage.h: New class. * src/BtPieceMessage.h: New class. * src/BtHaveAllMessage.h: New class. * src/BtKeepAliveMessage.h: New class. * src/BtPortMessage.h: New class. * src/BtRejectMessage.h: New class. * src/BtBitfieldMessage.h: New class. * src/BtRequestMessage.h: New class. * src/DefaultBtRequestFactory.h: New class. * src/DefaultBtMessageReceiver.h: New class. * src/BtInteractive.h: New class. * src/BtMessageDispatcher.h: New class. * src/DefaultBtMessageDispatcher.h: New class. * src/DefaultBtInteractive.h: New class. * src/BitfieldManFactory.h: New class. * src/HandleRegistry.h: New class. * src/BtMessageFactory.h: New class. * src/BtMessageReceiver.h: New class. * src/DefaultBtMessageFactory.h: New class. * src/PeerObject.h: New class. * src/BtRequestFactory.h: New class.
2006-12-24 06:25:21 +00:00
/* <!-- copyright */
/*
* aria2 - The high speed download utility
*
* Copyright (C) 2006 Tatsuhiro Tsujikawa
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* In addition, as a special exception, the copyright holders give
* permission to link the code of portions of this program with the
* OpenSSL library under certain conditions as described in each
* individual source file, and distribute linked combinations
* including the two.
* You must obey the GNU General Public License in all respects
* for all of the code used other than OpenSSL. If you modify
* file(s) with this exception, you may extend this exception to your
* version of the file(s), but you are not obligated to do so. If you
* do not wish to do so, delete this exception statement from your
* version. If you delete this exception statement from all source
* files in the program, then also delete it here.
*/
/* copyright --> */
#include "DefaultBtInteractive.h"
#include "prefs.h"
#include "message.h"
#include "BtHandshakeMessage.h"
#include "Util.h"
#include "BtKeepAliveMessage.h"
#include "BtChokeMessage.h"
#include "BtUnchokeMessage.h"
#include "DlAbortEx.h"
void DefaultBtInteractive::initiateHandshake() {
BtMessageHandle message =
BT_MESSAGE_FACTORY(btContext, peer)->
createHandshakeMessage(btContext->getInfoHash(),
btContext->getPeerId());
dispatcher->addMessageToQueue(message);
dispatcher->sendMessages();
}
BtMessageHandle DefaultBtInteractive::receiveHandshake(bool quickReply) {
BtHandshakeMessageHandle message =
btMessageReceiver->receiveHandshake(quickReply);
if(message.isNull()) {
return 0;
}
peer->setPeerId(message->getPeerId());
logger->info(MSG_RECEIVE_PEER_MESSAGE, cuid,
peer->ipaddr.c_str(), peer->port,
message->toString().c_str());
return message;
}
BtMessageHandle DefaultBtInteractive::receiveAndSendHandshake() {
return receiveHandshake(true);
}
void DefaultBtInteractive::doPostHandshakeProcessing() {
// TODO where is the valid place to rest haveCheckTime?
haveCheckPoint.reset();
keepAliveCheckPoint.reset();
floodingCheckPoint.reset();
addBitfieldMessageToQueue();
addAllowedFastMessageToQueue();
}
void DefaultBtInteractive::addBitfieldMessageToQueue() {
BtMessageFactoryHandle factory = BT_MESSAGE_FACTORY(btContext, peer);
if(peer->isFastExtensionEnabled()) {
if(pieceStorage->downloadFinished()) {
dispatcher->addMessageToQueue(factory->createHaveAllMessage());
} else if(pieceStorage->getCompletedLength() > 0) {
dispatcher->addMessageToQueue(factory->createBitfieldMessage());
} else {
dispatcher->addMessageToQueue(factory->createHaveNoneMessage());
}
} else {
if(pieceStorage->getCompletedLength() > 0) {
dispatcher->addMessageToQueue(factory->createBitfieldMessage());
}
}
}
void DefaultBtInteractive::addAllowedFastMessageToQueue() {
if(peer->isFastExtensionEnabled()) {
Integers fastSet = Util::computeFastSet(peer->ipaddr,
btContext->getInfoHash(),
btContext->getNumPieces(),
allowedFastSetSize);
for(Integers::const_iterator itr = fastSet.begin();
itr != fastSet.end(); itr++) {
dispatcher->addMessageToQueue(BT_MESSAGE_FACTORY(btContext, peer)->
createAllowedFastMessage(*itr));
}
}
}
void DefaultBtInteractive::decideChoking() {
if(peer->shouldBeChoking()) {
if(!peer->amChoking) {
dispatcher->addMessageToQueue(BT_MESSAGE_FACTORY(btContext, peer)->
createChokeMessage());
}
} else {
if(peer->amChoking) {
dispatcher->addMessageToQueue(BT_MESSAGE_FACTORY(btContext, peer)->
createUnchokeMessage());
}
}
}
void DefaultBtInteractive::checkHave() {
BtMessageFactoryHandle factory = BT_MESSAGE_FACTORY(btContext, peer);
Integers indexes =
pieceStorage->getAdvertisedPieceIndexes(cuid, haveCheckPoint);
haveCheckPoint.reset();
if(indexes.size() >= 20) {
if(peer->isFastExtensionEnabled() && pieceStorage->downloadFinished()) {
dispatcher->addMessageToQueue(factory->createHaveAllMessage());
} else {
dispatcher->addMessageToQueue(factory->createBitfieldMessage());
}
} else {
for(Integers::iterator itr = indexes.begin(); itr != indexes.end(); itr++) {
dispatcher->addMessageToQueue(factory->createHaveMessage(*itr));
}
}
}
void DefaultBtInteractive::sendKeepAlive() {
if(keepAliveCheckPoint.elapsed(option->getAsInt(PREF_BT_KEEP_ALIVE_INTERVAL))) {
if(dispatcher->countMessageInQueue() == 0) {
dispatcher->addMessageToQueue(BT_MESSAGE_FACTORY(btContext, peer)->createKeepAliveMessage());
dispatcher->sendMessages();
}
keepAliveCheckPoint.reset();
}
}
void DefaultBtInteractive::receiveMessages() {
for(int i = 0; i < 50; i++) {
int maxSpeedLimit = option->getAsInt(PREF_MAX_DOWNLOAD_LIMIT);
if(maxSpeedLimit > 0) {
TransferStat stat = peerStorage->calculateStat();
if(maxSpeedLimit < stat.downloadSpeed) {
break;
}
}
BtMessageHandle message = btMessageReceiver->receiveMessage();
if(message.isNull()) {
break;
}
logger->info(MSG_RECEIVE_PEER_MESSAGE, cuid,
peer->ipaddr.c_str(), peer->port,
message->toString().c_str());
message->doReceivedAction();
switch(message->getId()) {
case BtKeepAliveMessage::ID:
floodingStat.incKeepAliveCount();
break;
case BtChokeMessage::ID:
if(!peer->peerChoking) {
floodingStat.incChokeUnchokeCount();
}
break;
case BtUnchokeMessage::ID:
if(peer->peerChoking) {
floodingStat.incChokeUnchokeCount();
}
break;
}
}
}
void DefaultBtInteractive::decideInterest() {
if(!pieceStorage->hasMissingPiece(peer)) {
if(peer->amInterested) {
logger->debug("CUID#%d - Not interested in the peer", cuid);
dispatcher->
addMessageToQueue(BT_MESSAGE_FACTORY(btContext, peer)->
createNotInterestedMessage());
}
} else {
if(!peer->amInterested) {
logger->debug("CUID#%d - Interested in the peer", cuid);
dispatcher->
addMessageToQueue(BT_MESSAGE_FACTORY(btContext, peer)->
createInterestedMessage());
}
}
}
void DefaultBtInteractive::fillPiece(int maxPieceNum) {
if(pieceStorage->hasMissingPiece(peer)) {
if(peer->peerChoking) {
dispatcher->doChokedAction();
if(peer->isFastExtensionEnabled()) {
while(btRequestFactory->countTargetPiece() < maxPieceNum) {
PieceHandle piece = pieceStorage->getMissingFastPiece(peer);
if(piece.isNull()) {
break;
} else {
btRequestFactory->addTargetPiece(piece);
}
}
}
} else {
while(btRequestFactory->countTargetPiece() < maxPieceNum) {
PieceHandle piece = pieceStorage->getMissingPiece(peer);
if(piece.isNull()) {
break;
} else {
btRequestFactory->addTargetPiece(piece);
}
}
}
}
}
void DefaultBtInteractive::addRequests() {
uint32_t MAX_PENDING_REQUEST;
if(peer->getLatency() < 500) {
MAX_PENDING_REQUEST = 24;
} else if(peer->getLatency() < 1500) {
MAX_PENDING_REQUEST = 12;
} else {
MAX_PENDING_REQUEST = 6;
}
uint32_t pieceNum;
if(pieceStorage->isEndGame()) {
pieceNum = 1;
} else {
uint32_t blocks = DIV_FLOOR(btContext->getPieceLength(), BLOCK_LENGTH);
pieceNum = DIV_FLOOR(MAX_PENDING_REQUEST, blocks);
}
fillPiece(pieceNum);
uint32_t reqNumToCreate =
MAX_PENDING_REQUEST <= dispatcher->countOutstandingRequest() ?
0 : MAX_PENDING_REQUEST-dispatcher->countOutstandingRequest();
if(reqNumToCreate > 0) {
//logger->debug("CUID#%d - %u requets to go.", cuid, reqNumToCreate);
BtMessages requests;
if(pieceStorage->isEndGame()) {
requests = btRequestFactory->createRequestMessagesOnEndGame(reqNumToCreate);
} else {
requests = btRequestFactory->createRequestMessages(reqNumToCreate);
}
dispatcher->addMessageToQueue(requests);
}
}
void DefaultBtInteractive::cancelAllPiece() {
btRequestFactory->removeAllTargetPiece();
}
void DefaultBtInteractive::sendPendingMessage() {
dispatcher->sendMessages();
}
void DefaultBtInteractive::detectMessageFlooding() {
if(floodingCheckPoint.elapsed(FLOODING_CHECK_INTERVAL)) {
if(floodingStat.getChokeUnchokeCount() >= 2 ||
floodingStat.getKeepAliveCount() >= 2) {
throw new DlAbortEx("Flooding detected.");
} else {
floodingStat.reset();
}
floodingCheckPoint.reset();
}
}
void DefaultBtInteractive::doInteractionProcessing() {
decideChoking();
detectMessageFlooding();
dispatcher->checkRequestSlotAndDoNecessaryThing();
checkHave();
sendKeepAlive();
receiveMessages();
btRequestFactory->removeCompletedPiece();
decideInterest();
if(!pieceStorage->downloadFinished()) {
addRequests();
}
sendPendingMessage();
}