You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
346 lines
8.3 KiB
346 lines
8.3 KiB
/***************************************************************************
|
|
* Copyright (C) 2005 by Joris Guisson *
|
|
* joris.guisson@gmail.com *
|
|
* *
|
|
* 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. *
|
|
***************************************************************************/
|
|
#if 0
|
|
#include <util/log.h>
|
|
#include <util/timer.h>
|
|
#include <util/functions.h>
|
|
#include <torrent/globals.h>
|
|
#include <interfaces/functions.h>
|
|
#include "newchokealgorithm.h"
|
|
#include "peermanager.h"
|
|
#include "peer.h"
|
|
#include "packetwriter.h"
|
|
#include "peeruploader.h"
|
|
|
|
|
|
using namespace kt;
|
|
|
|
namespace bt
|
|
{
|
|
|
|
|
|
|
|
NewChokeAlgorithm::NewChokeAlgorithm(): ChokeAlgorithm()
|
|
{
|
|
round_state = 1;
|
|
}
|
|
|
|
|
|
NewChokeAlgorithm::~NewChokeAlgorithm()
|
|
{}
|
|
|
|
int RevDownloadRateCmp(Peer* a,Peer* b)
|
|
{
|
|
if (b->getDownloadRate() > a->getDownloadRate())
|
|
return 1;
|
|
else if (a->getDownloadRate() > b->getDownloadRate())
|
|
return -1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
void NewChokeAlgorithm::doChokingLeechingState(PeerManager & pman,ChunkManager & cman,const kt::TorrentStats & stats)
|
|
{
|
|
Uint32 num_peers = pman.getNumConnectedPeers();
|
|
if (num_peers == 0)
|
|
return;
|
|
|
|
Uint32 now = GetCurrentTime();
|
|
Peer* poup = pman.findPeer(opt_unchoked_peer_id);
|
|
Peer* unchokers[] = {0,0,0,0};
|
|
|
|
// first find the planned optimistic unchoked peer if we are in the correct round
|
|
if (round_state == 1 || poup == 0)
|
|
{
|
|
opt_unchoked_peer_id = findPlannedOptimisticUnchokedPeer(pman);
|
|
poup = pman.findPeer(opt_unchoked_peer_id);
|
|
}
|
|
|
|
PeerPtrList peers,other;
|
|
// now get all the peers who are interested and have sent us a piece in the
|
|
// last 30 seconds
|
|
for (Uint32 i = 0;i < num_peers;i++)
|
|
{
|
|
Peer* p = pman.getPeer(i);
|
|
if (!p)
|
|
continue;
|
|
|
|
if (!p->isSeeder())
|
|
{
|
|
if (p->isInterested() && now - p->getTimeSinceLastPiece() <= 30000)
|
|
peers.append(p);
|
|
else
|
|
other.append(p);
|
|
}
|
|
else
|
|
{
|
|
p->choke();
|
|
}
|
|
}
|
|
|
|
// sort them using a reverse download rate compare
|
|
// so that the fastest downloaders are in front
|
|
peers.setCompareFunc(RevDownloadRateCmp);
|
|
peers.sort();
|
|
other.setCompareFunc(RevDownloadRateCmp);
|
|
other.sort();
|
|
|
|
// get the first tree and punt them in the unchokers
|
|
for (Uint32 i = 0;i < 3;i++)
|
|
{
|
|
if (i < peers.count())
|
|
{
|
|
unchokers[i] = peers.at(i);
|
|
}
|
|
}
|
|
|
|
// see if poup if part of the first 3
|
|
// and if necessary replace it
|
|
bool poup_in_unchokers = false;
|
|
Uint32 attempts = 0;
|
|
do
|
|
{
|
|
poup_in_unchokers = false;
|
|
for (Uint32 i = 0;i < 3;i++)
|
|
{
|
|
if (unchokers[i] != poup)
|
|
continue;
|
|
|
|
opt_unchoked_peer_id = findPlannedOptimisticUnchokedPeer(pman);
|
|
poup = pman.findPeer(opt_unchoked_peer_id);
|
|
poup_in_unchokers = true;
|
|
break;
|
|
}
|
|
// we don't want to keep trying this forever, so limit it to 5 atttempts
|
|
attempts++;
|
|
}while (poup_in_unchokers && attempts < 5);
|
|
|
|
unchokers[3] = poup;
|
|
|
|
Uint32 other_idx = 0;
|
|
Uint32 peers_idx = 3;
|
|
// unchoke the 4 unchokers
|
|
for (Uint32 i = 0;i < 4;i++)
|
|
{
|
|
if (!unchokers[i])
|
|
{
|
|
// pick some other peer to unchoke
|
|
unchokers[i] = peers.at(peers_idx++);
|
|
if (unchokers[i] == poup) // it must not be equal to the poup
|
|
unchokers[i] = peers.at(peers_idx++);
|
|
|
|
// nobody in the peers list, try the others list
|
|
if (!unchokers[i])
|
|
unchokers[i] = other.at(other_idx++);
|
|
}
|
|
|
|
if (unchokers[i])
|
|
unchokers[i]->getPacketWriter().sendUnchoke();
|
|
}
|
|
|
|
// choke the rest
|
|
for (Uint32 i = 0;i < num_peers;i++)
|
|
{
|
|
Peer* p = pman.getPeer(i);
|
|
if (p == unchokers[0] || p == unchokers[1] || p == unchokers[2] || p == unchokers[3])
|
|
continue;
|
|
if (p)
|
|
p->choke();
|
|
}
|
|
|
|
round_state++;
|
|
if (round_state > 3)
|
|
round_state = 1;
|
|
}
|
|
|
|
Uint32 NewChokeAlgorithm::findPlannedOptimisticUnchokedPeer(PeerManager& pman)
|
|
{
|
|
Uint32 num_peers = pman.getNumConnectedPeers();
|
|
if (num_peers == 0)
|
|
return UNDEFINED_ID;
|
|
|
|
// find a random peer that is choked and interested
|
|
Uint32 start = rand() % num_peers;
|
|
Uint32 i = (start + 1) % num_peers;
|
|
while (i != start)
|
|
{
|
|
Peer* p = pman.getPeer(i);
|
|
if (p && p->isChoked() && p->isInterested() && !p->isSeeder())
|
|
return p->getID();
|
|
i = (i + 1) % num_peers;
|
|
}
|
|
|
|
// we do not expect to have 4 billion peers
|
|
return 0xFFFFFFFF;
|
|
}
|
|
|
|
//////////////////////////////////////////////
|
|
|
|
int NChokeCmp(Peer* a,Peer* b)
|
|
{
|
|
Uint32 now = GetCurrentTime();
|
|
// if they have pending upload requests or they were unchoked in the last 20 seconds,
|
|
// they are category 1
|
|
bool a_first_class = a->getPeerUploader()->getNumRequests() > 0 ||
|
|
(now - a->getUnchokeTime() <= 20000);
|
|
bool b_first_class = b->getPeerUploader()->getNumRequests() > 0 ||
|
|
(now - b->getUnchokeTime() <= 20000);
|
|
|
|
if (a_first_class && !b_first_class)
|
|
{
|
|
// category 1 come first
|
|
return -1;
|
|
}
|
|
else if (!a_first_class && b_first_class)
|
|
{
|
|
// category 1 come first
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
// use upload rate to differentiate peers of the same class
|
|
if (a->getUploadRate() > b->getUploadRate())
|
|
return -1;
|
|
else if (b->getUploadRate() > a->getUploadRate())
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
void NewChokeAlgorithm::doChokingSeedingState(PeerManager & pman,ChunkManager & cman,const kt::TorrentStats & stats)
|
|
{
|
|
Uint32 num_peers = pman.getNumConnectedPeers();
|
|
if (num_peers == 0)
|
|
return;
|
|
|
|
// first get all unchoked and interested peers
|
|
PeerPtrList peers,others;
|
|
for (Uint32 i = 0;i < num_peers;i++)
|
|
{
|
|
Peer* p = pman.getPeer(i);
|
|
if (!p)
|
|
continue;
|
|
|
|
if (!p->isSeeder())
|
|
{
|
|
if (!p->isChoked() && p->isInterested())
|
|
peers.append(p);
|
|
else
|
|
others.append(p);
|
|
}
|
|
else
|
|
{
|
|
p->choke();
|
|
}
|
|
}
|
|
|
|
// sort them
|
|
peers.setCompareFunc(NChokeCmp);
|
|
peers.sort();
|
|
others.setCompareFunc(NChokeCmp);
|
|
others.sort();
|
|
|
|
// first round so take the 4 first peers
|
|
if (round_state == 1)
|
|
{
|
|
Uint32 num_unchoked = 0;
|
|
for (Uint32 i = 0;i < peers.count();i++)
|
|
{
|
|
Peer* p = peers.at(i);
|
|
if (!p)
|
|
continue;
|
|
|
|
if (num_unchoked < 4)
|
|
{
|
|
p->getPacketWriter().sendUnchoke();
|
|
num_unchoked++;
|
|
}
|
|
else
|
|
p->choke();
|
|
}
|
|
// go over the other peers and unchoke, if we do not have enough
|
|
for (Uint32 i = 0;i < others.count();i++)
|
|
{
|
|
Peer* p = others.at(i);
|
|
if (!p)
|
|
continue;
|
|
|
|
if (num_unchoked < 4)
|
|
{
|
|
p->getPacketWriter().sendUnchoke();
|
|
num_unchoked++;
|
|
}
|
|
else
|
|
p->choke();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Uint32 rnd = 0;
|
|
if (peers.count() > 3)
|
|
rnd = 3 + rand() % (peers.count() - 3);
|
|
|
|
Uint32 num_unchoked = 0;
|
|
// take the first 3 and a random one
|
|
for (Uint32 i = 0;i < peers.count();i++)
|
|
{
|
|
Peer* p = peers.at(i);
|
|
if (!p)
|
|
continue;
|
|
|
|
if (num_unchoked < 4 || i == rnd)
|
|
{
|
|
p->getPacketWriter().sendUnchoke();
|
|
num_unchoked++;
|
|
}
|
|
else
|
|
p->choke();
|
|
}
|
|
|
|
// go over the other peers and unchoke, if we do not have enough
|
|
for (Uint32 i = 0;i < others.count();i++)
|
|
{
|
|
Peer* p = others.at(i);
|
|
if (!p)
|
|
continue;
|
|
|
|
if (num_unchoked < 4 || i == rnd)
|
|
{
|
|
p->getPacketWriter().sendUnchoke();
|
|
num_unchoked++;
|
|
}
|
|
else
|
|
p->choke();
|
|
}
|
|
}
|
|
|
|
round_state++;
|
|
if (round_state > 3)
|
|
round_state = 1;
|
|
}
|
|
|
|
|
|
|
|
}
|
|
#endif
|
|
|