2003-10-23 01:00:57 +02:00
|
|
|
/*
|
|
|
|
|
|
|
|
Copyright (c) 2003, Arvid Norberg
|
|
|
|
All rights reserved.
|
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
|
|
modification, are permitted provided that the following conditions
|
|
|
|
are met:
|
|
|
|
|
|
|
|
* Redistributions of source code must retain the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer in
|
|
|
|
the documentation and/or other materials provided with the distribution.
|
|
|
|
* Neither the name of the author nor the names of its
|
|
|
|
contributors may be used to endorse or promote products derived
|
|
|
|
from this software without specific prior written permission.
|
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
|
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
2007-03-17 18:15:16 +01:00
|
|
|
#include "libtorrent/pch.hpp"
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2007-03-17 18:15:16 +01:00
|
|
|
#include <iostream>
|
2006-01-11 02:32:26 +01:00
|
|
|
|
2004-01-25 19:18:36 +01:00
|
|
|
#ifdef _MSC_VER
|
|
|
|
#pragma warning(push, 1)
|
|
|
|
#endif
|
|
|
|
|
2005-09-28 20:32:05 +02:00
|
|
|
#include <boost/bind.hpp>
|
2007-04-10 23:23:13 +02:00
|
|
|
#include <boost/utility.hpp>
|
2003-12-01 06:01:40 +01:00
|
|
|
|
2004-01-25 19:18:36 +01:00
|
|
|
#ifdef _MSC_VER
|
|
|
|
#pragma warning(pop)
|
|
|
|
#endif
|
|
|
|
|
2007-03-17 18:15:16 +01:00
|
|
|
#include "libtorrent/peer_connection.hpp"
|
2006-04-25 23:04:48 +02:00
|
|
|
#include "libtorrent/web_peer_connection.hpp"
|
2003-10-23 01:00:57 +02:00
|
|
|
#include "libtorrent/policy.hpp"
|
|
|
|
#include "libtorrent/torrent.hpp"
|
|
|
|
#include "libtorrent/socket.hpp"
|
2004-01-18 20:12:18 +01:00
|
|
|
#include "libtorrent/alert_types.hpp"
|
2005-05-12 01:03:12 +02:00
|
|
|
#include "libtorrent/invariant_check.hpp"
|
2007-04-05 00:27:36 +02:00
|
|
|
#include "libtorrent/time.hpp"
|
2006-10-11 16:02:21 +02:00
|
|
|
#include "libtorrent/aux_/session_impl.hpp"
|
2004-01-12 04:05:10 +01:00
|
|
|
|
2004-01-15 17:45:34 +01:00
|
|
|
namespace libtorrent
|
|
|
|
{
|
|
|
|
class peer_connection;
|
|
|
|
}
|
|
|
|
|
2005-09-28 20:32:05 +02:00
|
|
|
using boost::bind;
|
2004-10-14 03:17:04 +02:00
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
namespace
|
|
|
|
{
|
|
|
|
using namespace libtorrent;
|
2003-11-05 00:27:06 +01:00
|
|
|
|
2007-02-12 10:20:49 +01:00
|
|
|
size_type collect_free_download(
|
|
|
|
torrent::peer_iterator start
|
|
|
|
, torrent::peer_iterator end)
|
|
|
|
{
|
|
|
|
size_type accumulator = 0;
|
|
|
|
for (torrent::peer_iterator i = start; i != end; ++i)
|
|
|
|
{
|
|
|
|
// if the peer is interested in us, it means it may
|
|
|
|
// want to trade it's surplus uploads for downloads itself
|
|
|
|
// (and we should not consider it free). If the share diff is
|
|
|
|
// negative, there's no free download to get from this peer.
|
|
|
|
size_type diff = i->second->share_diff();
|
|
|
|
assert(diff < std::numeric_limits<size_type>::max());
|
|
|
|
if (i->second->is_peer_interested() || diff <= 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
assert(diff > 0);
|
|
|
|
i->second->add_free_upload(-diff);
|
|
|
|
accumulator += diff;
|
|
|
|
assert(accumulator > 0);
|
|
|
|
}
|
|
|
|
assert(accumulator >= 0);
|
|
|
|
return accumulator;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// returns the amount of free upload left after
|
|
|
|
// it has been distributed to the peers
|
|
|
|
size_type distribute_free_upload(
|
|
|
|
torrent::peer_iterator start
|
|
|
|
, torrent::peer_iterator end
|
|
|
|
, size_type free_upload)
|
|
|
|
{
|
|
|
|
if (free_upload <= 0) return free_upload;
|
|
|
|
int num_peers = 0;
|
|
|
|
size_type total_diff = 0;
|
|
|
|
for (torrent::peer_iterator i = start; i != end; ++i)
|
|
|
|
{
|
|
|
|
size_type d = i->second->share_diff();
|
|
|
|
assert(d < std::numeric_limits<size_type>::max());
|
|
|
|
total_diff += d;
|
|
|
|
if (!i->second->is_peer_interested() || i->second->share_diff() >= 0) continue;
|
|
|
|
++num_peers;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (num_peers == 0) return free_upload;
|
|
|
|
size_type upload_share;
|
|
|
|
if (total_diff >= 0)
|
|
|
|
{
|
|
|
|
upload_share = std::min(free_upload, total_diff) / num_peers;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
upload_share = (free_upload + total_diff) / num_peers;
|
|
|
|
}
|
|
|
|
if (upload_share < 0) return free_upload;
|
|
|
|
|
|
|
|
for (torrent::peer_iterator i = start; i != end; ++i)
|
|
|
|
{
|
|
|
|
peer_connection* p = i->second;
|
|
|
|
if (!p->is_peer_interested() || p->share_diff() >= 0) continue;
|
|
|
|
p->add_free_upload(upload_share);
|
|
|
|
free_upload -= upload_share;
|
|
|
|
}
|
|
|
|
return free_upload;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct match_peer_ip
|
|
|
|
{
|
|
|
|
match_peer_ip(tcp::endpoint const& ip)
|
|
|
|
: m_ip(ip)
|
|
|
|
{}
|
|
|
|
|
|
|
|
bool operator()(policy::peer const& p) const
|
|
|
|
{ return p.ip.address() == m_ip.address(); }
|
|
|
|
|
|
|
|
tcp::endpoint m_ip;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct match_peer_connection
|
|
|
|
{
|
|
|
|
match_peer_connection(peer_connection const& c)
|
|
|
|
: m_conn(c)
|
|
|
|
{}
|
|
|
|
|
|
|
|
bool operator()(policy::peer const& p) const
|
|
|
|
{ return p.connection == &m_conn; }
|
|
|
|
|
|
|
|
const peer_connection& m_conn;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace libtorrent
|
|
|
|
{
|
2004-02-01 11:45:54 +01:00
|
|
|
// the case where ignore_peer is motivated is if two peers
|
2004-02-01 10:47:58 +01:00
|
|
|
// have only one piece that we don't have, and it's the
|
|
|
|
// same piece for both peers. Then they might get into an
|
2005-08-15 00:04:58 +02:00
|
|
|
// infinite loop, fighting to request the same blocks.
|
2004-02-01 14:05:29 +01:00
|
|
|
void request_a_block(
|
|
|
|
torrent& t
|
|
|
|
, peer_connection& c
|
2007-02-12 10:20:49 +01:00
|
|
|
, std::vector<peer_connection*> ignore)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2006-12-04 13:20:34 +01:00
|
|
|
assert(!t.is_seed());
|
2007-02-12 10:20:49 +01:00
|
|
|
assert(!c.has_peer_choked());
|
2006-04-25 23:04:48 +02:00
|
|
|
int num_requests = c.desired_queue_size()
|
|
|
|
- (int)c.download_queue().size()
|
2005-09-27 10:07:24 +02:00
|
|
|
- (int)c.request_queue().size();
|
2003-11-05 00:27:06 +01:00
|
|
|
|
2007-02-12 06:46:29 +01:00
|
|
|
assert(c.desired_queue_size() > 0);
|
2003-11-05 00:27:06 +01:00
|
|
|
// if our request queue is already full, we
|
|
|
|
// don't have to make any new requests yet
|
|
|
|
if (num_requests <= 0) return;
|
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
piece_picker& p = t.picker();
|
|
|
|
std::vector<piece_block> interesting_pieces;
|
|
|
|
interesting_pieces.reserve(100);
|
2003-11-02 22:06:50 +01:00
|
|
|
|
2006-11-14 01:08:16 +01:00
|
|
|
bool prefer_whole_pieces = c.prefer_whole_pieces();
|
|
|
|
if (!prefer_whole_pieces)
|
|
|
|
{
|
|
|
|
prefer_whole_pieces = c.statistics().download_payload_rate()
|
|
|
|
* t.settings().whole_pieces_threshold
|
|
|
|
> t.torrent_file().piece_length();
|
|
|
|
}
|
|
|
|
|
|
|
|
// if we prefer whole pieces, the piece picker will pick at least
|
|
|
|
// the number of blocks we want, but it will try to make the picked
|
|
|
|
// blocks be from whole pieces, possibly by returning more blocks
|
|
|
|
// than we requested.
|
2007-04-25 20:26:35 +02:00
|
|
|
assert(c.remote() == c.get_socket()->remote_endpoint());
|
2006-11-14 01:08:16 +01:00
|
|
|
|
2007-04-27 02:27:37 +02:00
|
|
|
piece_picker::piece_state_t state;
|
|
|
|
peer_connection::peer_speed_t speed = c.peer_speed();
|
|
|
|
if (speed == peer_connection::fast) state = piece_picker::fast;
|
|
|
|
else if (speed == peer_connection::medium) state = piece_picker::medium;
|
|
|
|
else if (speed == peer_connection::slow) state = piece_picker::slow;
|
|
|
|
|
2003-11-02 22:06:50 +01:00
|
|
|
// picks the interesting pieces from this peer
|
|
|
|
// the integer is the number of pieces that
|
|
|
|
// should be guaranteed to be available for download
|
2004-01-12 21:31:27 +01:00
|
|
|
// (if num_requests is too big, too many pieces are
|
2003-11-02 22:06:50 +01:00
|
|
|
// picked and cpu-time is wasted)
|
2005-08-15 00:04:58 +02:00
|
|
|
// the last argument is if we should prefer whole pieces
|
|
|
|
// for this peer. If we're downloading one piece in 20 seconds
|
|
|
|
// then use this mode.
|
|
|
|
p.pick_pieces(c.get_bitfield(), interesting_pieces
|
2007-04-27 02:27:37 +02:00
|
|
|
, num_requests, prefer_whole_pieces, c.remote(), state);
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-12 21:31:27 +01:00
|
|
|
// this vector is filled with the interesting pieces
|
2003-10-23 01:00:57 +02:00
|
|
|
// that some other peer is currently downloading
|
|
|
|
// we should then compare this peer's download speed
|
|
|
|
// with the other's, to see if we should abort another
|
|
|
|
// peer_connection in favour of this one
|
|
|
|
std::vector<piece_block> busy_pieces;
|
|
|
|
busy_pieces.reserve(10);
|
|
|
|
|
|
|
|
for (std::vector<piece_block>::iterator i = interesting_pieces.begin();
|
2005-05-25 12:01:01 +02:00
|
|
|
i != interesting_pieces.end(); ++i)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
|
|
|
if (p.is_downloading(*i))
|
|
|
|
{
|
|
|
|
busy_pieces.push_back(*i);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ok, we found a piece that's not being downloaded
|
|
|
|
// by somebody else. request it from this peer
|
2004-02-01 10:47:58 +01:00
|
|
|
// and return
|
2006-04-25 23:04:48 +02:00
|
|
|
c.add_request(*i);
|
2003-11-05 00:27:06 +01:00
|
|
|
num_requests--;
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2006-05-15 00:30:05 +02:00
|
|
|
c.send_block_requests();
|
|
|
|
|
2004-02-01 10:47:58 +01:00
|
|
|
// in this case, we could not find any blocks
|
|
|
|
// that was free. If we couldn't find any busy
|
|
|
|
// blocks as well, we cannot download anything
|
|
|
|
// more from this peer.
|
|
|
|
|
2003-11-05 00:27:06 +01:00
|
|
|
if (busy_pieces.empty()) return;
|
|
|
|
|
|
|
|
// first look for blocks that are just queued
|
|
|
|
// and not actually sent to us yet
|
|
|
|
// (then we can cancel those and request them
|
|
|
|
// from this peer instead)
|
|
|
|
|
2004-02-01 18:42:20 +01:00
|
|
|
while (num_requests > 0)
|
2003-11-05 00:27:06 +01:00
|
|
|
{
|
2004-02-01 18:42:20 +01:00
|
|
|
peer_connection* peer = 0;
|
2005-08-15 00:04:58 +02:00
|
|
|
|
2006-06-17 03:29:36 +02:00
|
|
|
const int initial_queue_size = (int)c.download_queue().size()
|
|
|
|
+ (int)c.request_queue().size();
|
|
|
|
|
2005-08-15 00:04:58 +02:00
|
|
|
// This peer's weight will be the minimum, to prevent
|
|
|
|
// cancelling requests from a faster peer.
|
2006-06-17 03:29:36 +02:00
|
|
|
float min_weight = initial_queue_size == 0
|
2005-08-15 00:04:58 +02:00
|
|
|
? std::numeric_limits<float>::max()
|
2006-06-17 03:29:36 +02:00
|
|
|
: c.statistics().download_payload_rate() / initial_queue_size;
|
2005-08-15 00:04:58 +02:00
|
|
|
|
2004-02-01 18:42:20 +01:00
|
|
|
// find the peer with the lowest download
|
|
|
|
// speed that also has a piece that this
|
|
|
|
// peer could send us
|
|
|
|
for (torrent::peer_iterator i = t.begin();
|
2005-08-15 00:04:58 +02:00
|
|
|
i != t.end(); ++i)
|
2003-11-05 00:27:06 +01:00
|
|
|
{
|
2004-02-01 18:42:20 +01:00
|
|
|
// don't try to take over blocks from ourself
|
|
|
|
if (i->second == &c)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// ignore all peers in the ignore list
|
|
|
|
if (std::find(ignore.begin(), ignore.end(), i->second) != ignore.end())
|
|
|
|
continue;
|
|
|
|
|
2006-06-17 03:29:36 +02:00
|
|
|
const std::deque<piece_block>& download_queue = i->second->download_queue();
|
|
|
|
const std::deque<piece_block>& request_queue = i->second->request_queue();
|
|
|
|
const int queue_size = (int)i->second->download_queue().size()
|
|
|
|
+ (int)i->second->request_queue().size();
|
2004-02-01 18:42:20 +01:00
|
|
|
|
2006-12-15 11:42:56 +01:00
|
|
|
bool in_request_queue = std::find_first_of(
|
2006-06-17 03:29:36 +02:00
|
|
|
busy_pieces.begin()
|
|
|
|
, busy_pieces.end()
|
|
|
|
, request_queue.begin()
|
2006-12-15 11:42:56 +01:00
|
|
|
, request_queue.end()) != busy_pieces.end();
|
|
|
|
|
|
|
|
bool in_download_queue = std::find_first_of(
|
2004-02-01 18:42:20 +01:00
|
|
|
busy_pieces.begin()
|
|
|
|
, busy_pieces.end()
|
2006-06-17 03:29:36 +02:00
|
|
|
, download_queue.begin()
|
2006-12-15 11:42:56 +01:00
|
|
|
, download_queue.end()) != busy_pieces.end();
|
|
|
|
|
|
|
|
// if the block is in the request queue rather than the download queue
|
|
|
|
// (i.e. the request message hasn't been sent yet) lower the weight in
|
|
|
|
// order to prioritize it. Taking over a block in the request queue is
|
|
|
|
// free in terms of redundant download. A block that already has been
|
|
|
|
// requested is likely to be in transit already, and would in that case
|
|
|
|
// mean redundant data to receive.
|
|
|
|
const float weight = (queue_size == 0)
|
|
|
|
? std::numeric_limits<float>::max()
|
|
|
|
: i->second->statistics().download_payload_rate() / queue_size
|
|
|
|
* in_request_queue ? .1f : 1.f;
|
|
|
|
|
|
|
|
// if the peer's (i) weight is less than the lowest we've found so
|
|
|
|
// far (weight == priority) and it has blocks in its request-
|
|
|
|
// or download queue that we could request from this peer (c),
|
|
|
|
// replace the currently lowest ranking peer.
|
|
|
|
if (weight < min_weight && (in_request_queue || in_download_queue))
|
2004-02-01 18:42:20 +01:00
|
|
|
{
|
|
|
|
peer = i->second;
|
|
|
|
min_weight = weight;
|
|
|
|
}
|
2003-11-05 00:27:06 +01:00
|
|
|
}
|
|
|
|
|
2004-02-01 18:42:20 +01:00
|
|
|
if (peer == 0)
|
|
|
|
{
|
|
|
|
// we probably couldn't request the block because
|
|
|
|
// we are ignoring some peers
|
2004-02-04 12:00:29 +01:00
|
|
|
break;
|
2004-02-01 18:42:20 +01:00
|
|
|
}
|
2003-11-05 00:27:06 +01:00
|
|
|
|
2004-02-01 18:42:20 +01:00
|
|
|
// find a suitable block to take over from this peer
|
2004-02-01 01:07:20 +01:00
|
|
|
|
2004-02-01 18:42:20 +01:00
|
|
|
std::deque<piece_block>::const_reverse_iterator common_block =
|
|
|
|
std::find_first_of(
|
2006-06-17 03:29:36 +02:00
|
|
|
peer->request_queue().rbegin()
|
|
|
|
, peer->request_queue().rend()
|
|
|
|
, busy_pieces.begin()
|
|
|
|
, busy_pieces.end());
|
|
|
|
|
|
|
|
if (common_block == peer->request_queue().rend())
|
|
|
|
{
|
|
|
|
common_block = std::find_first_of(
|
2004-02-01 18:42:20 +01:00
|
|
|
peer->download_queue().rbegin()
|
|
|
|
, peer->download_queue().rend()
|
|
|
|
, busy_pieces.begin()
|
|
|
|
, busy_pieces.end());
|
2006-06-17 03:29:36 +02:00
|
|
|
assert(common_block != peer->download_queue().rend());
|
|
|
|
}
|
2004-02-01 01:07:20 +01:00
|
|
|
|
2004-02-01 18:42:20 +01:00
|
|
|
piece_block block = *common_block;
|
2003-11-05 00:27:06 +01:00
|
|
|
|
2006-06-17 03:29:36 +02:00
|
|
|
// the one we interrupted may need to request a new piece.
|
2004-02-01 18:42:20 +01:00
|
|
|
// make sure it doesn't take over a block from the peer
|
2007-02-12 06:46:29 +01:00
|
|
|
// that just took over its block (that would cause an
|
|
|
|
// infinite recursion)
|
2007-02-12 10:20:49 +01:00
|
|
|
peer->cancel_request(block);
|
|
|
|
c.add_request(block);
|
2004-02-01 18:42:20 +01:00
|
|
|
ignore.push_back(&c);
|
2007-02-12 10:20:49 +01:00
|
|
|
if (!peer->has_peer_choked() && !t.is_seed())
|
|
|
|
{
|
|
|
|
request_a_block(t, *peer, ignore);
|
|
|
|
peer->send_block_requests();
|
|
|
|
}
|
|
|
|
|
2004-02-01 18:42:20 +01:00
|
|
|
num_requests--;
|
2004-02-04 12:00:29 +01:00
|
|
|
|
2006-06-14 23:08:55 +02:00
|
|
|
const int queue_size = (int)c.download_queue().size()
|
|
|
|
+ (int)c.request_queue().size();
|
2004-02-04 12:00:29 +01:00
|
|
|
const float weight = queue_size == 0
|
|
|
|
? std::numeric_limits<float>::max()
|
2004-04-18 14:28:02 +02:00
|
|
|
: c.statistics().download_payload_rate() / queue_size;
|
2004-02-04 12:00:29 +01:00
|
|
|
|
2006-06-17 03:29:36 +02:00
|
|
|
// this peer doesn't have a faster connection than the
|
|
|
|
// slowest peer. Don't take over any blocks
|
2004-02-04 12:00:29 +01:00
|
|
|
if (weight <= min_weight) break;
|
2004-02-01 18:42:20 +01:00
|
|
|
}
|
2006-05-15 00:30:05 +02:00
|
|
|
c.send_block_requests();
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
2003-12-01 06:01:40 +01:00
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
policy::policy(torrent* t)
|
2006-07-08 21:41:39 +02:00
|
|
|
: m_torrent(t)
|
2003-12-14 06:56:12 +01:00
|
|
|
, m_num_unchoked(0)
|
2003-12-14 23:55:32 +01:00
|
|
|
, m_available_free_upload(0)
|
2007-04-05 00:27:36 +02:00
|
|
|
, m_last_optimistic_disconnect(min_time())
|
2004-01-25 23:41:55 +01:00
|
|
|
{ assert(t); }
|
2003-12-14 06:56:12 +01:00
|
|
|
// finds the peer that has the worst download rate
|
|
|
|
// and returns it. May return 0 if all peers are
|
|
|
|
// choked.
|
2007-04-12 12:21:55 +02:00
|
|
|
policy::iterator policy::find_choke_candidate()
|
2003-12-14 06:56:12 +01:00
|
|
|
{
|
2005-05-12 01:03:12 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2007-04-12 12:21:55 +02:00
|
|
|
iterator worst_peer = m_peers.end();
|
2005-04-03 17:44:17 +02:00
|
|
|
size_type min_weight = std::numeric_limits<int>::min();
|
2003-12-14 06:56:12 +01:00
|
|
|
|
2005-04-05 02:54:33 +02:00
|
|
|
#ifndef NDEBUG
|
|
|
|
int unchoked_counter = m_num_unchoked;
|
|
|
|
#endif
|
|
|
|
|
2004-01-12 21:31:27 +01:00
|
|
|
// TODO: make this selection better
|
|
|
|
|
2007-04-10 23:23:13 +02:00
|
|
|
for (iterator i = m_peers.begin();
|
2005-03-24 13:13:47 +01:00
|
|
|
i != m_peers.end(); ++i)
|
2003-12-14 06:56:12 +01:00
|
|
|
{
|
|
|
|
peer_connection* c = i->connection;
|
|
|
|
|
|
|
|
if (c == 0) continue;
|
|
|
|
if (c->is_choked()) continue;
|
2005-04-05 02:54:33 +02:00
|
|
|
#ifndef NDEBUG
|
|
|
|
unchoked_counter--;
|
|
|
|
#endif
|
2005-10-04 01:09:22 +02:00
|
|
|
if (c->is_disconnecting()) continue;
|
2003-12-14 06:56:12 +01:00
|
|
|
// if the peer isn't interested, just choke it
|
|
|
|
if (!c->is_peer_interested())
|
2007-04-12 12:21:55 +02:00
|
|
|
return i;
|
2003-12-14 06:56:12 +01:00
|
|
|
|
2004-01-25 19:18:36 +01:00
|
|
|
size_type diff = i->total_download()
|
2004-01-14 13:53:17 +01:00
|
|
|
- i->total_upload();
|
2003-12-14 06:56:12 +01:00
|
|
|
|
2004-01-25 19:18:36 +01:00
|
|
|
size_type weight = static_cast<int>(c->statistics().download_rate() * 10.f)
|
2003-12-14 06:56:12 +01:00
|
|
|
+ diff
|
2005-04-05 02:54:33 +02:00
|
|
|
+ ((c->is_interesting() && c->has_peer_choked())?-10:10)*1024;
|
2003-12-14 06:56:12 +01:00
|
|
|
|
2007-04-12 12:21:55 +02:00
|
|
|
if (weight >= min_weight && worst_peer != m_peers.end()) continue;
|
2003-12-14 06:56:12 +01:00
|
|
|
|
|
|
|
min_weight = weight;
|
2007-04-12 12:21:55 +02:00
|
|
|
worst_peer = i;
|
2003-12-14 06:56:12 +01:00
|
|
|
continue;
|
|
|
|
}
|
2005-04-05 02:54:33 +02:00
|
|
|
assert(unchoked_counter == 0);
|
2003-12-14 06:56:12 +01:00
|
|
|
return worst_peer;
|
|
|
|
}
|
|
|
|
|
2007-04-12 12:21:55 +02:00
|
|
|
policy::iterator policy::find_unchoke_candidate()
|
2003-12-14 06:56:12 +01:00
|
|
|
{
|
2005-05-12 01:03:12 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2003-12-14 06:56:12 +01:00
|
|
|
// if all of our peers are unchoked, there's
|
|
|
|
// no left to unchoke
|
|
|
|
if (m_num_unchoked == m_torrent->num_peers())
|
2007-04-12 12:21:55 +02:00
|
|
|
return m_peers.end();
|
2003-12-14 06:56:12 +01:00
|
|
|
|
2007-04-12 12:21:55 +02:00
|
|
|
iterator unchoke_peer = m_peers.end();
|
2007-04-05 00:27:36 +02:00
|
|
|
ptime min_time = libtorrent::min_time();
|
2004-01-12 21:31:27 +01:00
|
|
|
float max_down_speed = 0.f;
|
|
|
|
|
|
|
|
// TODO: make this selection better
|
2003-12-14 06:56:12 +01:00
|
|
|
|
2007-04-10 23:23:13 +02:00
|
|
|
for (iterator i = m_peers.begin();
|
2005-03-24 13:13:47 +01:00
|
|
|
i != m_peers.end(); ++i)
|
2003-12-14 06:56:12 +01:00
|
|
|
{
|
|
|
|
peer_connection* c = i->connection;
|
|
|
|
if (c == 0) continue;
|
2004-01-21 01:59:38 +01:00
|
|
|
if (c->is_disconnecting()) continue;
|
2003-12-14 06:56:12 +01:00
|
|
|
if (!c->is_choked()) continue;
|
|
|
|
if (!c->is_peer_interested()) continue;
|
2005-04-03 17:44:17 +02:00
|
|
|
if (c->share_diff() < -free_upload_amount
|
2005-03-24 13:13:47 +01:00
|
|
|
&& m_torrent->ratio() != 0) continue;
|
2004-01-12 21:31:27 +01:00
|
|
|
if (c->statistics().download_rate() < max_down_speed) continue;
|
2003-12-14 06:56:12 +01:00
|
|
|
|
|
|
|
min_time = i->last_optimistically_unchoked;
|
2004-01-12 21:31:27 +01:00
|
|
|
max_down_speed = c->statistics().download_rate();
|
2007-04-12 12:21:55 +02:00
|
|
|
unchoke_peer = i;
|
2003-12-14 06:56:12 +01:00
|
|
|
}
|
|
|
|
return unchoke_peer;
|
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2007-04-12 12:21:55 +02:00
|
|
|
policy::iterator policy::find_disconnect_candidate()
|
2004-01-21 01:59:38 +01:00
|
|
|
{
|
2007-04-13 19:47:40 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2007-04-12 12:21:55 +02:00
|
|
|
iterator disconnect_peer = m_peers.end();
|
2004-01-25 19:18:36 +01:00
|
|
|
double slowest_transfer_rate = std::numeric_limits<double>::max();
|
2004-01-21 01:59:38 +01:00
|
|
|
|
2007-04-12 12:21:55 +02:00
|
|
|
ptime now = time_now();
|
2004-01-21 01:59:38 +01:00
|
|
|
|
2007-04-10 23:23:13 +02:00
|
|
|
for (iterator i = m_peers.begin();
|
2005-08-18 22:38:03 +02:00
|
|
|
i != m_peers.end(); ++i)
|
2004-01-21 01:59:38 +01:00
|
|
|
{
|
|
|
|
peer_connection* c = i->connection;
|
2007-04-12 12:21:55 +02:00
|
|
|
if (c == 0) continue;
|
|
|
|
if (c->is_disconnecting()) continue;
|
|
|
|
|
|
|
|
// never disconnect an interesting peer if we have a candidate that
|
|
|
|
// isn't interesting
|
|
|
|
if (disconnect_peer != m_peers.end()
|
|
|
|
&& c->is_interesting()
|
|
|
|
&& !disconnect_peer->connection->is_interesting())
|
2004-01-21 01:59:38 +01:00
|
|
|
continue;
|
|
|
|
|
2004-01-25 19:18:36 +01:00
|
|
|
double transferred_amount
|
|
|
|
= (double)c->statistics().total_payload_download();
|
2004-01-21 01:59:38 +01:00
|
|
|
|
2007-04-12 12:21:55 +02:00
|
|
|
time_duration connected_time = now - i->connected;
|
2004-01-21 01:59:38 +01:00
|
|
|
|
2007-04-12 12:21:55 +02:00
|
|
|
double connected_time_in_seconds = total_seconds(connected_time);
|
2004-01-21 01:59:38 +01:00
|
|
|
|
2004-01-25 19:18:36 +01:00
|
|
|
double transfer_rate
|
2007-04-12 12:21:55 +02:00
|
|
|
= transferred_amount / (connected_time_in_seconds + 1);
|
2004-01-21 01:59:38 +01:00
|
|
|
|
2007-04-13 19:47:40 +02:00
|
|
|
// prefer to disconnect uninteresting peers, and secondly slow peers
|
|
|
|
if (transfer_rate <= slowest_transfer_rate
|
|
|
|
|| (disconnect_peer != m_peers.end()
|
|
|
|
&& disconnect_peer->connection->is_interesting()
|
|
|
|
&& !c->is_interesting()))
|
2004-01-21 01:59:38 +01:00
|
|
|
{
|
2004-01-25 19:18:36 +01:00
|
|
|
slowest_transfer_rate = transfer_rate;
|
2007-04-12 12:21:55 +02:00
|
|
|
disconnect_peer = i;
|
2004-01-21 01:59:38 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return disconnect_peer;
|
|
|
|
}
|
|
|
|
|
2007-04-12 12:21:55 +02:00
|
|
|
policy::iterator policy::find_connect_candidate()
|
2004-01-14 17:18:53 +01:00
|
|
|
{
|
2007-04-13 19:47:40 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2007-04-05 00:27:36 +02:00
|
|
|
ptime now = time_now();
|
|
|
|
ptime ptime(now);
|
2007-04-12 12:21:55 +02:00
|
|
|
iterator candidate = m_peers.end();
|
2007-04-14 23:47:07 +02:00
|
|
|
|
|
|
|
int max_failcount = m_torrent->settings().max_failcount;
|
|
|
|
int min_reconnect_time = m_torrent->settings().min_reconnect_time;
|
2004-01-14 17:18:53 +01:00
|
|
|
|
2007-04-10 23:23:13 +02:00
|
|
|
for (iterator i = m_peers.begin();
|
2005-05-12 01:03:12 +02:00
|
|
|
i != m_peers.end(); ++i)
|
2004-01-14 17:18:53 +01:00
|
|
|
{
|
2007-03-28 03:06:15 +02:00
|
|
|
if (i->connection) continue;
|
|
|
|
if (i->banned) continue;
|
|
|
|
if (i->type == peer::not_connectable) continue;
|
|
|
|
if (i->seed && m_torrent->is_seed()) continue;
|
2007-04-14 23:47:07 +02:00
|
|
|
if (i->failcount >= max_failcount) continue;
|
|
|
|
if (now - i->connected < seconds(i->failcount * min_reconnect_time))
|
|
|
|
continue;
|
2004-01-14 17:22:49 +01:00
|
|
|
|
2007-04-05 00:27:36 +02:00
|
|
|
assert(i->connected <= now);
|
2004-01-14 17:18:53 +01:00
|
|
|
|
2007-04-05 00:27:36 +02:00
|
|
|
libtorrent::ptime next_connect = i->connected;
|
2004-01-14 17:18:53 +01:00
|
|
|
|
|
|
|
if (next_connect <= ptime)
|
|
|
|
{
|
2004-01-25 19:18:36 +01:00
|
|
|
ptime = next_connect;
|
2007-04-12 12:21:55 +02:00
|
|
|
candidate = i;
|
2004-01-14 17:18:53 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-04-05 00:27:36 +02:00
|
|
|
assert(ptime <= now);
|
2004-01-14 17:18:53 +01:00
|
|
|
|
|
|
|
return candidate;
|
|
|
|
}
|
|
|
|
|
2007-04-12 12:21:55 +02:00
|
|
|
policy::iterator policy::find_seed_choke_candidate()
|
2004-03-01 22:54:10 +01:00
|
|
|
{
|
2005-05-12 01:03:12 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2005-08-18 22:38:03 +02:00
|
|
|
assert(m_num_unchoked > 0);
|
2004-05-14 01:34:42 +02:00
|
|
|
// first choice candidate.
|
|
|
|
// it is a candidate we owe nothing to and which has been unchoked
|
|
|
|
// the longest.
|
2007-04-12 12:21:55 +02:00
|
|
|
iterator candidate = m_peers.end();
|
2004-11-18 23:33:50 +01:00
|
|
|
|
|
|
|
// not valid when candidate == 0
|
2007-04-05 00:27:36 +02:00
|
|
|
ptime last_unchoke = min_time();
|
2004-03-01 22:54:10 +01:00
|
|
|
|
2004-05-14 01:34:42 +02:00
|
|
|
// second choice candidate.
|
|
|
|
// if there is no first choice candidate, this candidate will be chosen.
|
|
|
|
// it is the candidate that we owe the least to.
|
2007-04-12 12:21:55 +02:00
|
|
|
iterator second_candidate = m_peers.end();
|
2004-05-21 01:26:40 +02:00
|
|
|
size_type lowest_share_diff = 0; // not valid when secondCandidate==0
|
2004-05-14 01:34:42 +02:00
|
|
|
|
2007-04-10 23:23:13 +02:00
|
|
|
for (iterator i = m_peers.begin();
|
2005-05-12 01:03:12 +02:00
|
|
|
i != m_peers.end(); ++i)
|
2004-03-01 22:54:10 +01:00
|
|
|
{
|
|
|
|
peer_connection* c = i->connection;
|
2007-04-12 12:21:55 +02:00
|
|
|
// ignore peers that are choked or
|
|
|
|
// whose connection is closed
|
2004-03-01 22:54:10 +01:00
|
|
|
if (c == 0) continue;
|
2004-05-14 01:34:42 +02:00
|
|
|
|
2004-03-01 22:54:10 +01:00
|
|
|
if (c->is_choked()) continue;
|
2005-09-29 10:56:13 +02:00
|
|
|
if (c->is_disconnecting()) continue;
|
2004-03-12 17:42:33 +01:00
|
|
|
|
2004-11-18 23:33:50 +01:00
|
|
|
size_type share_diff = c->share_diff();
|
2004-03-12 17:42:33 +01:00
|
|
|
|
2004-05-14 01:34:42 +02:00
|
|
|
// select as second candidate the one that we owe the least
|
|
|
|
// to
|
2007-04-12 12:21:55 +02:00
|
|
|
if (second_candidate == m_peers.end()
|
|
|
|
|| share_diff <= lowest_share_diff)
|
2004-05-14 01:34:42 +02:00
|
|
|
{
|
|
|
|
lowest_share_diff = share_diff;
|
2007-04-12 12:21:55 +02:00
|
|
|
second_candidate = i;
|
2004-05-14 01:34:42 +02:00
|
|
|
}
|
|
|
|
|
2005-05-12 01:03:12 +02:00
|
|
|
// select as first candidate the one that we don't owe anything to
|
2004-05-14 01:34:42 +02:00
|
|
|
// and has been waiting for an unchoke the longest
|
|
|
|
if (share_diff > 0) continue;
|
2007-04-12 12:21:55 +02:00
|
|
|
if (candidate == m_peers.end()
|
|
|
|
|| last_unchoke > i->last_optimistically_unchoked)
|
2004-05-14 01:34:42 +02:00
|
|
|
{
|
|
|
|
last_unchoke = i->last_optimistically_unchoked;
|
2007-04-12 12:21:55 +02:00
|
|
|
candidate = i;
|
2004-05-14 01:34:42 +02:00
|
|
|
}
|
2004-03-01 22:54:10 +01:00
|
|
|
}
|
2007-04-12 12:21:55 +02:00
|
|
|
if (candidate != m_peers.end()) return candidate;
|
|
|
|
assert(second_candidate != m_peers.end());
|
|
|
|
return second_candidate;
|
2004-03-01 22:54:10 +01:00
|
|
|
}
|
|
|
|
|
2007-04-12 12:21:55 +02:00
|
|
|
policy::iterator policy::find_seed_unchoke_candidate()
|
2004-03-01 22:54:10 +01:00
|
|
|
{
|
2005-05-12 01:03:12 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2007-04-12 12:21:55 +02:00
|
|
|
iterator candidate = m_peers.end();
|
2007-04-05 00:27:36 +02:00
|
|
|
ptime last_unchoke = time_now();
|
2004-03-01 22:54:10 +01:00
|
|
|
|
2007-04-10 23:23:13 +02:00
|
|
|
for (iterator i = m_peers.begin();
|
2005-05-12 01:03:12 +02:00
|
|
|
i != m_peers.end(); ++i)
|
2004-03-01 22:54:10 +01:00
|
|
|
{
|
|
|
|
peer_connection* c = i->connection;
|
|
|
|
if (c == 0) continue;
|
|
|
|
if (!c->is_choked()) continue;
|
|
|
|
if (!c->is_peer_interested()) continue;
|
|
|
|
if (c->is_disconnecting()) continue;
|
|
|
|
if (last_unchoke < i->last_optimistically_unchoked) continue;
|
|
|
|
last_unchoke = i->last_optimistically_unchoked;
|
2007-04-12 12:21:55 +02:00
|
|
|
candidate = i;
|
2004-03-01 22:54:10 +01:00
|
|
|
}
|
|
|
|
return candidate;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool policy::seed_unchoke_one_peer()
|
|
|
|
{
|
2005-08-18 22:38:03 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2007-04-12 12:21:55 +02:00
|
|
|
iterator p = find_seed_unchoke_candidate();
|
|
|
|
if (p != m_peers.end())
|
2004-03-01 22:54:10 +01:00
|
|
|
{
|
2005-05-12 01:03:12 +02:00
|
|
|
assert(p->connection->is_choked());
|
2004-03-01 22:54:10 +01:00
|
|
|
p->connection->send_unchoke();
|
2007-04-05 00:27:36 +02:00
|
|
|
p->last_optimistically_unchoked = time_now();
|
2004-03-01 22:54:10 +01:00
|
|
|
++m_num_unchoked;
|
|
|
|
}
|
2007-04-12 12:21:55 +02:00
|
|
|
return p != m_peers.end();
|
2004-03-01 22:54:10 +01:00
|
|
|
}
|
|
|
|
|
2005-08-18 22:38:03 +02:00
|
|
|
void policy::seed_choke_one_peer()
|
|
|
|
{
|
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2007-04-12 12:21:55 +02:00
|
|
|
iterator p = find_seed_choke_candidate();
|
|
|
|
if (p != m_peers.end())
|
2005-08-18 22:38:03 +02:00
|
|
|
{
|
|
|
|
assert(!p->connection->is_choked());
|
|
|
|
p->connection->send_choke();
|
|
|
|
--m_num_unchoked;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-12-01 06:01:40 +01:00
|
|
|
void policy::pulse()
|
|
|
|
{
|
2005-05-12 01:03:12 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2004-11-30 12:17:32 +01:00
|
|
|
if (m_torrent->is_paused()) return;
|
|
|
|
|
2004-01-21 01:59:38 +01:00
|
|
|
// remove old disconnected peers from the list
|
2007-04-13 19:47:40 +02:00
|
|
|
for (iterator i = m_peers.begin(); i != m_peers.end();)
|
|
|
|
{
|
|
|
|
// this timeout has to be customizable!
|
|
|
|
if (i->connection == 0
|
|
|
|
&& i->connected != min_time()
|
|
|
|
&& time_now() - i->connected > minutes(120))
|
|
|
|
{
|
|
|
|
m_peers.erase(i++);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
}
|
2003-12-01 06:01:40 +01:00
|
|
|
|
2004-03-01 22:54:10 +01:00
|
|
|
// -------------------------------------
|
|
|
|
// maintain the number of connections
|
|
|
|
// -------------------------------------
|
|
|
|
|
|
|
|
// count the number of connected peers except for peers
|
|
|
|
// that are currently in the process of disconnecting
|
|
|
|
int num_connected_peers = 0;
|
|
|
|
|
2007-04-10 23:23:13 +02:00
|
|
|
for (iterator i = m_peers.begin();
|
2007-04-12 12:21:55 +02:00
|
|
|
i != m_peers.end(); ++i)
|
2004-01-21 01:59:38 +01:00
|
|
|
{
|
2004-03-01 22:54:10 +01:00
|
|
|
if (i->connection && !i->connection->is_disconnecting())
|
|
|
|
++num_connected_peers;
|
|
|
|
}
|
2004-01-21 01:59:38 +01:00
|
|
|
|
2004-10-29 15:21:09 +02:00
|
|
|
if (m_torrent->m_connections_quota.given != std::numeric_limits<int>::max())
|
2004-03-01 22:54:10 +01:00
|
|
|
{
|
2004-01-21 01:59:38 +01:00
|
|
|
|
2004-10-29 15:21:09 +02:00
|
|
|
int max_connections = m_torrent->m_connections_quota.given;
|
2004-01-21 01:59:38 +01:00
|
|
|
|
2004-03-01 22:54:10 +01:00
|
|
|
if (num_connected_peers >= max_connections)
|
2004-01-21 01:59:38 +01:00
|
|
|
{
|
|
|
|
// every minute, disconnect the worst peer in hope of finding a better peer
|
|
|
|
|
2007-04-05 00:27:36 +02:00
|
|
|
ptime local_time = time_now();
|
|
|
|
if (m_last_optimistic_disconnect + seconds(120) <= local_time
|
2007-04-12 12:21:55 +02:00
|
|
|
&& find_connect_candidate() != m_peers.end())
|
2004-01-21 01:59:38 +01:00
|
|
|
{
|
2004-03-01 22:54:10 +01:00
|
|
|
m_last_optimistic_disconnect = local_time;
|
2004-01-21 01:59:38 +01:00
|
|
|
--max_connections; // this will have the effect of disconnecting the worst peer
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// don't do a disconnect earlier than 1 minute after some peer was connected
|
2007-04-05 00:27:36 +02:00
|
|
|
m_last_optimistic_disconnect = time_now();
|
2004-01-21 01:59:38 +01:00
|
|
|
}
|
|
|
|
|
2004-03-01 22:54:10 +01:00
|
|
|
while (num_connected_peers > max_connections)
|
2004-01-21 01:59:38 +01:00
|
|
|
{
|
2005-03-24 13:13:47 +01:00
|
|
|
bool ret = disconnect_one_peer();
|
2005-05-31 00:50:54 +02:00
|
|
|
(void)ret;
|
2005-03-24 13:13:47 +01:00
|
|
|
assert(ret);
|
2004-01-21 01:59:38 +01:00
|
|
|
--num_connected_peers;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-10-29 15:21:09 +02:00
|
|
|
while (m_torrent->num_peers() < m_torrent->m_connections_quota.given)
|
2004-01-21 01:59:38 +01:00
|
|
|
{
|
2004-03-01 22:54:10 +01:00
|
|
|
if (!connect_one_peer())
|
2004-01-21 01:59:38 +01:00
|
|
|
break;
|
|
|
|
}
|
2004-01-14 13:53:17 +01:00
|
|
|
|
2004-03-01 22:54:10 +01:00
|
|
|
|
|
|
|
// ------------------------
|
|
|
|
// upload shift
|
|
|
|
// ------------------------
|
|
|
|
|
|
|
|
// this part will shift downloads
|
|
|
|
// from peers that are seeds and peers
|
|
|
|
// that don't want to download from us
|
|
|
|
// to peers that cannot upload anything
|
|
|
|
// to us. The shifting will make sure
|
|
|
|
// that the torrent's share ratio
|
|
|
|
// will be maintained
|
|
|
|
|
2004-01-14 02:19:30 +01:00
|
|
|
// if the share ratio is 0 (infinite)
|
|
|
|
// m_available_free_upload isn't used
|
|
|
|
// because it isn't necessary
|
|
|
|
if (m_torrent->ratio() != 0.f)
|
|
|
|
{
|
|
|
|
// accumulate all the free download we get
|
|
|
|
// and add it to the available free upload
|
|
|
|
m_available_free_upload
|
|
|
|
+= collect_free_download(
|
|
|
|
m_torrent->begin()
|
|
|
|
, m_torrent->end());
|
|
|
|
|
|
|
|
// distribute the free upload among the peers
|
|
|
|
m_available_free_upload = distribute_free_upload(
|
2003-12-14 23:55:32 +01:00
|
|
|
m_torrent->begin()
|
2004-01-14 02:19:30 +01:00
|
|
|
, m_torrent->end()
|
|
|
|
, m_available_free_upload);
|
|
|
|
}
|
2004-01-05 00:51:54 +01:00
|
|
|
|
2004-01-15 01:46:44 +01:00
|
|
|
// ------------------------
|
2004-03-01 22:54:10 +01:00
|
|
|
// seed choking policy
|
2004-01-15 01:46:44 +01:00
|
|
|
// ------------------------
|
2004-01-05 00:51:54 +01:00
|
|
|
if (m_torrent->is_seed())
|
|
|
|
{
|
2005-08-18 22:38:03 +02:00
|
|
|
if (m_num_unchoked > m_torrent->m_uploads_quota.given)
|
2004-03-01 22:54:10 +01:00
|
|
|
{
|
2005-08-18 22:38:03 +02:00
|
|
|
do
|
|
|
|
{
|
2007-04-12 12:21:55 +02:00
|
|
|
iterator p = find_seed_choke_candidate();
|
2005-08-18 22:38:03 +02:00
|
|
|
--m_num_unchoked;
|
2007-04-12 12:21:55 +02:00
|
|
|
assert(p != m_peers.end());
|
|
|
|
if (p == m_peers.end()) break;
|
2004-03-01 22:54:10 +01:00
|
|
|
|
2005-08-18 22:38:03 +02:00
|
|
|
assert(!p->connection->is_choked());
|
|
|
|
p->connection->send_choke();
|
|
|
|
} while (m_num_unchoked > m_torrent->m_uploads_quota.given);
|
|
|
|
}
|
2005-10-13 09:59:05 +02:00
|
|
|
else if (m_num_unchoked > 0)
|
2004-02-01 17:30:13 +01:00
|
|
|
{
|
2005-08-18 22:38:03 +02:00
|
|
|
// optimistic unchoke. trade the 'worst'
|
|
|
|
// unchoked peer with one of the choked
|
|
|
|
// TODO: This rotation should happen
|
|
|
|
// far less frequent than this!
|
|
|
|
assert(m_num_unchoked <= m_torrent->num_peers());
|
2007-04-12 12:21:55 +02:00
|
|
|
iterator p = find_seed_unchoke_candidate();
|
|
|
|
if (p != m_peers.end())
|
2005-08-18 22:38:03 +02:00
|
|
|
{
|
|
|
|
assert(p->connection->is_choked());
|
|
|
|
seed_choke_one_peer();
|
|
|
|
p->connection->send_unchoke();
|
|
|
|
++m_num_unchoked;
|
|
|
|
}
|
|
|
|
|
2004-02-01 17:30:13 +01:00
|
|
|
}
|
|
|
|
|
2004-01-05 00:51:54 +01:00
|
|
|
// make sure we have enough
|
|
|
|
// unchoked peers
|
2004-10-29 15:21:09 +02:00
|
|
|
while (m_num_unchoked < m_torrent->m_uploads_quota.given)
|
2004-01-05 00:51:54 +01:00
|
|
|
{
|
2004-03-01 22:54:10 +01:00
|
|
|
if (!seed_unchoke_one_peer()) break;
|
2004-01-05 00:51:54 +01:00
|
|
|
}
|
2004-05-10 08:12:29 +02:00
|
|
|
#ifndef NDEBUG
|
|
|
|
check_invariant();
|
|
|
|
#endif
|
2004-01-05 00:51:54 +01:00
|
|
|
}
|
2004-01-15 01:46:44 +01:00
|
|
|
|
2004-03-01 22:54:10 +01:00
|
|
|
// ----------------------------
|
|
|
|
// downloading choking policy
|
|
|
|
// ----------------------------
|
2004-01-14 13:53:17 +01:00
|
|
|
else
|
2003-12-01 06:01:40 +01:00
|
|
|
{
|
2004-11-18 23:33:50 +01:00
|
|
|
if (m_torrent->ratio() != 0)
|
2004-01-14 13:53:17 +01:00
|
|
|
{
|
2004-11-18 23:33:50 +01:00
|
|
|
// choke peers that have leeched too much without giving anything back
|
2007-04-10 23:23:13 +02:00
|
|
|
for (iterator i = m_peers.begin();
|
2005-03-24 13:13:47 +01:00
|
|
|
i != m_peers.end(); ++i)
|
2004-01-14 13:53:17 +01:00
|
|
|
{
|
2004-11-18 23:33:50 +01:00
|
|
|
peer_connection* c = i->connection;
|
|
|
|
if (c == 0) continue;
|
|
|
|
|
|
|
|
size_type diff = i->connection->share_diff();
|
|
|
|
if (diff < -free_upload_amount
|
|
|
|
&& !c->is_choked())
|
|
|
|
{
|
|
|
|
// if we have uploaded more than a piece for free, choke peer and
|
|
|
|
// wait until we catch up with our download.
|
|
|
|
c->send_choke();
|
|
|
|
--m_num_unchoked;
|
|
|
|
}
|
2004-01-14 13:53:17 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-10-29 15:21:09 +02:00
|
|
|
if (m_torrent->m_uploads_quota.given < m_torrent->num_peers())
|
2003-12-01 06:01:40 +01:00
|
|
|
{
|
2005-04-05 02:54:33 +02:00
|
|
|
assert(m_torrent->m_uploads_quota.given >= 0);
|
|
|
|
|
2004-03-01 01:50:00 +01:00
|
|
|
// make sure we don't have too many
|
|
|
|
// unchoked peers
|
2005-08-18 22:38:03 +02:00
|
|
|
if (m_num_unchoked > m_torrent->m_uploads_quota.given)
|
2004-03-01 01:50:00 +01:00
|
|
|
{
|
2005-08-18 22:38:03 +02:00
|
|
|
do
|
|
|
|
{
|
2007-04-12 12:21:55 +02:00
|
|
|
iterator p = find_choke_candidate();
|
|
|
|
if (p == m_peers.end()) break;
|
|
|
|
assert(p != m_peers.end());
|
2005-08-18 22:38:03 +02:00
|
|
|
assert(!p->connection->is_choked());
|
|
|
|
p->connection->send_choke();
|
|
|
|
--m_num_unchoked;
|
|
|
|
} while (m_num_unchoked > m_torrent->m_uploads_quota.given);
|
2004-03-01 01:50:00 +01:00
|
|
|
}
|
2005-08-18 22:38:03 +02:00
|
|
|
else
|
2004-03-01 01:50:00 +01:00
|
|
|
{
|
2005-08-18 22:38:03 +02:00
|
|
|
// optimistic unchoke. trade the 'worst'
|
|
|
|
// unchoked peer with one of the choked
|
|
|
|
// TODO: This rotation should happen
|
|
|
|
// far less frequent than this!
|
|
|
|
assert(m_num_unchoked <= m_torrent->num_peers());
|
2007-04-12 12:21:55 +02:00
|
|
|
iterator p = find_unchoke_candidate();
|
|
|
|
if (p != m_peers.end())
|
2005-08-18 22:38:03 +02:00
|
|
|
{
|
|
|
|
assert(p->connection->is_choked());
|
|
|
|
choke_one_peer();
|
|
|
|
p->connection->send_unchoke();
|
|
|
|
++m_num_unchoked;
|
|
|
|
}
|
2004-03-01 01:50:00 +01:00
|
|
|
}
|
2003-12-14 06:56:12 +01:00
|
|
|
}
|
2003-12-01 22:27:27 +01:00
|
|
|
|
2003-12-14 06:56:12 +01:00
|
|
|
// make sure we have enough
|
|
|
|
// unchoked peers
|
2004-10-29 15:21:09 +02:00
|
|
|
while (m_num_unchoked < m_torrent->m_uploads_quota.given
|
|
|
|
&& unchoke_one_peer());
|
2003-12-14 06:56:12 +01:00
|
|
|
}
|
2003-12-01 06:01:40 +01:00
|
|
|
}
|
|
|
|
|
2004-01-12 21:31:27 +01:00
|
|
|
void policy::new_connection(peer_connection& c)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2004-01-15 02:29:43 +01:00
|
|
|
assert(!c.is_local());
|
2006-11-14 16:53:38 +01:00
|
|
|
|
2005-09-28 18:12:47 +02:00
|
|
|
INVARIANT_CHECK;
|
2006-11-14 16:53:38 +01:00
|
|
|
|
2004-03-01 01:50:00 +01:00
|
|
|
// if the connection comes from the tracker,
|
|
|
|
// it's probably just a NAT-check. Ignore the
|
|
|
|
// num connections constraint then.
|
2004-09-16 03:14:16 +02:00
|
|
|
|
2004-03-28 19:45:37 +02:00
|
|
|
// TODO: only allow _one_ connection to use this
|
|
|
|
// override at a time
|
2007-04-25 20:26:35 +02:00
|
|
|
assert(c.remote() == c.get_socket()->remote_endpoint());
|
2007-02-12 06:46:29 +01:00
|
|
|
|
2004-10-29 15:21:09 +02:00
|
|
|
if (m_torrent->num_peers() >= m_torrent->m_connections_quota.given
|
2006-04-25 23:04:48 +02:00
|
|
|
&& c.remote().address() != m_torrent->current_tracker().address())
|
2004-03-01 01:50:00 +01:00
|
|
|
{
|
2004-01-21 01:59:38 +01:00
|
|
|
throw protocol_error("too many connections, refusing incoming connection"); // cause a disconnect
|
2004-03-01 01:50:00 +01:00
|
|
|
}
|
|
|
|
|
2005-07-06 15:18:10 +02:00
|
|
|
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
2006-04-25 23:04:48 +02:00
|
|
|
if (c.remote().address() == m_torrent->current_tracker().address())
|
2004-03-01 01:50:00 +01:00
|
|
|
{
|
|
|
|
m_torrent->debug_log("overriding connection limit for tracker NAT-check");
|
|
|
|
}
|
|
|
|
#endif
|
2004-01-21 01:59:38 +01:00
|
|
|
|
2007-04-10 23:23:13 +02:00
|
|
|
iterator i;
|
2004-01-15 02:29:43 +01:00
|
|
|
|
2006-11-14 16:53:38 +01:00
|
|
|
if (m_torrent->settings().allow_multiple_connections_per_ip)
|
|
|
|
{
|
2006-11-30 12:56:19 +01:00
|
|
|
i = m_peers.end();
|
2006-11-14 16:53:38 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
i = std::find_if(
|
|
|
|
m_peers.begin()
|
|
|
|
, m_peers.end()
|
|
|
|
, match_peer_ip(c.remote()));
|
|
|
|
}
|
2006-09-28 15:27:34 +02:00
|
|
|
|
|
|
|
if (i != m_peers.end())
|
|
|
|
{
|
|
|
|
if (i->banned)
|
|
|
|
throw protocol_error("ip address banned, closing");
|
|
|
|
|
|
|
|
if (i->connection != 0)
|
|
|
|
{
|
2006-11-30 12:56:19 +01:00
|
|
|
assert(i->connection != &c);
|
2006-09-28 15:27:34 +02:00
|
|
|
// the new connection is a local (outgoing) connection
|
|
|
|
// or the current one is already connected
|
|
|
|
if (!i->connection->is_connecting() || c.is_local())
|
|
|
|
{
|
|
|
|
throw protocol_error("duplicate connection, closing");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
|
|
|
m_torrent->debug_log("duplicate connection. existing connection"
|
|
|
|
" is connecting and this connection is incoming. closing existing "
|
|
|
|
"connection in favour of this one");
|
|
|
|
#endif
|
|
|
|
i->connection->disconnect();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2003-12-01 06:01:40 +01:00
|
|
|
{
|
|
|
|
// we don't have ny info about this peer.
|
|
|
|
// add a new entry
|
2007-04-25 20:26:35 +02:00
|
|
|
assert(c.remote() == c.get_socket()->remote_endpoint());
|
2007-02-12 06:46:29 +01:00
|
|
|
|
2007-04-10 23:23:13 +02:00
|
|
|
peer p(c.remote(), peer::not_connectable, 0);
|
2003-12-01 06:01:40 +01:00
|
|
|
m_peers.push_back(p);
|
2007-04-10 23:23:13 +02:00
|
|
|
i = boost::prior(m_peers.end());
|
2003-12-01 06:01:40 +01:00
|
|
|
}
|
|
|
|
|
2007-04-10 23:23:13 +02:00
|
|
|
c.set_peer_info(&*i);
|
2004-01-15 17:45:34 +01:00
|
|
|
assert(i->connection == 0);
|
2004-02-25 00:55:42 +01:00
|
|
|
c.add_stat(i->prev_amount_download, i->prev_amount_upload);
|
|
|
|
i->prev_amount_download = 0;
|
|
|
|
i->prev_amount_upload = 0;
|
2003-12-14 06:56:12 +01:00
|
|
|
i->connection = &c;
|
2005-11-02 17:28:39 +01:00
|
|
|
assert(i->connection);
|
2007-04-05 00:27:36 +02:00
|
|
|
i->connected = time_now();
|
|
|
|
m_last_optimistic_disconnect = time_now();
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2007-03-28 03:06:15 +02:00
|
|
|
void policy::peer_from_tracker(const tcp::endpoint& remote, const peer_id& pid
|
2007-04-10 23:23:13 +02:00
|
|
|
, int src, char flags)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2005-09-28 18:12:47 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2004-01-16 17:36:09 +01:00
|
|
|
// just ignore the obviously invalid entries from the tracker
|
2006-09-01 05:06:00 +02:00
|
|
|
if(remote.address() == address() || remote.port() == 0)
|
2004-01-16 17:19:27 +01:00
|
|
|
return;
|
|
|
|
|
2003-10-27 17:06:00 +01:00
|
|
|
try
|
|
|
|
{
|
2007-04-10 23:23:13 +02:00
|
|
|
iterator i;
|
2006-11-15 22:39:58 +01:00
|
|
|
|
|
|
|
if (m_torrent->settings().allow_multiple_connections_per_ip)
|
|
|
|
{
|
2006-11-30 12:56:19 +01:00
|
|
|
i = m_peers.end();
|
2006-11-15 22:39:58 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
i = std::find_if(
|
|
|
|
m_peers.begin()
|
|
|
|
, m_peers.end()
|
|
|
|
, match_peer_ip(remote));
|
|
|
|
}
|
2004-01-15 02:29:43 +01:00
|
|
|
|
2003-12-01 06:01:40 +01:00
|
|
|
if (i == m_peers.end())
|
|
|
|
{
|
2004-05-10 08:12:29 +02:00
|
|
|
// we don't have any info about this peer.
|
2003-12-01 06:01:40 +01:00
|
|
|
// add a new entry
|
2007-04-10 23:23:13 +02:00
|
|
|
peer p(remote, peer::connectable, src);
|
2003-12-01 06:01:40 +01:00
|
|
|
m_peers.push_back(p);
|
2004-01-15 17:45:34 +01:00
|
|
|
// the iterator is invalid
|
|
|
|
// because of the push_back()
|
2007-04-10 23:23:13 +02:00
|
|
|
i = boost::prior(m_peers.end());
|
2007-03-28 03:06:15 +02:00
|
|
|
if (flags & 0x02) p.seed = true;
|
2007-04-18 01:06:00 +02:00
|
|
|
|
|
|
|
// try to send a DHT ping to this peer
|
|
|
|
// as well, to figure out if it supports
|
|
|
|
// DHT (uTorrent and BitComet doesn't
|
|
|
|
// advertise support)
|
|
|
|
#ifndef TORRENT_DISABLE_DHT
|
|
|
|
udp::endpoint node(remote.address(), remote.port());
|
|
|
|
m_torrent->session().add_dht_node(node);
|
|
|
|
#endif
|
2003-12-01 06:01:40 +01:00
|
|
|
}
|
2004-01-15 02:29:43 +01:00
|
|
|
else
|
2003-12-01 06:01:40 +01:00
|
|
|
{
|
2004-01-15 20:32:03 +01:00
|
|
|
i->type = peer::connectable;
|
2004-01-15 17:45:34 +01:00
|
|
|
|
2004-01-15 02:29:43 +01:00
|
|
|
// in case we got the ip from a remote connection, port is
|
|
|
|
// not known, so save it. Client may also have changed port
|
|
|
|
// for some reason.
|
2006-04-25 23:04:48 +02:00
|
|
|
i->ip = remote;
|
2007-04-10 23:23:13 +02:00
|
|
|
i->source |= src;
|
2007-04-14 23:47:07 +02:00
|
|
|
|
|
|
|
// if this peer has failed before, decrease the
|
|
|
|
// counter to allow it another try, since somebody
|
|
|
|
// else is appearantly able to connect to it
|
|
|
|
// if it comes from the DHT it might be stale though
|
|
|
|
if (i->failcount > 0 && src != peer_info::dht)
|
|
|
|
--i->failcount;
|
2007-04-10 23:23:13 +02:00
|
|
|
|
2007-03-28 03:06:15 +02:00
|
|
|
if (flags & 0x02) i->seed = true;
|
2004-01-15 02:29:43 +01:00
|
|
|
|
|
|
|
if (i->connection)
|
|
|
|
{
|
|
|
|
// this means we're already connected
|
|
|
|
// to this peer. don't connect to
|
|
|
|
// it again.
|
2005-09-18 12:18:23 +02:00
|
|
|
|
|
|
|
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
2006-04-25 23:04:48 +02:00
|
|
|
m_torrent->debug_log("already connected to peer: " + remote.address().to_string() + ":"
|
2006-11-15 22:39:58 +01:00
|
|
|
+ boost::lexical_cast<std::string>(remote.port()) + " "
|
|
|
|
+ boost::lexical_cast<std::string>(i->connection->pid()));
|
2005-09-18 12:18:23 +02:00
|
|
|
#endif
|
|
|
|
|
2006-04-25 23:04:48 +02:00
|
|
|
assert(i->connection->associated_torrent().lock().get() == m_torrent);
|
2004-01-15 02:29:43 +01:00
|
|
|
return;
|
|
|
|
}
|
2003-12-01 06:01:40 +01:00
|
|
|
}
|
2003-10-27 17:06:00 +01:00
|
|
|
}
|
2006-04-25 23:04:48 +02:00
|
|
|
catch(std::exception& e)
|
2004-01-12 04:05:10 +01:00
|
|
|
{
|
|
|
|
if (m_torrent->alerts().should_post(alert::debug))
|
|
|
|
{
|
|
|
|
m_torrent->alerts().post_alert(
|
2006-04-25 23:04:48 +02:00
|
|
|
peer_error_alert(remote, pid, e.what()));
|
2004-01-12 04:05:10 +01:00
|
|
|
}
|
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// this is called when we are choked by a peer
|
|
|
|
// i.e. a peer lets us know that we will not receive
|
|
|
|
// anything for a while
|
2005-04-24 02:50:52 +02:00
|
|
|
void policy::choked(peer_connection&)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2003-12-14 06:56:12 +01:00
|
|
|
void policy::piece_finished(int index, bool successfully_verified)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2005-09-28 18:12:47 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2004-01-25 23:41:55 +01:00
|
|
|
assert(index >= 0 && index < m_torrent->torrent_file().num_pieces());
|
|
|
|
|
2003-12-17 04:40:13 +01:00
|
|
|
if (successfully_verified)
|
|
|
|
{
|
|
|
|
// have all peers update their interested-flag
|
2007-04-10 23:23:13 +02:00
|
|
|
for (iterator i = m_peers.begin();
|
2005-08-18 22:38:03 +02:00
|
|
|
i != m_peers.end(); ++i)
|
2003-12-17 04:40:13 +01:00
|
|
|
{
|
|
|
|
if (i->connection == 0) continue;
|
|
|
|
// if we're not interested, we will not become interested
|
|
|
|
if (!i->connection->is_interesting()) continue;
|
2003-12-17 17:37:20 +01:00
|
|
|
if (!i->connection->has_piece(index)) continue;
|
2003-12-17 04:40:13 +01:00
|
|
|
|
2007-03-21 03:09:50 +01:00
|
|
|
i->connection->update_interest();
|
2003-12-17 04:40:13 +01:00
|
|
|
}
|
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2003-12-17 04:40:13 +01:00
|
|
|
// TODO: we must be able to get interested
|
|
|
|
// in a peer again, if a piece fails that
|
|
|
|
// this peer has.
|
2005-04-24 02:50:52 +02:00
|
|
|
void policy::block_finished(peer_connection& c, piece_block)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2005-09-28 18:12:47 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2003-12-01 06:01:40 +01:00
|
|
|
// if the peer hasn't choked us, ask for another piece
|
2006-12-31 15:48:18 +01:00
|
|
|
if (!c.has_peer_choked() && !m_torrent->is_seed())
|
2003-12-17 04:40:13 +01:00
|
|
|
request_a_block(*m_torrent, c);
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// this is called when we are unchoked by a peer
|
|
|
|
// i.e. a peer lets us know that we will receive
|
|
|
|
// data from now on
|
|
|
|
void policy::unchoked(peer_connection& c)
|
|
|
|
{
|
2005-09-28 18:12:47 +02:00
|
|
|
INVARIANT_CHECK;
|
2003-12-01 06:01:40 +01:00
|
|
|
if (c.is_interesting())
|
|
|
|
{
|
|
|
|
request_a_block(*m_torrent, c);
|
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2003-12-14 06:56:12 +01:00
|
|
|
// called when a peer is interested in us
|
2005-08-05 04:43:44 +02:00
|
|
|
void policy::interested(peer_connection& c)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2005-09-28 18:12:47 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2005-09-28 20:32:05 +02:00
|
|
|
assert(std::find_if(m_peers.begin(), m_peers.end()
|
2006-01-10 01:38:52 +01:00
|
|
|
, boost::bind<bool>(std::equal_to<peer_connection*>(), bind(&peer::connection, _1)
|
|
|
|
, &c)) != m_peers.end());
|
2005-09-28 20:32:05 +02:00
|
|
|
|
2005-08-05 04:43:44 +02:00
|
|
|
// if the peer is choked and we have upload slots left,
|
|
|
|
// then unchoke it. Another condition that has to be met
|
|
|
|
// is that the torrent doesn't keep track of the individual
|
|
|
|
// up/down ratio for each peer (ratio == 0) or (if it does
|
|
|
|
// keep track) this particular connection isn't a leecher.
|
|
|
|
// If the peer was choked because it was leeching, don't
|
|
|
|
// unchoke it again.
|
|
|
|
// The exception to this last condition is if we're a seed.
|
|
|
|
// In that case we don't care if people are leeching, they
|
|
|
|
// can't pay for their downloads anyway.
|
|
|
|
if (c.is_choked()
|
|
|
|
&& m_num_unchoked < m_torrent->m_uploads_quota.given
|
2005-09-28 20:07:00 +02:00
|
|
|
&& (m_torrent->ratio() == 0
|
|
|
|
|| c.share_diff() >= -free_upload_amount
|
2005-08-05 04:43:44 +02:00
|
|
|
|| m_torrent->is_seed()))
|
|
|
|
{
|
|
|
|
c.send_unchoke();
|
|
|
|
++m_num_unchoked;
|
|
|
|
}
|
2003-12-14 23:55:32 +01:00
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2003-12-14 06:56:12 +01:00
|
|
|
// called when a peer is no longer interested in us
|
2003-10-23 01:00:57 +02:00
|
|
|
void policy::not_interested(peer_connection& c)
|
|
|
|
{
|
2005-09-28 18:12:47 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2004-01-17 21:04:19 +01:00
|
|
|
if (m_torrent->ratio() != 0.f)
|
|
|
|
{
|
2004-11-18 23:33:50 +01:00
|
|
|
assert(c.share_diff() < std::numeric_limits<size_type>::max());
|
2004-01-25 19:18:36 +01:00
|
|
|
size_type diff = c.share_diff();
|
2004-01-17 21:04:19 +01:00
|
|
|
if (diff > 0 && c.is_seed())
|
|
|
|
{
|
|
|
|
// the peer is a seed and has sent
|
|
|
|
// us more than we have sent it back.
|
|
|
|
// consider the download as free download
|
|
|
|
m_available_free_upload += diff;
|
|
|
|
c.add_free_upload(-diff);
|
|
|
|
}
|
|
|
|
}
|
2004-03-21 03:03:37 +01:00
|
|
|
if (!c.is_choked())
|
|
|
|
{
|
|
|
|
c.send_choke();
|
|
|
|
--m_num_unchoked;
|
2006-06-12 01:24:36 +02:00
|
|
|
|
|
|
|
if (m_torrent->is_seed()) seed_unchoke_one_peer();
|
|
|
|
else unchoke_one_peer();
|
2004-03-21 03:03:37 +01:00
|
|
|
}
|
2003-12-14 06:56:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool policy::unchoke_one_peer()
|
|
|
|
{
|
2007-04-13 19:47:40 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2007-04-12 12:21:55 +02:00
|
|
|
iterator p = find_unchoke_candidate();
|
|
|
|
if (p == m_peers.end()) return false;
|
2004-01-21 01:59:38 +01:00
|
|
|
assert(p->connection);
|
|
|
|
assert(!p->connection->is_disconnecting());
|
2003-12-14 06:56:12 +01:00
|
|
|
|
2005-05-12 01:03:12 +02:00
|
|
|
assert(p->connection->is_choked());
|
2004-01-12 21:31:27 +01:00
|
|
|
p->connection->send_unchoke();
|
2007-04-05 00:27:36 +02:00
|
|
|
p->last_optimistically_unchoked = time_now();
|
2003-12-14 06:56:12 +01:00
|
|
|
++m_num_unchoked;
|
|
|
|
return true;
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2005-08-18 22:38:03 +02:00
|
|
|
void policy::choke_one_peer()
|
|
|
|
{
|
2007-04-13 19:47:40 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2007-04-12 12:21:55 +02:00
|
|
|
iterator p = find_choke_candidate();
|
|
|
|
if (p == m_peers.end()) return;
|
2005-08-18 22:38:03 +02:00
|
|
|
assert(p->connection);
|
|
|
|
assert(!p->connection->is_disconnecting());
|
|
|
|
assert(!p->connection->is_choked());
|
|
|
|
p->connection->send_choke();
|
|
|
|
--m_num_unchoked;
|
|
|
|
}
|
|
|
|
|
2004-01-14 17:18:53 +01:00
|
|
|
bool policy::connect_one_peer()
|
|
|
|
{
|
2007-04-13 19:47:40 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2004-10-29 15:21:09 +02:00
|
|
|
if(m_torrent->num_peers() >= m_torrent->m_connections_quota.given)
|
2004-01-21 01:59:38 +01:00
|
|
|
return false;
|
2007-04-12 12:21:55 +02:00
|
|
|
|
|
|
|
bool succeed = false;
|
|
|
|
while (!succeed)
|
|
|
|
{
|
|
|
|
iterator p = find_connect_candidate();
|
|
|
|
if (p == m_peers.end()) return false;
|
|
|
|
assert(!p->banned);
|
|
|
|
assert(!p->connection);
|
|
|
|
assert(p->type == peer::connectable);
|
|
|
|
succeed = connect_peer(p);
|
|
|
|
}
|
|
|
|
return true;
|
2004-02-25 14:18:41 +01:00
|
|
|
}
|
|
|
|
|
2007-04-12 12:21:55 +02:00
|
|
|
bool policy::connect_peer(iterator p)
|
2004-02-25 14:18:41 +01:00
|
|
|
{
|
2006-01-06 21:20:20 +01:00
|
|
|
INVARIANT_CHECK;
|
2004-02-26 19:55:10 +01:00
|
|
|
try
|
|
|
|
{
|
2005-11-02 17:28:39 +01:00
|
|
|
assert(!p->connection);
|
2007-04-12 12:21:55 +02:00
|
|
|
p->connection = &m_torrent->connect_to_peer(&*p);
|
2005-11-02 17:28:39 +01:00
|
|
|
assert(p->connection);
|
2004-02-26 19:55:10 +01:00
|
|
|
p->connection->add_stat(p->prev_amount_download, p->prev_amount_upload);
|
|
|
|
p->prev_amount_download = 0;
|
|
|
|
p->prev_amount_upload = 0;
|
|
|
|
p->connected =
|
|
|
|
m_last_optimistic_disconnect =
|
2007-04-05 00:27:36 +02:00
|
|
|
time_now();
|
2004-02-26 19:55:10 +01:00
|
|
|
return true;
|
|
|
|
}
|
2006-04-25 23:04:48 +02:00
|
|
|
catch (std::exception& e)
|
2007-04-12 12:21:55 +02:00
|
|
|
{
|
|
|
|
++p->failcount;
|
|
|
|
}
|
2004-02-26 19:55:10 +01:00
|
|
|
return false;
|
2004-01-14 17:18:53 +01:00
|
|
|
}
|
|
|
|
|
2004-01-21 01:59:38 +01:00
|
|
|
bool policy::disconnect_one_peer()
|
|
|
|
{
|
2007-04-12 12:21:55 +02:00
|
|
|
iterator p = find_disconnect_candidate();
|
|
|
|
if (p == m_peers.end())
|
2004-01-21 01:59:38 +01:00
|
|
|
return false;
|
2005-09-01 23:04:21 +02:00
|
|
|
#if defined(TORRENT_VERBOSE_LOGGING)
|
|
|
|
(*p->connection->m_logger) << "*** CLOSING CONNECTION 'too many connections'\n";
|
|
|
|
#endif
|
|
|
|
|
2004-01-21 01:59:38 +01:00
|
|
|
p->connection->disconnect();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2003-12-01 06:01:40 +01:00
|
|
|
// this is called whenever a peer connection is closed
|
2006-04-25 23:04:48 +02:00
|
|
|
void policy::connection_closed(const peer_connection& c) try
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2005-09-28 18:12:47 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2006-04-25 23:04:48 +02:00
|
|
|
// assert(c.is_disconnecting());
|
2005-05-12 01:03:12 +02:00
|
|
|
bool unchoked = false;
|
|
|
|
|
2007-04-10 23:23:13 +02:00
|
|
|
iterator i = std::find_if(
|
2004-01-15 17:45:34 +01:00
|
|
|
m_peers.begin()
|
|
|
|
, m_peers.end()
|
|
|
|
, match_peer_connection(c));
|
2003-12-01 06:01:40 +01:00
|
|
|
|
2004-03-21 03:03:37 +01:00
|
|
|
// if we couldn't find the connection in our list, just ignore it.
|
2004-01-15 17:45:34 +01:00
|
|
|
if (i == m_peers.end()) return;
|
|
|
|
assert(i->connection == &c);
|
2007-04-12 12:21:55 +02:00
|
|
|
i->connection = 0;
|
2003-12-01 06:01:40 +01:00
|
|
|
|
2007-04-05 00:27:36 +02:00
|
|
|
i->connected = time_now();
|
2007-04-12 12:21:55 +02:00
|
|
|
if (!c.is_choked() && !m_torrent->is_aborted())
|
2003-12-14 06:56:12 +01:00
|
|
|
{
|
2005-05-12 01:03:12 +02:00
|
|
|
unchoked = true;
|
2003-12-14 06:56:12 +01:00
|
|
|
}
|
2004-01-14 02:19:30 +01:00
|
|
|
|
2004-03-21 03:03:37 +01:00
|
|
|
if (c.failed())
|
|
|
|
{
|
2007-04-14 23:47:07 +02:00
|
|
|
++i->failcount;
|
2007-04-05 00:27:36 +02:00
|
|
|
i->connected = time_now();
|
2004-03-21 03:03:37 +01:00
|
|
|
}
|
|
|
|
|
2004-01-14 02:19:30 +01:00
|
|
|
// if the share ratio is 0 (infinite), the
|
|
|
|
// m_available_free_upload isn't used,
|
|
|
|
// because it isn't necessary.
|
|
|
|
if (m_torrent->ratio() != 0.f)
|
|
|
|
{
|
2007-04-12 12:21:55 +02:00
|
|
|
assert(c.associated_torrent().lock().get() == m_torrent);
|
|
|
|
assert(c.share_diff() < std::numeric_limits<size_type>::max());
|
|
|
|
m_available_free_upload += c.share_diff();
|
2004-01-14 02:19:30 +01:00
|
|
|
}
|
2004-02-25 14:18:41 +01:00
|
|
|
i->prev_amount_download += c.statistics().total_payload_download();
|
|
|
|
i->prev_amount_upload += c.statistics().total_payload_upload();
|
2005-05-12 01:03:12 +02:00
|
|
|
|
|
|
|
if (unchoked)
|
|
|
|
{
|
|
|
|
// if the peer that is diconnecting is unchoked
|
|
|
|
// then unchoke another peer in order to maintain
|
|
|
|
// the total number of unchoked peers
|
|
|
|
--m_num_unchoked;
|
2005-09-29 01:58:55 +02:00
|
|
|
if (m_torrent->is_seed()) seed_unchoke_one_peer();
|
|
|
|
else unchoke_one_peer();
|
2005-05-12 01:03:12 +02:00
|
|
|
}
|
2003-12-14 06:56:12 +01:00
|
|
|
}
|
2006-04-25 23:04:48 +02:00
|
|
|
catch (std::exception& e)
|
|
|
|
{
|
|
|
|
#ifndef NDEBUG
|
|
|
|
std::string err = e.what();
|
|
|
|
#endif
|
|
|
|
assert(false);
|
|
|
|
}
|
2003-12-14 06:56:12 +01:00
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
void policy::peer_is_interesting(peer_connection& c)
|
|
|
|
{
|
2005-09-28 18:12:47 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2004-01-12 21:31:27 +01:00
|
|
|
c.send_interested();
|
2003-10-23 01:00:57 +02:00
|
|
|
if (c.has_peer_choked()) return;
|
|
|
|
request_a_block(*m_torrent, c);
|
|
|
|
}
|
2003-12-01 06:01:40 +01:00
|
|
|
|
|
|
|
#ifndef NDEBUG
|
2004-01-15 02:29:43 +01:00
|
|
|
bool policy::has_connection(const peer_connection* c)
|
2003-12-01 06:01:40 +01:00
|
|
|
{
|
2007-04-13 19:47:40 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2004-01-25 23:41:55 +01:00
|
|
|
assert(c);
|
2007-04-25 20:26:35 +02:00
|
|
|
assert(c->remote() == c->get_socket()->remote_endpoint());
|
2007-02-12 06:46:29 +01:00
|
|
|
|
2004-01-15 17:45:34 +01:00
|
|
|
return std::find_if(
|
|
|
|
m_peers.begin()
|
|
|
|
, m_peers.end()
|
2006-11-14 16:53:38 +01:00
|
|
|
, match_peer_connection(*c)) != m_peers.end();
|
2003-12-01 06:01:40 +01:00
|
|
|
}
|
2003-12-14 06:56:12 +01:00
|
|
|
|
2004-05-10 08:12:29 +02:00
|
|
|
void policy::check_invariant() const
|
2003-12-14 06:56:12 +01:00
|
|
|
{
|
2005-05-12 01:14:58 +02:00
|
|
|
if (m_torrent->is_aborted()) return;
|
2003-12-14 06:56:12 +01:00
|
|
|
int actual_unchoked = 0;
|
2005-09-28 22:22:34 +02:00
|
|
|
int connected_peers = 0;
|
2005-11-02 17:28:39 +01:00
|
|
|
|
|
|
|
int total_connections = 0;
|
|
|
|
int nonempty_connections = 0;
|
|
|
|
|
|
|
|
|
2007-04-10 23:23:13 +02:00
|
|
|
for (const_iterator i = m_peers.begin();
|
2005-05-12 01:03:12 +02:00
|
|
|
i != m_peers.end(); ++i)
|
2003-12-14 06:56:12 +01:00
|
|
|
{
|
2005-11-02 17:28:39 +01:00
|
|
|
++total_connections;
|
2003-12-14 06:56:12 +01:00
|
|
|
if (!i->connection) continue;
|
2007-04-13 19:47:40 +02:00
|
|
|
assert(i->connection->peer_info_struct() == 0
|
|
|
|
|| i->connection->peer_info_struct() == &*i);
|
2005-11-02 17:28:39 +01:00
|
|
|
++nonempty_connections;
|
2005-09-29 01:58:55 +02:00
|
|
|
if (!i->connection->is_disconnecting())
|
|
|
|
++connected_peers;
|
2005-09-28 22:22:34 +02:00
|
|
|
if (!i->connection->is_choked()) ++actual_unchoked;
|
2003-12-14 06:56:12 +01:00
|
|
|
}
|
2005-05-12 01:03:12 +02:00
|
|
|
// assert(actual_unchoked <= m_torrent->m_uploads_quota.given);
|
|
|
|
assert(actual_unchoked == m_num_unchoked);
|
2005-09-28 22:22:34 +02:00
|
|
|
|
2005-09-29 01:58:55 +02:00
|
|
|
int num_torrent_peers = 0;
|
|
|
|
for (torrent::const_peer_iterator i = m_torrent->begin();
|
|
|
|
i != m_torrent->end(); ++i)
|
|
|
|
{
|
|
|
|
if (i->second->is_disconnecting()) continue;
|
2006-04-25 23:04:48 +02:00
|
|
|
// ignore web_peer_connections since they are not managed
|
|
|
|
// by the policy class
|
|
|
|
if (dynamic_cast<web_peer_connection*>(i->second)) continue;
|
2005-09-29 01:58:55 +02:00
|
|
|
++num_torrent_peers;
|
|
|
|
}
|
2005-09-28 22:22:34 +02:00
|
|
|
|
2005-10-01 17:12:10 +02:00
|
|
|
// this invariant is a bit complicated.
|
|
|
|
// the usual case should be that connected_peers
|
|
|
|
// == num_torrent_peers. But when there's an incoming
|
|
|
|
// connection, it will first be added to the policy
|
|
|
|
// and then be added to the torrent.
|
|
|
|
// When there's an outgoing connection, it will first
|
|
|
|
// be added to the torrent and then to the policy.
|
|
|
|
// that's why the two second cases are in there.
|
2005-11-02 17:28:39 +01:00
|
|
|
|
2005-09-29 01:58:55 +02:00
|
|
|
assert(connected_peers == num_torrent_peers
|
2005-10-01 17:12:10 +02:00
|
|
|
|| (connected_peers == num_torrent_peers + 1
|
|
|
|
&& connected_peers > 0)
|
|
|
|
|| (connected_peers + 1 == num_torrent_peers
|
|
|
|
&& num_torrent_peers > 0));
|
2005-09-28 22:22:34 +02:00
|
|
|
|
|
|
|
// TODO: Make sure the number of peers in m_torrent is equal
|
|
|
|
// to the number of connected peers in m_peers.
|
2003-12-14 06:56:12 +01:00
|
|
|
}
|
2003-12-01 06:01:40 +01:00
|
|
|
#endif
|
2003-12-14 06:56:12 +01:00
|
|
|
|
2007-04-10 23:23:13 +02:00
|
|
|
policy::peer::peer(const tcp::endpoint& ip_, peer::connection_type t, int src)
|
2007-03-28 21:56:53 +02:00
|
|
|
: ip(ip_)
|
2004-01-15 20:32:03 +01:00
|
|
|
, type(t)
|
2007-03-28 21:56:53 +02:00
|
|
|
, failcount(0)
|
|
|
|
, seed(false)
|
2007-04-05 00:27:36 +02:00
|
|
|
, last_optimistically_unchoked(min_time())
|
|
|
|
, connected(min_time())
|
2003-12-14 06:56:12 +01:00
|
|
|
, prev_amount_upload(0)
|
|
|
|
, prev_amount_download(0)
|
|
|
|
, banned(false)
|
2007-04-10 23:23:13 +02:00
|
|
|
, source(src)
|
2004-01-14 17:18:53 +01:00
|
|
|
, connection(0)
|
|
|
|
{
|
2007-04-05 00:27:36 +02:00
|
|
|
assert(connected < time_now());
|
2004-01-14 17:18:53 +01:00
|
|
|
}
|
2003-12-14 06:56:12 +01:00
|
|
|
|
2004-01-21 01:59:38 +01:00
|
|
|
size_type policy::peer::total_download() const
|
2003-12-14 06:56:12 +01:00
|
|
|
{
|
|
|
|
if (connection != 0)
|
2004-01-15 17:45:34 +01:00
|
|
|
{
|
2004-02-25 00:55:42 +01:00
|
|
|
assert(prev_amount_download == 0);
|
|
|
|
return connection->statistics().total_payload_download();
|
2004-01-15 17:45:34 +01:00
|
|
|
}
|
2003-12-14 06:56:12 +01:00
|
|
|
else
|
2004-01-15 17:45:34 +01:00
|
|
|
{
|
2003-12-14 06:56:12 +01:00
|
|
|
return prev_amount_download;
|
2004-01-15 17:45:34 +01:00
|
|
|
}
|
2003-12-14 06:56:12 +01:00
|
|
|
}
|
|
|
|
|
2004-01-21 01:59:38 +01:00
|
|
|
size_type policy::peer::total_upload() const
|
2003-12-14 06:56:12 +01:00
|
|
|
{
|
|
|
|
if (connection != 0)
|
2004-01-15 17:45:34 +01:00
|
|
|
{
|
2004-02-25 00:55:42 +01:00
|
|
|
assert(prev_amount_upload == 0);
|
|
|
|
return connection->statistics().total_payload_upload();
|
2004-01-15 17:45:34 +01:00
|
|
|
}
|
2003-12-14 06:56:12 +01:00
|
|
|
else
|
2004-01-15 17:45:34 +01:00
|
|
|
{
|
2003-12-14 06:56:12 +01:00
|
|
|
return prev_amount_upload;
|
2004-01-15 17:45:34 +01:00
|
|
|
}
|
2003-12-14 06:56:12 +01:00
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
2005-08-18 22:38:03 +02:00
|
|
|
|