diff --git a/include/libtorrent/peer_connection.hpp b/include/libtorrent/peer_connection.hpp index 709560962..3335f5e97 100755 --- a/include/libtorrent/peer_connection.hpp +++ b/include/libtorrent/peer_connection.hpp @@ -242,6 +242,9 @@ namespace libtorrent std::vector const& allowed_fast(); std::vector const& suggested_pieces() const { return m_suggested_pieces; } + ptime connected_time() const { return m_connect; } + ptime last_received() const { return m_last_receive; } + void timed_out(); // this will cause this peer_connection to be disconnected. void disconnect(char const* message); diff --git a/include/libtorrent/policy.hpp b/include/libtorrent/policy.hpp index de0119de0..e95793826 100755 --- a/include/libtorrent/policy.hpp +++ b/include/libtorrent/policy.hpp @@ -248,7 +248,6 @@ namespace libtorrent const_iterator end_peer() const { return m_peers.end(); } bool connect_one_peer(); - bool disconnect_one_peer(); bool has_peer(policy::peer const* p) const; @@ -267,7 +266,6 @@ namespace libtorrent bool compare_peer(policy::peer const& lhs, policy::peer const& rhs , address const& external_ip) const; - iterator find_disconnect_candidate(); iterator find_connect_candidate(); bool is_connect_candidate(peer const& p, bool finished); diff --git a/include/libtorrent/session_settings.hpp b/include/libtorrent/session_settings.hpp index 069eea08d..744b299d7 100644 --- a/include/libtorrent/session_settings.hpp +++ b/include/libtorrent/session_settings.hpp @@ -102,11 +102,7 @@ namespace libtorrent , min_reconnect_time(60) , peer_connect_timeout(7) , ignore_limits_on_local_network(true) -#if !defined NDEBUG && !defined TORRENT_DISABLE_INVARIANT_CHECKS - , connection_speed(2) -#else , connection_speed(20) -#endif , send_redundant_have(false) , lazy_bitfields(true) , inactivity_timeout(600) @@ -134,6 +130,9 @@ namespace libtorrent , share_ratio_limit(2.f) , seed_time_ratio_limit(7.f) , seed_time_limit(24 * 60 * 60) // 24 hours + , peer_turnover(1 / 50.f) + , peer_turnover_cutoff(1.f) + , close_redundant_connections(true) {} // this is the user agent that will be sent to the tracker @@ -376,6 +375,23 @@ namespace libtorrent float share_ratio_limit; float seed_time_ratio_limit; int seed_time_limit; + + // the percentage of peers to disconnect every + // 90 seconds (if we're at the peer limit) + // defaults to 1/50:th + float peer_turnover; + + // when we are connected to more than + // limit * peer_turnover_enable peers + // disconnect peer_turnover fraction + // of the peers + float peer_turnover_cutoff; + + // if this is true (default) connections where both + // ends have no utility in keeping the connection open + // are closed. for instance if both ends have completed + // their downloads + bool close_redundant_connections; }; #ifndef TORRENT_DISABLE_DHT diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index b137141e5..b30d6050e 100755 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -452,6 +452,7 @@ namespace libtorrent void announce_piece(int index); void disconnect_all(); + int disconnect_peers(int num); // this is called wheh the torrent has completed // the download. It will post an event, disconnect diff --git a/src/policy.cpp b/src/policy.cpp index 74600f151..c87eb4056 100755 --- a/src/policy.cpp +++ b/src/policy.cpp @@ -421,49 +421,6 @@ namespace libtorrent return true; } - policy::iterator policy::find_disconnect_candidate() - { - INVARIANT_CHECK; - - iterator disconnect_peer = m_peers.end(); - double slowest_transfer_rate = (std::numeric_limits::max)(); - - ptime now = time_now(); - - for (iterator i = m_peers.begin(); - i != m_peers.end(); ++i) - { - peer_connection* c = i->second.connection; - 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->second.connection->is_interesting()) - continue; - - double transferred_amount - = (double)c->statistics().total_payload_download(); - - time_duration connected_time = now - i->second.connected; - - double connected_time_in_seconds = total_seconds(connected_time); - - double transfer_rate - = transferred_amount / (connected_time_in_seconds + 1); - - // prefer to disconnect uninteresting peers, and secondly slow peers - if (transfer_rate <= slowest_transfer_rate) - { - slowest_transfer_rate = transfer_rate; - disconnect_peer = i; - } - } - return disconnect_peer; - } - policy::iterator policy::find_connect_candidate() { // too expensive @@ -1074,19 +1031,6 @@ namespace libtorrent return true; } - bool policy::disconnect_one_peer() - { - iterator p = find_disconnect_candidate(); - if (p == m_peers.end()) - return false; -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING - (*p->second.connection->m_logger) << "*** CLOSING CONNECTION 'too many connections'\n"; -#endif - - p->second.connection->disconnect("too many connections, closing"); - return true; - } - // this is called whenever a peer connection is closed void policy::connection_closed(const peer_connection& c) { diff --git a/src/session_impl.cpp b/src/session_impl.cpp index 05574ca15..e3b669094 100755 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -1214,17 +1214,37 @@ namespace aux { { m_disconnect_time_scaler = 90; - // every 90 seconds, disconnect the worst peer + // every 90 seconds, disconnect the worst peers // if we have reached the connection limit - if (num_connections() >= max_connections() && !m_torrents.empty()) + if (num_connections() >= max_connections() * m_settings.peer_turnover_cutoff + && !m_torrents.empty()) { torrent_map::iterator i = std::max_element(m_torrents.begin(), m_torrents.end() , bind(&torrent::num_peers, bind(&torrent_map::value_type::second, _1)) < bind(&torrent::num_peers, bind(&torrent_map::value_type::second, _2))); TORRENT_ASSERT(i != m_torrents.end()); - // TODO: make the number of peers a percentage of the number of connected peers - i->second->get_policy().disconnect_one_peer(); + int peers_to_disconnect = (std::min)((std::max)(int(i->second->num_peers() + * m_settings.peer_turnover), 1) + , i->second->get_policy().num_connect_candidates()); + i->second->disconnect_peers(peers_to_disconnect); + } + else + { + // if we haven't reached the global max. see if any torrent + // has reached its local limit + for (torrent_map::iterator i = m_torrents.begin() + , end(m_torrents.end()); i != end; ++i) + { + boost::shared_ptr t = i->second; + if (t->num_peers() < t->max_connections() * m_settings.peer_turnover_cutoff) + continue; + + int peers_to_disconnect = (std::min)((std::max)(int(i->second->num_peers() + * m_settings.peer_turnover), 1) + , i->second->get_policy().num_connect_candidates()); + t->disconnect_peers(peers_to_disconnect); + } } } } diff --git a/src/torrent.cpp b/src/torrent.cpp index e9d2ae9e5..22dd9ccee 100755 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -2732,6 +2732,71 @@ namespace libtorrent } } + namespace + { + // this returns true if lhs is a better disconnect candidate than rhs + bool compare_disconnect_peer(peer_connection const* lhs, peer_connection const* rhs) + { + // prefer to disconnect peers we're not interested in + if (lhs->is_interesting() != rhs->is_interesting()) + return rhs->is_interesting(); + + // prefer to disconnect peers that are not seeds + if (lhs->is_seed() != rhs->is_seed()) + return rhs->is_seed(); + + // prefer to disconnect peers that are on parole + if (lhs->on_parole() != rhs->on_parole()) + return lhs->on_parole(); + + // prefer to disconnect peers that send data at a lower rate + size_type lhs_transferred = lhs->statistics().total_payload_download(); + size_type rhs_transferred = rhs->statistics().total_payload_download(); + + if (lhs_transferred != rhs_transferred + && lhs_transferred > 0 + && rhs_transferred > 0) + { + ptime now = time_now(); + size_type lhs_time_connected = total_seconds(now - lhs->connected_time()); + size_type rhs_time_connected = total_seconds(now - rhs->connected_time()); + + double lhs_rate = double(lhs_transferred) / (lhs_time_connected + 1); + double rhs_rate = double(rhs_transferred) / (rhs_time_connected + 1); + + return lhs_rate < rhs_rate; + } + + // prefer to disconnect peers that chokes us + if (lhs->is_choked() != rhs->is_choked()) + return lhs->is_choked(); + + return lhs->last_received() < rhs->last_received(); + } + } + + int torrent::disconnect_peers(int num) + { + int ret = 0; + // buils a list of all connected peers and sort it by 'disconnectability'. + std::vector peers(m_connections.size()); + std::copy(m_connections.begin(), m_connections.end(), peers.begin()); + std::sort(peers.begin(), peers.end(), boost::bind(&compare_disconnect_peer, _1, _2)); + + // never disconnect peers that connected less than 90 seconds ago + ptime cut_off = time_now() - seconds(90); + + for (std::vector::iterator i = peers.begin() + , end(peers.end()); i != end && ret < num; ++i) + { + peer_connection* p = *i; + if (p->connected_time() > cut_off) continue; + ++ret; + p->disconnect("optimistic disconnect"); + } + return ret; + } + int torrent::bandwidth_throttle(int channel) const { return m_bandwidth_limit[channel].throttle();