added initial support for share-mode
This commit is contained in:
parent
a8cc326df6
commit
b6f92aa981
|
@ -1,3 +1,4 @@
|
|||
* added share-mode feature for improving share ratios
|
||||
* merged all proxy settings into a single one
|
||||
* improved SOCKS5 support by proxying hostname lookups
|
||||
* improved support for multi-homed clients
|
||||
|
|
|
@ -48,6 +48,8 @@ extensions
|
|||
* support for IPv6, including `BEP 7`_ and `BEP 24`_.
|
||||
* support for merkle hash tree torrents. This makes the size of torrent files
|
||||
scale well with the size of the content.
|
||||
* share-mode. This is a special mode torrents can be put in to optimize share
|
||||
ratio rather than downloading the torrent.
|
||||
|
||||
.. _extensions: manual.html#extensions
|
||||
.. _`http seeding`: manual.html#http-seeding
|
||||
|
@ -252,6 +254,17 @@ and hence decreasing the likelihood of slow peers blocking the completion of pie
|
|||
|
||||
The piece picker can also be set to download pieces in sequential order.
|
||||
|
||||
share mode
|
||||
----------
|
||||
|
||||
The share mode feature in libtorrent is intended for users who are only interested in
|
||||
helping out swarms, not downloading the torrents.
|
||||
|
||||
It works by predicting the demand for pieces, and only download pieces if there is enough
|
||||
demand. New pieces will only be downloaded once the share ratio has hit a certain target.
|
||||
|
||||
This feature is especially useful when combined with RSS, so that a client can be set up
|
||||
to provide additional bandwidth to an entire feed.
|
||||
|
||||
merkle hash tree torrents
|
||||
-------------------------
|
||||
|
|
|
@ -393,6 +393,7 @@ add_torrent()
|
|||
bool override_resume_data;
|
||||
bool upload_mode;
|
||||
std::vector<boost::uint8_t> const* file_priorities;
|
||||
bool share_mode;
|
||||
};
|
||||
|
||||
torrent_handle add_torrent(add_torrent_params const& params);
|
||||
|
@ -503,6 +504,22 @@ on disk I/O errors, and if the torrent is also auto managed, it will be taken ou
|
|||
of this state periodically. This mode can be used to avoid race conditions when
|
||||
adjusting priorities of pieces before allowing the torrent to start downloading.
|
||||
|
||||
``share_mode`` determines if the torrent should be added in *share mode* or not.
|
||||
Share mode indicates that we are not interested in downloading the torrent, but
|
||||
merlely want to improve our share ratio (i.e. increase it). A torrent started in
|
||||
share mode will do its best to never download more than it uploads to the swarm.
|
||||
If the swarm does not have enough demand for upload capacity, the torrent will
|
||||
not download anything. This mode is intended to be safe to add any number of torrents
|
||||
to, without manual screening, without the risk of downloading more than is uploaded.
|
||||
|
||||
A torrent in share mode sets the priority to all pieces to 0, except for the pieces
|
||||
that are downloaded, when pieces are decided to be downloaded. This affects the progress
|
||||
bar, which might be set to "100% finished" most of the time. Do not change file or piece
|
||||
priorities for torrents in share mode, it will make it not work.
|
||||
|
||||
The share mode has one setting, the share ratio target, see ``session_settings::share_mode_target``
|
||||
for more info.
|
||||
|
||||
``file_priorities`` can be set to control the initial file priorities when adding
|
||||
a torrent. The semantics are the same as for ``torrent_handle::prioritize_files()``.
|
||||
|
||||
|
@ -2058,6 +2075,7 @@ Its declaration looks like this::
|
|||
void force_recheck() const;
|
||||
void clear_error() const;
|
||||
void set_upload_mode(bool m) const;
|
||||
void set_share_mode(bool m) const;
|
||||
|
||||
void flush_cache() const;
|
||||
|
||||
|
@ -2559,6 +2577,18 @@ are automatically put in upload mode whenever they encounter a disk write error.
|
|||
To test if a torrent is in upload mode, call ``torrent_handle::status()`` and inspect
|
||||
``torrent_status::upload_mode``.
|
||||
|
||||
set_share_mode()
|
||||
----------------
|
||||
|
||||
::
|
||||
|
||||
void set_share_mode(bool m) const;
|
||||
|
||||
Enable or disable share mode for this torrent. When in share mode, the torrent will
|
||||
not necessarily be downloaded, especially not the whole of it. Only parts that are likely
|
||||
to be distributed to more than 2 other peers are downloaded, and only if the previous
|
||||
prediction was correct.
|
||||
|
||||
resolve_countries()
|
||||
-------------------
|
||||
|
||||
|
@ -3129,8 +3159,8 @@ It contains the following fields::
|
|||
int sparse_regions;
|
||||
|
||||
bool seed_mode;
|
||||
|
||||
bool upload_mode;
|
||||
bool share_mode;
|
||||
|
||||
int priority;
|
||||
|
||||
|
@ -3363,6 +3393,9 @@ been resolved. If the torrent is not auto-managed, you have to explicitly
|
|||
take it out of the upload mode by calling `set_upload_mode()`_ on the
|
||||
torrent_handle_.
|
||||
|
||||
``share_mode`` is true if the torrent is currently in share-mode, i.e.
|
||||
not downloading the torrent, but just helping the swarm out.
|
||||
|
||||
``added_time`` is the posix-time when this torrent was added. i.e. what
|
||||
``time(NULL)`` returned at the time.
|
||||
|
||||
|
@ -3929,6 +3962,7 @@ session_settings
|
|||
bool ignore_resume_timestamps;
|
||||
bool anonymous_mode;
|
||||
int tick_interval;
|
||||
int share_mode_target;
|
||||
};
|
||||
|
||||
``version`` is automatically set to the libtorrent version you're using
|
||||
|
@ -4590,6 +4624,15 @@ peers. It should not be more than one second (i.e. 1000 ms). Setting this
|
|||
to a low value (around 100) means higher resolution bandwidth quota distribution,
|
||||
setting it to a higher value saves CPU cycles.
|
||||
|
||||
``share_mode_target`` specifies the target share ratio for share mode torrents.
|
||||
This defaults to 3, meaning we'll try to upload 3 times as much as we download.
|
||||
Setting this very high, will make it very conservative and you might end up
|
||||
not downloading anything ever (and not affecting your share ratio). It does
|
||||
not make any sense to set this any lower than 2. For instance, if only 3 peers
|
||||
need to download the rarest piece, it's impossible to download a single piece
|
||||
and upload it more than 3 times. If the share_mode_target is set to more than 3,
|
||||
nothing is downloaded.
|
||||
|
||||
pe_settings
|
||||
===========
|
||||
|
||||
|
|
|
@ -530,6 +530,8 @@ std::string outgoing_interface = "";
|
|||
int poll_interval = 5;
|
||||
int max_connections_per_torrent = 50;
|
||||
|
||||
bool share_mode = false;
|
||||
|
||||
using boost::bind;
|
||||
|
||||
// monitored_dir is true if this torrent is added because
|
||||
|
@ -560,6 +562,7 @@ void add_torrent(libtorrent::session& ses
|
|||
printf("%s\n", t->name().c_str());
|
||||
|
||||
add_torrent_params p;
|
||||
p.share_mode = share_mode;
|
||||
lazy_entry resume_data;
|
||||
|
||||
std::string filename = combine_path(save_path, t->name() + ".resume");
|
||||
|
@ -810,7 +813,7 @@ int main(int argc, char* argv[])
|
|||
//settings.announce_to_all_trackers = true;
|
||||
settings.optimize_hashing_for_speed = false;
|
||||
settings.disk_cache_algorithm = session_settings::largest_contiguous;
|
||||
settings.volatile_read_cache = true;
|
||||
settings.volatile_read_cache = false;
|
||||
|
||||
proxy_settings ps;
|
||||
|
||||
|
@ -868,6 +871,7 @@ int main(int argc, char* argv[])
|
|||
from_hex(argv[i], 40, (char*)&info_hash[0]);
|
||||
|
||||
add_torrent_params p;
|
||||
p.share_mode = share_mode;
|
||||
p.tracker_url = argv[i] + 41;
|
||||
p.info_hash = info_hash;
|
||||
p.save_path = save_path;
|
||||
|
@ -908,6 +912,7 @@ int main(int argc, char* argv[])
|
|||
case 'U': torrent_upload_limit = atoi(arg) * 1000; break;
|
||||
case 'D': torrent_download_limit = atoi(arg) * 1000; break;
|
||||
case 'm': monitor_dir = arg; break;
|
||||
case 'M': share_mode = true; --i; break;
|
||||
case 'b': bind_to_interface = arg; break;
|
||||
case 'w': settings.urlseed_wait_retry = atoi(arg); break;
|
||||
case 't': poll_interval = atoi(arg); break;
|
||||
|
@ -1053,6 +1058,7 @@ int main(int argc, char* argv[])
|
|||
if (std::strstr(i->c_str(), "magnet:") == i->c_str())
|
||||
{
|
||||
add_torrent_params p;
|
||||
p.share_mode = share_mode;
|
||||
p.save_path = save_path;
|
||||
p.storage_mode = (storage_mode_t)allocation_mode;
|
||||
printf("adding MANGET link: %s\n", i->c_str());
|
||||
|
@ -1404,7 +1410,7 @@ int main(int argc, char* argv[])
|
|||
out += str;
|
||||
}
|
||||
|
||||
if (print_piece_bar && s.progress_ppm < 1000000 && s.progress > 0)
|
||||
if (print_piece_bar && s.state != torrent_status::seeding)
|
||||
{
|
||||
out += " ";
|
||||
out += piece_bar(s.pieces, terminal_width - 7);
|
||||
|
|
|
@ -62,6 +62,7 @@ namespace libtorrent
|
|||
, override_resume_data(false)
|
||||
, upload_mode(false)
|
||||
, file_priorities(0)
|
||||
, share_mode(false)
|
||||
{}
|
||||
|
||||
// libtorrent version. Used for forward binary compatibility
|
||||
|
@ -82,6 +83,7 @@ namespace libtorrent
|
|||
bool override_resume_data;
|
||||
bool upload_mode;
|
||||
std::vector<boost::uint8_t> const* file_priorities;
|
||||
bool share_mode;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -102,7 +102,7 @@ namespace libtorrent
|
|||
|
||||
void start();
|
||||
|
||||
enum { upload_only_msg = 2 };
|
||||
enum { upload_only_msg = 2, share_mode_msg = 3 };
|
||||
|
||||
~bt_peer_connection();
|
||||
|
||||
|
@ -205,6 +205,7 @@ namespace libtorrent
|
|||
#ifndef TORRENT_DISABLE_EXTENSIONS
|
||||
void write_extensions();
|
||||
void write_upload_only();
|
||||
void write_share_mode();
|
||||
#endif
|
||||
void write_metadata(std::pair<int, int> req);
|
||||
void write_metadata_request(std::pair<int, int> req);
|
||||
|
@ -365,6 +366,10 @@ private:
|
|||
// 0 if not supported
|
||||
int m_upload_only_id;
|
||||
|
||||
// the message ID for share mode message
|
||||
// 0 if not supported
|
||||
int m_share_mode_id;
|
||||
|
||||
char m_reserved_bits[8];
|
||||
// this is set to true if the handshake from
|
||||
// the peer indicated that it supports the
|
||||
|
|
|
@ -274,6 +274,10 @@ namespace libtorrent
|
|||
bool can_read(char* state = 0) const;
|
||||
|
||||
bool is_seed() const;
|
||||
int num_have_pieces() const { return m_num_pieces; }
|
||||
|
||||
void set_share_mode(bool m);
|
||||
bool share_mode() const { return m_share_mode; }
|
||||
|
||||
void set_upload_only(bool u);
|
||||
bool upload_only() const { return m_upload_only; }
|
||||
|
@ -1043,7 +1047,10 @@ namespace libtorrent
|
|||
// the http-downloader, to request whole pieces
|
||||
// at a time.
|
||||
bool m_request_large_blocks:1;
|
||||
|
||||
|
||||
// set to true if this peer is in share mode
|
||||
bool m_share_mode:1;
|
||||
|
||||
// set to true when this peer is only uploading
|
||||
bool m_upload_only:1;
|
||||
|
||||
|
|
|
@ -95,6 +95,8 @@ namespace libtorrent
|
|||
{
|
||||
public:
|
||||
|
||||
struct piece_pos;
|
||||
|
||||
enum
|
||||
{
|
||||
// the number of priority levels
|
||||
|
@ -162,7 +164,7 @@ namespace libtorrent
|
|||
// the number of blocks in the requested state
|
||||
boost::int16_t requested;
|
||||
};
|
||||
|
||||
|
||||
piece_picker();
|
||||
|
||||
void get_availability(std::vector<int>& avail) const;
|
||||
|
@ -292,7 +294,13 @@ namespace libtorrent
|
|||
|
||||
// returns information about the given piece
|
||||
void piece_info(int index, piece_picker::downloading_piece& st) const;
|
||||
|
||||
|
||||
piece_pos const& piece_stats(int index) const
|
||||
{
|
||||
TORRENT_ASSERT(index >= 0 && index < int(m_piece_map.size()));
|
||||
return m_piece_map[index];
|
||||
}
|
||||
|
||||
// if a piece had a hash-failure, it must be restored and
|
||||
// made available for redownloading
|
||||
void restore_piece(int index);
|
||||
|
@ -359,6 +367,8 @@ namespace libtorrent
|
|||
std::pair<int, int> expand_piece(int piece, int whole_pieces
|
||||
, bitfield const& have) const;
|
||||
|
||||
public:
|
||||
|
||||
struct piece_pos
|
||||
{
|
||||
piece_pos() {}
|
||||
|
@ -452,6 +462,8 @@ namespace libtorrent
|
|||
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
BOOST_STATIC_ASSERT(sizeof(piece_pos) == sizeof(char) * 4);
|
||||
|
||||
void update_pieces() const;
|
||||
|
|
|
@ -221,6 +221,7 @@ namespace libtorrent
|
|||
, anonymous_mode(false)
|
||||
, tick_interval(100)
|
||||
, report_web_seed_downloads(true)
|
||||
, share_mode_target(3)
|
||||
{}
|
||||
|
||||
// libtorrent version. Used for forward binary compatibility
|
||||
|
@ -843,6 +844,9 @@ namespace libtorrent
|
|||
// specifies whether downloads from web seeds is reported to the
|
||||
// tracker or not. Defaults to on
|
||||
bool report_web_seed_downloads;
|
||||
|
||||
// this is the target share ratio for share-mode torrents
|
||||
int share_mode_target;
|
||||
};
|
||||
|
||||
#ifndef TORRENT_DISABLE_DHT
|
||||
|
|
|
@ -178,8 +178,12 @@ namespace libtorrent
|
|||
void start_announcing();
|
||||
void stop_announcing();
|
||||
|
||||
void send_share_mode();
|
||||
void send_upload_only();
|
||||
|
||||
void set_share_mode(bool s);
|
||||
bool share_mode() const { return m_share_mode; }
|
||||
|
||||
void set_upload_mode(bool b);
|
||||
bool upload_mode() const { return m_upload_mode; }
|
||||
bool is_upload_only() const
|
||||
|
@ -502,6 +506,8 @@ namespace libtorrent
|
|||
// --------------------------------------------
|
||||
// PIECE MANAGEMENT
|
||||
|
||||
void recalc_share_mode();
|
||||
|
||||
void update_sparse_piece_prio(int piece, int cursor, int reverse_cursor);
|
||||
|
||||
void get_suggested_pieces(std::vector<int>& s) const;
|
||||
|
@ -525,8 +531,8 @@ namespace libtorrent
|
|||
int num_have() const
|
||||
{
|
||||
return has_picker()
|
||||
?m_picker->num_have()
|
||||
:m_torrent_file->num_pieces();
|
||||
? m_picker->num_have()
|
||||
: m_torrent_file->num_pieces();
|
||||
}
|
||||
|
||||
// when we get a have message, this is called for that piece
|
||||
|
@ -1210,6 +1216,9 @@ namespace libtorrent
|
|||
// slots.
|
||||
bool m_auto_managed:1;
|
||||
|
||||
// this is set when the torrent is in share-mode
|
||||
bool m_share_mode:1;
|
||||
|
||||
// m_num_verified = m_verified.count()
|
||||
boost::uint16_t m_num_verified;
|
||||
|
||||
|
|
|
@ -123,6 +123,7 @@ namespace libtorrent
|
|||
, sparse_regions(0)
|
||||
, seed_mode(false)
|
||||
, upload_mode(false)
|
||||
, share_mode(false)
|
||||
, priority(0)
|
||||
, added_time(0)
|
||||
, completed_time(0)
|
||||
|
@ -306,6 +307,9 @@ namespace libtorrent
|
|||
// write operation failing
|
||||
bool upload_mode;
|
||||
|
||||
// this is true if the torrent is in share-mode
|
||||
bool share_mode;
|
||||
|
||||
// the priority of this torrent
|
||||
int priority;
|
||||
|
||||
|
@ -468,6 +472,7 @@ namespace libtorrent
|
|||
void pause() const;
|
||||
void resume() const;
|
||||
void set_upload_mode(bool b) const;
|
||||
void set_share_mode(bool b) const;
|
||||
void flush_cache() const;
|
||||
|
||||
void force_recheck() const;
|
||||
|
|
|
@ -98,6 +98,7 @@ namespace libtorrent
|
|||
, m_state(read_protocol_identifier)
|
||||
#ifndef TORRENT_DISABLE_EXTENSIONS
|
||||
, m_upload_only_id(0)
|
||||
, m_share_mode_id(0)
|
||||
, m_supports_extensions(false)
|
||||
#endif
|
||||
, m_supports_dht_port(false)
|
||||
|
@ -1496,6 +1497,13 @@ namespace libtorrent
|
|||
return;
|
||||
}
|
||||
|
||||
if (extended_id == share_mode_msg)
|
||||
{
|
||||
if (!packet_finished()) return;
|
||||
set_share_mode(detail::read_uint8(recv_buffer.begin));
|
||||
return;
|
||||
}
|
||||
|
||||
disconnect(errors::invalid_message, 2);
|
||||
return;
|
||||
}
|
||||
|
@ -1564,6 +1572,9 @@ namespace libtorrent
|
|||
if (root.dict_find_int_value("upload_only", 0))
|
||||
set_upload_only(true);
|
||||
|
||||
if (root.dict_find_int_value("share_mode", 0))
|
||||
set_share_mode(true);
|
||||
|
||||
std::string myip = root.dict_find_string_value("yourip");
|
||||
if (!myip.empty())
|
||||
{
|
||||
|
@ -1591,7 +1602,8 @@ namespace libtorrent
|
|||
// if we're finished and this peer is uploading only
|
||||
// disconnect it
|
||||
if (t->is_finished() && upload_only()
|
||||
&& t->settings().close_redundant_connections)
|
||||
&& t->settings().close_redundant_connections
|
||||
&& !t->share_mode())
|
||||
disconnect(errors::upload_upload_connection);
|
||||
}
|
||||
|
||||
|
@ -1662,6 +1674,7 @@ namespace libtorrent
|
|||
|
||||
boost::shared_ptr<torrent> t = associated_torrent().lock();
|
||||
if (m_upload_only_id == 0) return;
|
||||
if (t->share_mode()) return;
|
||||
|
||||
char msg[7] = {0, 0, 0, 3, msg_extended};
|
||||
char* ptr = msg + 5;
|
||||
|
@ -1669,6 +1682,20 @@ namespace libtorrent
|
|||
detail::write_uint8(t->is_upload_only(), ptr);
|
||||
send_buffer(msg, sizeof(msg));
|
||||
}
|
||||
|
||||
void bt_peer_connection::write_share_mode()
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
boost::shared_ptr<torrent> t = associated_torrent().lock();
|
||||
if (m_share_mode_id == 0) return;
|
||||
|
||||
char msg[7] = {0, 0, 0, 3, msg_extended};
|
||||
char* ptr = msg + 5;
|
||||
detail::write_uint8(m_share_mode_id, ptr);
|
||||
detail::write_uint8(t->share_mode(), ptr);
|
||||
send_buffer(msg, sizeof(msg));
|
||||
}
|
||||
#endif
|
||||
|
||||
void bt_peer_connection::write_keepalive()
|
||||
|
@ -1900,19 +1927,25 @@ namespace libtorrent
|
|||
TORRENT_ASSERT(t);
|
||||
|
||||
m["upload_only"] = upload_only_msg;
|
||||
m["share_mode"] = share_mode_msg;
|
||||
int complete_ago = -1;
|
||||
if (t->last_seen_complete() > 0) complete_ago = t->time_since_complete();
|
||||
handshake["complete_ago"] = complete_ago;
|
||||
|
||||
// if we're using lazy bitfields or if we're super seeding, don't say
|
||||
// we're upload only, since it might make peers disconnect
|
||||
if (t->is_upload_only() && (!m_ses.settings().lazy_bitfields
|
||||
// don't tell anyone we're upload only when in share mode
|
||||
// we want to stay connected to seeds
|
||||
if (t->is_upload_only() && !t->share_mode() && (!m_ses.settings().lazy_bitfields
|
||||
#ifndef TORRENT_DISABLE_ENCRYPTION
|
||||
|| m_encrypted
|
||||
#endif
|
||||
))
|
||||
handshake["upload_only"] = 1;
|
||||
|
||||
if (t->share_mode())
|
||||
handshake["share_mode"] = 1;
|
||||
|
||||
if (!m_ses.m_settings.anonymous_mode)
|
||||
{
|
||||
tcp::endpoint ep = m_ses.get_ipv6_interface();
|
||||
|
|
|
@ -156,7 +156,7 @@ namespace libtorrent { namespace
|
|||
virtual void add_handshake(entry& h)
|
||||
{
|
||||
entry& messages = h["m"];
|
||||
messages["lt_tex"] = 3;
|
||||
messages["lt_tex"] = 19;
|
||||
h["tr"] = m_tp.list_hash().to_string();
|
||||
}
|
||||
|
||||
|
@ -186,7 +186,7 @@ namespace libtorrent { namespace
|
|||
virtual bool on_extended(int length
|
||||
, int extended_msg, buffer::const_interval body)
|
||||
{
|
||||
if (extended_msg != 3) return false;
|
||||
if (extended_msg != 19) return false;
|
||||
if (m_message_index == 0) return false;
|
||||
if (!m_pc.packet_finished()) return true;
|
||||
|
||||
|
|
|
@ -144,6 +144,7 @@ namespace libtorrent
|
|||
, m_connecting(true)
|
||||
, m_queued(true)
|
||||
, m_request_large_blocks(false)
|
||||
, m_share_mode(false)
|
||||
, m_upload_only(false)
|
||||
, m_snubbed(false)
|
||||
, m_bitfield_received(false)
|
||||
|
@ -282,6 +283,7 @@ namespace libtorrent
|
|||
, m_connecting(false)
|
||||
, m_queued(false)
|
||||
, m_request_large_blocks(false)
|
||||
, m_share_mode(false)
|
||||
, m_upload_only(false)
|
||||
, m_snubbed(false)
|
||||
, m_bitfield_received(false)
|
||||
|
@ -513,7 +515,7 @@ namespace libtorrent
|
|||
void peer_connection::update_interest()
|
||||
{
|
||||
boost::shared_ptr<torrent> t = m_torrent.lock();
|
||||
TORRENT_ASSERT(t);
|
||||
if (!t) return;
|
||||
|
||||
// if m_have_piece is 0, it means the connections
|
||||
// have not been initialized yet. The interested
|
||||
|
@ -1794,6 +1796,9 @@ namespace libtorrent
|
|||
boost::shared_ptr<torrent> t = m_torrent.lock();
|
||||
if (!t) return;
|
||||
|
||||
// don't close connections in share mode, we don't know if we need them
|
||||
if (t->share_mode()) return;
|
||||
|
||||
if (m_upload_only && t->is_finished())
|
||||
{
|
||||
disconnect(errors::upload_upload_connection);
|
||||
|
@ -4025,6 +4030,7 @@ namespace libtorrent
|
|||
INVARIANT_CHECK;
|
||||
#endif
|
||||
|
||||
bool sent_a_piece = false;
|
||||
boost::shared_ptr<torrent> t = m_torrent.lock();
|
||||
if (!t) return;
|
||||
|
||||
|
@ -4070,7 +4076,11 @@ namespace libtorrent
|
|||
m_reading_bytes += r.length;
|
||||
|
||||
m_requests.erase(m_requests.begin());
|
||||
sent_a_piece = true;
|
||||
}
|
||||
|
||||
if (t->share_mode() && sent_a_piece)
|
||||
t->recalc_share_mode();
|
||||
}
|
||||
|
||||
void peer_connection::on_disk_read_complete(int ret, disk_io_job const& j, peer_request r)
|
||||
|
@ -5143,7 +5153,8 @@ namespace libtorrent
|
|||
if (t->ready_for_connections() && m_initialized)
|
||||
TORRENT_ASSERT(t->torrent_file().num_pieces() == int(m_have_piece.size()));
|
||||
|
||||
if (m_ses.settings().close_redundant_connections)
|
||||
// in share mode we don't close redundant connections
|
||||
if (m_ses.settings().close_redundant_connections && !t->share_mode())
|
||||
{
|
||||
// make sure upload only peers are disconnected
|
||||
if (t->is_finished() && m_upload_only)
|
||||
|
@ -5333,6 +5344,14 @@ namespace libtorrent
|
|||
return m_num_pieces == (int)m_have_piece.size() && m_num_pieces > 0 && t && t->valid_metadata();
|
||||
}
|
||||
|
||||
void peer_connection::set_share_mode(bool u)
|
||||
{
|
||||
// if the peer is a seed, ignore share mode messages
|
||||
if (is_seed()) return;
|
||||
|
||||
m_share_mode = u;
|
||||
}
|
||||
|
||||
void peer_connection::set_upload_only(bool u)
|
||||
{
|
||||
// if the peer is a seed, don't allow setting
|
||||
|
|
199
src/torrent.cpp
199
src/torrent.cpp
|
@ -378,6 +378,7 @@ namespace libtorrent
|
|||
, m_allow_peers(!p.paused)
|
||||
, m_upload_mode(p.upload_mode)
|
||||
, m_auto_managed(p.auto_managed)
|
||||
, m_share_mode(p.share_mode)
|
||||
, m_num_verified(0)
|
||||
, m_last_scrape(0)
|
||||
, m_last_download(0)
|
||||
|
@ -418,7 +419,6 @@ namespace libtorrent
|
|||
m_trackers.back().source = announce_entry::source_magnet_link;
|
||||
m_torrent_file->add_tracker(p.tracker_url);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void torrent::start()
|
||||
|
@ -533,6 +533,19 @@ namespace libtorrent
|
|||
}
|
||||
}
|
||||
|
||||
void torrent::send_share_mode()
|
||||
{
|
||||
#ifndef TORRENT_DISABLE_EXTENSIONS
|
||||
for (std::set<peer_connection*>::iterator i = m_connections.begin()
|
||||
, end(m_connections.end()); i != end; ++i)
|
||||
{
|
||||
if ((*i)->type() != peer_connection::bittorrent_connection) continue;
|
||||
bt_peer_connection* p = (bt_peer_connection*)*i;
|
||||
p->write_share_mode();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void torrent::send_upload_only()
|
||||
{
|
||||
#ifndef TORRENT_DISABLE_EXTENSIONS
|
||||
|
@ -546,6 +559,20 @@ namespace libtorrent
|
|||
#endif
|
||||
}
|
||||
|
||||
void torrent::set_share_mode(bool s)
|
||||
{
|
||||
if (s == m_share_mode) return;
|
||||
|
||||
m_share_mode = s;
|
||||
|
||||
// in share mode, all pieces have their priorities initialized to 0
|
||||
std::fill(m_file_priority.begin(), m_file_priority.end(), !m_share_mode);
|
||||
|
||||
update_piece_priorities();
|
||||
|
||||
if (m_share_mode) recalc_share_mode();
|
||||
}
|
||||
|
||||
void torrent::set_upload_mode(bool b)
|
||||
{
|
||||
if (b == m_upload_mode) return;
|
||||
|
@ -855,6 +882,16 @@ namespace libtorrent
|
|||
m_picker->init(blocks_per_piece, blocks_in_last_piece, m_torrent_file->num_pieces());
|
||||
}
|
||||
|
||||
if (m_share_mode)
|
||||
{
|
||||
// in share mode, all pieces have their priorities initialized to 0
|
||||
std::fill(m_file_priority.begin(), m_file_priority.end(), 0);
|
||||
}
|
||||
|
||||
// in case file priorities were passed in via the add_torrent_params
|
||||
// ans also in the case of share mode, we need to update the priorities
|
||||
update_piece_priorities();
|
||||
|
||||
std::vector<std::string> const& url_seeds = m_torrent_file->url_seeds();
|
||||
for (std::vector<std::string>::const_iterator i = url_seeds.begin()
|
||||
, end(url_seeds.end()); i != end; ++i)
|
||||
|
@ -2335,6 +2372,9 @@ namespace libtorrent
|
|||
}
|
||||
|
||||
m_last_download = 0;
|
||||
|
||||
if (m_share_mode)
|
||||
recalc_share_mode();
|
||||
}
|
||||
|
||||
void torrent::piece_failed(int index)
|
||||
|
@ -3744,6 +3784,7 @@ namespace libtorrent
|
|||
m_file_priority[i] = file_priority->list_int_value_at(i, 1);
|
||||
update_piece_priorities();
|
||||
}
|
||||
|
||||
lazy_entry const* piece_priority = rd.dict_find_string("piece_priority");
|
||||
if (piece_priority && piece_priority->string_length()
|
||||
== m_torrent_file->num_pieces())
|
||||
|
@ -4365,6 +4406,9 @@ namespace libtorrent
|
|||
}
|
||||
#endif
|
||||
|
||||
if (m_share_mode)
|
||||
recalc_share_mode();
|
||||
|
||||
return peerinfo->connection;
|
||||
}
|
||||
|
||||
|
@ -4494,6 +4538,10 @@ namespace libtorrent
|
|||
#if defined TORRENT_DEBUG && !defined TORRENT_DISABLE_INVARIANT_CHECKS
|
||||
m_policy.check_invariant();
|
||||
#endif
|
||||
|
||||
if (m_share_mode)
|
||||
recalc_share_mode();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -4961,7 +5009,7 @@ namespace libtorrent
|
|||
if (!i->not_wanted && !i->timed_out) ++num_requests[i->block];
|
||||
if (!p.is_choked() && !p.ignore_unchoke_slots()) ++num_uploads;
|
||||
torrent* associated_torrent = p.associated_torrent().lock().get();
|
||||
if (associated_torrent != this)
|
||||
if (associated_torrent != this && associated_torrent != 0)
|
||||
TORRENT_ASSERT(false);
|
||||
}
|
||||
TORRENT_ASSERT(num_uploads == m_num_uploads);
|
||||
|
@ -5862,6 +5910,152 @@ namespace libtorrent
|
|||
m_stat.second_tick(tick_interval_ms);
|
||||
}
|
||||
|
||||
void torrent::recalc_share_mode()
|
||||
{
|
||||
TORRENT_ASSERT(share_mode());
|
||||
if (is_seed()) return;
|
||||
|
||||
int pieces_in_torrent = m_torrent_file->num_pieces();
|
||||
int num_seeds = 0;
|
||||
int num_peers = 0;
|
||||
int num_downloaders = 0;
|
||||
int missing_pieces = 0;
|
||||
int num_interested = 0;
|
||||
for (std::set<peer_connection*>::iterator i = m_connections.begin()
|
||||
, end(m_connections.end()); i != end; ++i)
|
||||
{
|
||||
peer_connection* p = *i;
|
||||
if (p->is_connecting()) continue;
|
||||
++num_peers;
|
||||
if (p->is_seed())
|
||||
{
|
||||
++num_seeds;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (p->share_mode()) continue;
|
||||
|
||||
if ((*i)->is_peer_interested()) ++num_interested;
|
||||
++num_downloaders;
|
||||
missing_pieces += pieces_in_torrent - p->num_have_pieces();
|
||||
}
|
||||
|
||||
if (num_peers == 0) return;
|
||||
|
||||
if (num_seeds * 100 / num_peers > 50
|
||||
&& (num_peers * 100 / m_max_connections > 90
|
||||
|| num_peers > 20))
|
||||
{
|
||||
// we are connected to more than 90% seeds (and we're beyond
|
||||
// 90% of the max number of connections). That will
|
||||
// limit our ability to upload. We need more downloaders.
|
||||
// disconnect some seeds so that we don't have more than 50%
|
||||
int to_disconnect = num_seeds - num_peers / 2;
|
||||
std::vector<peer_connection*> seeds;
|
||||
seeds.reserve(num_seeds);
|
||||
for (std::set<peer_connection*>::iterator i = m_connections.begin()
|
||||
, end(m_connections.end()); i != end; ++i)
|
||||
{
|
||||
peer_connection* p = *i;
|
||||
if (p->is_seed()) seeds.push_back(p);
|
||||
}
|
||||
|
||||
std::random_shuffle(seeds.begin(), seeds.end());
|
||||
TORRENT_ASSERT(to_disconnect <= int(seeds.size()));
|
||||
for (int i = 0; i < to_disconnect; ++i)
|
||||
seeds[i]->disconnect(errors::upload_upload_connection);
|
||||
}
|
||||
|
||||
if (num_downloaders == 0) return;
|
||||
|
||||
// assume that the seeds are about as fast as us. During the time
|
||||
// we can download one piece, and upload one piece, each seed
|
||||
// can upload two pieces.
|
||||
missing_pieces -= 2 * num_seeds;
|
||||
|
||||
if (missing_pieces <= 0) return;
|
||||
|
||||
// missing_pieces represents our opportunity to download pieces
|
||||
// and share them more than once each
|
||||
|
||||
// now, download at least one piece, otherwise download one more
|
||||
// piece if our downloaded (and downloading) pieces is less than 50%
|
||||
// of the uploaded bytes
|
||||
int num_downloaded_pieces = (std::max)(m_picker->num_have()
|
||||
, pieces_in_torrent - m_picker->num_filtered());
|
||||
|
||||
if (num_downloaded_pieces * m_torrent_file->piece_length()
|
||||
* settings().share_mode_target > m_total_uploaded
|
||||
&& num_downloaded_pieces > 0)
|
||||
return;
|
||||
|
||||
// don't have more pieces downloading in parallel than 5% of the total
|
||||
// number of pieces we have downloaded
|
||||
if (m_picker->get_download_queue().size() > num_downloaded_pieces / 20)
|
||||
return;
|
||||
|
||||
// one more important property is that there are enough pieces
|
||||
// that more than one peer wants to download
|
||||
// make sure that there are enough downloaders for the rarest
|
||||
// piece. Go through all pieces, figure out which one is the rarest
|
||||
// and how many peers that has that piece
|
||||
|
||||
std::vector<int> rarest_pieces;
|
||||
|
||||
int num_pieces = m_torrent_file->num_pieces();
|
||||
int rarest_rarity = INT_MAX;
|
||||
bool prio_updated = false;
|
||||
for (int i = 0; i < num_pieces; ++i)
|
||||
{
|
||||
piece_picker::piece_pos const& pp = m_picker->piece_stats(i);
|
||||
if (pp.peer_count == 0) continue;
|
||||
if (pp.filtered() && (pp.have() || pp.downloading))
|
||||
{
|
||||
m_picker->set_piece_priority(i, 1);
|
||||
prio_updated = true;
|
||||
continue;
|
||||
}
|
||||
// don't count pieces we already have or are downloading
|
||||
if (!pp.filtered() || pp.have()) continue;
|
||||
if (pp.peer_count > rarest_rarity) continue;
|
||||
if (pp.peer_count == rarest_rarity)
|
||||
{
|
||||
rarest_pieces.push_back(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
rarest_pieces.clear();
|
||||
rarest_rarity = pp.peer_count;
|
||||
rarest_pieces.push_back(i);
|
||||
}
|
||||
|
||||
if (prio_updated)
|
||||
m_policy.recalculate_connect_candidates();
|
||||
|
||||
// now, rarest_pieces is a list of all pieces that are the rarest ones.
|
||||
// and rarest_rarity is the number of peers that have the rarest pieces
|
||||
|
||||
// if there's only a single peer that doesn't have the rarest piece
|
||||
// it's impossible for us to download one piece and upload it
|
||||
// twice. i.e. we cannot get a positive share ratio
|
||||
if (num_peers - rarest_rarity < settings().share_mode_target) return;
|
||||
|
||||
// we might be able to do better than a share ratio of 2 if there are
|
||||
// enough downloaders of the pieces we already have.
|
||||
// TODO: go through the pieces we have and count the total number
|
||||
// of downloaders we have. Only count peers that are interested in us
|
||||
// since some peers might not send have messages for pieces we have
|
||||
// it num_interested == 0, we need to pick a new piece
|
||||
|
||||
// now, pick one of the rarest pieces to download
|
||||
int pick = rand() % rarest_pieces.size();
|
||||
bool was_finished = is_finished();
|
||||
m_picker->set_piece_priority(rarest_pieces[pick], 1);
|
||||
update_peer_interest(was_finished);
|
||||
|
||||
m_policy.recalculate_connect_candidates();
|
||||
}
|
||||
|
||||
void torrent::refresh_explicit_cache(int cache_size)
|
||||
{
|
||||
TORRENT_ASSERT(m_ses.is_network_thread());
|
||||
|
@ -6404,6 +6598,7 @@ namespace libtorrent
|
|||
st.completed_time = m_completed_time;
|
||||
|
||||
st.last_scrape = m_last_scrape;
|
||||
st.share_mode = m_share_mode;
|
||||
st.upload_mode = m_upload_mode;
|
||||
st.up_bandwidth_queue = 0;
|
||||
st.down_bandwidth_queue = 0;
|
||||
|
|
|
@ -363,6 +363,12 @@ namespace libtorrent
|
|||
TORRENT_ASYNC_CALL(pause);
|
||||
}
|
||||
|
||||
void torrent_handle::set_share_mode(bool b) const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
TORRENT_ASYNC_CALL1(set_share_mode, b);
|
||||
}
|
||||
|
||||
void torrent_handle::set_upload_mode(bool b) const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
|
Loading…
Reference in New Issue