forked from premiere/premiere-libtorrent
added natpmp support. bumped version number to 0.13. added piece priority support to the piece picker. optmized and simplified the piece picker in the process.
This commit is contained in:
parent
23c95e35a0
commit
ecaa3068d5
|
@ -1,3 +1,7 @@
|
|||
* added support for NAT-PMP
|
||||
* added support for piece priorities. Piece filtering is now set as
|
||||
a priority
|
||||
|
||||
release 0.12
|
||||
|
||||
* fixes to make the DHT more compatible
|
||||
|
|
1
Jamfile
1
Jamfile
|
@ -41,6 +41,7 @@ SOURCES =
|
|||
peer_connection.cpp
|
||||
bt_peer_connection.cpp
|
||||
web_peer_connection.cpp
|
||||
natpmp.cpp
|
||||
piece_picker.cpp
|
||||
policy.cpp
|
||||
session.cpp
|
||||
|
|
|
@ -32,6 +32,7 @@ following features:
|
|||
* trackerless torrents (using the Mainline kademlia DHT protocol) with
|
||||
some `DHT extensions`_.
|
||||
* support for IPv6
|
||||
* NAT-PMP support (automatic port mapping on routers that supports it)
|
||||
* piece-wise, unordered, incremental file allocation
|
||||
* uses separate threads for checking files and for main downloader, with a
|
||||
fool-proof thread-safe library interface. (i.e. There's no way for the
|
||||
|
|
|
@ -657,7 +657,11 @@ response to a <tt class="docutils literal"><span class="pre">get_peers</span></t
|
|||
<p><tt class="docutils literal"><span class="pre">search_branching</span></tt> is the number of concurrent search request the node will
|
||||
send when announcing and refreshing the routing table. This parameter is
|
||||
called alpha in the kademlia paper.</p>
|
||||
<p><tt class="docutils literal"><span class="pre">service_port</span></tt> is the udp port the node will listen to.</p>
|
||||
<p><tt class="docutils literal"><span class="pre">service_port</span></tt> is the udp port the node will listen to. This will default
|
||||
to 0, which means the udp listen port will be the same as the tcp listen
|
||||
port. This is in general a good idea, since some NAT implementations
|
||||
reserves the udp port for any mapped tcp port, and vice versa. NAT-PMP
|
||||
guarantees this for example.</p>
|
||||
<p><tt class="docutils literal"><span class="pre">max_fail_count</span></tt> is the maximum number of failed tries to contact a node
|
||||
before it is removed from the routing table. If there are known working nodes
|
||||
that are ready to replace a failing node, it will be replaced immediately,
|
||||
|
|
|
@ -542,7 +542,11 @@ response to a ``get_peers`` message from another node.
|
|||
send when announcing and refreshing the routing table. This parameter is
|
||||
called alpha in the kademlia paper.
|
||||
|
||||
``service_port`` is the udp port the node will listen to.
|
||||
``service_port`` is the udp port the node will listen to. This will default
|
||||
to 0, which means the udp listen port will be the same as the tcp listen
|
||||
port. This is in general a good idea, since some NAT implementations
|
||||
reserves the udp port for any mapped tcp port, and vice versa. NAT-PMP
|
||||
guarantees this for example.
|
||||
|
||||
``max_fail_count`` is the maximum number of failed tries to contact a node
|
||||
before it is removed from the routing table. If there are known working nodes
|
||||
|
|
|
@ -252,6 +252,9 @@ void print_peer_info(std::ostream& out, std::vector<libtorrent::peer_info> const
|
|||
for (std::vector<peer_info>::const_iterator i = peers.begin();
|
||||
i != peers.end(); ++i)
|
||||
{
|
||||
if (i->flags & (peer_info::handshake | peer_info::connecting | peer_info::queued))
|
||||
continue;
|
||||
|
||||
out.fill(' ');
|
||||
out.width(2);
|
||||
out << esc("32") << (i->down_speed > 0 ? add_suffix(i->down_speed) + "/s " : " ")
|
||||
|
@ -590,9 +593,6 @@ int main(int ac, char* av[])
|
|||
#ifndef TORRENT_DISABLE_DHT
|
||||
settings.use_dht_as_fallback = false;
|
||||
|
||||
dht_settings s;
|
||||
s.service_port = listen_port;
|
||||
ses.set_dht_settings(s);
|
||||
boost::filesystem::ifstream dht_state_file(".dht_state"
|
||||
, std::ios_base::binary);
|
||||
dht_state_file.unsetf(std::ios_base::skipws);
|
||||
|
@ -968,10 +968,10 @@ int main(int ac, char* av[])
|
|||
for (int i = 0; i < info.num_files(); ++i)
|
||||
{
|
||||
if (file_progress[i] == 1.f)
|
||||
out << progress_bar(file_progress[i], 20, "32") << " "
|
||||
out << progress_bar(file_progress[i], 40, "32") << " "
|
||||
<< info.file_at(i).path.leaf() << "\n";
|
||||
else
|
||||
out << progress_bar(file_progress[i], 20, "33") << " "
|
||||
out << progress_bar(file_progress[i], 40, "33") << " "
|
||||
<< info.file_at(i).path.leaf() << "\n";
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ libtorrent/peer.hpp \
|
|||
libtorrent/peer_connection.hpp \
|
||||
libtorrent/bt_peer_connection.hpp \
|
||||
libtorrent/web_peer_connection.hpp \
|
||||
libtorrent/natpmp.hpp \
|
||||
libtorrent/peer_id.hpp \
|
||||
libtorrent/peer_info.hpp \
|
||||
libtorrent/peer_request.hpp \
|
||||
|
|
|
@ -77,6 +77,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include "libtorrent/stat.hpp"
|
||||
#include "libtorrent/file_pool.hpp"
|
||||
#include "libtorrent/bandwidth_manager.hpp"
|
||||
#include "libtorrent/natpmp.hpp"
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
|
@ -213,11 +214,16 @@ namespace libtorrent
|
|||
void add_dht_node(udp::endpoint n);
|
||||
void add_dht_router(std::pair<std::string, int> const& node);
|
||||
void set_dht_settings(dht_settings const& s);
|
||||
dht_settings const& kad_settings() const { return m_dht_settings; }
|
||||
dht_settings const& get_dht_settings() const { return m_dht_settings; }
|
||||
void start_dht(entry const& startup_state);
|
||||
void stop_dht();
|
||||
entry dht_state() const;
|
||||
#endif
|
||||
|
||||
// called when a port mapping is successful, or a router returns
|
||||
// a failure to map a port
|
||||
void on_port_mapping(int tcp_port, int udp_port, std::string const& errmsg);
|
||||
|
||||
bool is_aborted() const { return m_abort; }
|
||||
|
||||
void set_ip_filter(ip_filter const& f);
|
||||
|
@ -328,6 +334,15 @@ namespace libtorrent
|
|||
// that we should let the os decide which
|
||||
// interface to listen on
|
||||
tcp::endpoint m_listen_interface;
|
||||
|
||||
// this is typically set to the same as the local
|
||||
// listen port. In case a NAT port forward was
|
||||
// successfully opened, this will be set to the
|
||||
// port that is open on the external (NAT) interface
|
||||
// on the NAT box itself. This is the port that has
|
||||
// to be published to peers, since this is the port
|
||||
// the client is reachable through.
|
||||
int m_external_listen_port;
|
||||
|
||||
boost::shared_ptr<socket_acceptor> m_listen_socket;
|
||||
|
||||
|
@ -365,7 +380,18 @@ namespace libtorrent
|
|||
#ifndef TORRENT_DISABLE_DHT
|
||||
boost::intrusive_ptr<dht::dht_tracker> m_dht;
|
||||
dht_settings m_dht_settings;
|
||||
// if this is set to true, the dht listen port
|
||||
// will be set to the same as the tcp listen port
|
||||
// and will be synchronlized with it as it changes
|
||||
// it defaults to true
|
||||
bool m_dht_same_port;
|
||||
|
||||
// see m_external_listen_port. This is the same
|
||||
// but for the udp port used by the DHT.
|
||||
int m_external_udp_port;
|
||||
#endif
|
||||
natpmp m_natpmp;
|
||||
|
||||
// the timer used to fire the second_tick
|
||||
deadline_timer m_timer;
|
||||
#ifndef NDEBUG
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
#ifndef TORRENT_NATPMP_HPP
|
||||
#define TORRENT_NATPMP_HPP
|
||||
|
||||
#include <libtorrent/socket.hpp>
|
||||
#include <boost/date_time/posix_time/ptime.hpp>
|
||||
#include <boost/function.hpp>
|
||||
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
#include <fstream>
|
||||
#endif
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
|
||||
// int: external tcp port
|
||||
// int: external udp port
|
||||
// std::string: error message
|
||||
typedef boost::function<void(int, int, std::string const&)> portmap_callback_t;
|
||||
|
||||
class natpmp
|
||||
{
|
||||
public:
|
||||
natpmp(io_service& ios, portmap_callback_t const& cb);
|
||||
|
||||
// maps the ports, if a port is set to 0
|
||||
// it will not be mapped
|
||||
void set_mappings(int tcp, int udp);
|
||||
|
||||
void close();
|
||||
|
||||
private:
|
||||
|
||||
void update_mapping(int i, int port);
|
||||
void send_map_request(int i);
|
||||
void resend_request(int i, asio::error_code const& e);
|
||||
void on_reply(asio::error_code const& e
|
||||
, std::size_t bytes_transferred);
|
||||
void try_next_mapping(int i);
|
||||
void update_expiration_timer();
|
||||
void refresh_mapping(int i);
|
||||
void mapping_expired(asio::error_code const& e, int i);
|
||||
|
||||
struct mapping
|
||||
{
|
||||
mapping()
|
||||
: need_update(false)
|
||||
, local_port(0)
|
||||
, external_port(0)
|
||||
, protocol(1)
|
||||
{}
|
||||
|
||||
// indicates that the mapping has changed
|
||||
// and needs an update
|
||||
bool need_update;
|
||||
|
||||
// the time the port mapping will expire
|
||||
boost::posix_time::ptime expires;
|
||||
|
||||
// the local port for this mapping. If this is set
|
||||
// to 0, the mapping is not in use
|
||||
int local_port;
|
||||
|
||||
// the external (on the NAT router) port
|
||||
// for the mapping. This is the port we
|
||||
// should announce to others
|
||||
int external_port;
|
||||
|
||||
// 1 = udp, 2 = tcp
|
||||
int protocol;
|
||||
};
|
||||
|
||||
portmap_callback_t m_callback;
|
||||
|
||||
// 0 is tcp and 1 is udp
|
||||
mapping m_mappings[2];
|
||||
|
||||
// the endpoint to the nat router
|
||||
udp::endpoint m_nat_endpoint;
|
||||
|
||||
// this is the mapping that is currently
|
||||
// being updated. It is -1 in case no
|
||||
// mapping is being updated at the moment
|
||||
int m_currently_mapping;
|
||||
|
||||
// current retry count
|
||||
int m_retry_count;
|
||||
|
||||
// used to receive responses in
|
||||
char m_response_buffer[16];
|
||||
|
||||
// the endpoint we received the message from
|
||||
udp::endpoint m_remote;
|
||||
|
||||
// the udp socket used to communicate
|
||||
// with the NAT router
|
||||
datagram_socket m_socket;
|
||||
|
||||
// used to resend udp packets in case
|
||||
// they time out
|
||||
deadline_timer m_send_timer;
|
||||
|
||||
// timer used to refresh mappings
|
||||
deadline_timer m_refresh_timer;
|
||||
|
||||
bool m_disabled;
|
||||
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
std::ofstream m_log;
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
|
@ -36,12 +36,14 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include <vector>
|
||||
#include <bitset>
|
||||
#include <cassert>
|
||||
#include <utility>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push, 1)
|
||||
#endif
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/static_assert.hpp>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
|
@ -137,19 +139,15 @@ namespace libtorrent
|
|||
// (i.e. we don't have to maintain a refcount)
|
||||
void we_have(int index);
|
||||
|
||||
// This will mark a piece as unfiltered, and if it was
|
||||
// previously marked as filtered, it will be considered
|
||||
// interesting again and be placed in the piece list available
|
||||
// for downloading.
|
||||
void mark_as_unfiltered(int index);
|
||||
// sets the priority of a piece.
|
||||
// 0 is filtered, i.e. do not download
|
||||
// 1 is normal priority
|
||||
// 2 is high priority
|
||||
// 3 is maximum priority (availability is ignored)
|
||||
void set_piece_priority(int index, int prio);
|
||||
|
||||
// This will mark a piece as filtered. The piece will be
|
||||
// removed from the list of pieces avalable for downloading
|
||||
// and hence, will not be downloaded.
|
||||
void mark_as_filtered(int index);
|
||||
|
||||
// returns true if the pieces at 'index' is marked as filtered
|
||||
bool is_filtered(int index) const;
|
||||
// returns the priority for the piece at 'index'
|
||||
int piece_priority(int index) const;
|
||||
|
||||
// fills the bitmask with 1's for pieces that are filtered
|
||||
void filtered_pieces(std::vector<bool>& mask) const;
|
||||
|
@ -236,7 +234,7 @@ namespace libtorrent
|
|||
piece_pos(int peer_count_, int index_)
|
||||
: peer_count(peer_count_)
|
||||
, downloading(0)
|
||||
, filtered(0)
|
||||
, piece_priority(1)
|
||||
, index(index_)
|
||||
{
|
||||
assert(peer_count_ >= 0);
|
||||
|
@ -244,26 +242,52 @@ namespace libtorrent
|
|||
}
|
||||
|
||||
// selects which vector to look in
|
||||
unsigned peer_count : 11;
|
||||
unsigned peer_count : 10;
|
||||
// is 1 if the piece is marked as being downloaded
|
||||
unsigned downloading : 1;
|
||||
// is 1 if the piece is filtered (not to be downloaded)
|
||||
unsigned filtered : 1;
|
||||
// is 0 if the piece is filtered (not to be downloaded)
|
||||
// 1 is normal priority (default)
|
||||
// 2 is high priority
|
||||
// 3 is maximum priority (ignores availability)
|
||||
unsigned piece_priority : 2;
|
||||
// index in to the piece_info vector
|
||||
unsigned index : 19;
|
||||
|
||||
enum { we_have_index = 0x3ffff };
|
||||
enum
|
||||
{
|
||||
// index is set to this to indicate that we have the
|
||||
// piece. There is no entry for the piece in the
|
||||
// buckets if this is the case.
|
||||
we_have_index = 0x7ffff,
|
||||
// the priority value that means the piece is filtered
|
||||
filter_priority = 0,
|
||||
// the max number the peer count can hold
|
||||
max_peer_count = 0x3ff
|
||||
};
|
||||
|
||||
bool have() const { return index == we_have_index; }
|
||||
void set_have() { index = we_have_index; assert(have()); }
|
||||
|
||||
bool filtered() const { return piece_priority == filter_priority; }
|
||||
void filtered(bool f) { piece_priority = f ? filter_priority : 0; }
|
||||
|
||||
int priority(int limit) const
|
||||
{
|
||||
return peer_count >= (unsigned)limit ? limit : peer_count;
|
||||
if (filtered() || have()) return 0;
|
||||
// pieces we are currently downloading are prioritized
|
||||
int prio = downloading ? peer_count : peer_count * 2;
|
||||
// if the peer_count is 0 or 1, the priority cannot be higher
|
||||
if (prio <= 1) return prio;
|
||||
if (prio >= limit * 2) prio = limit * 2;
|
||||
// the different priority levels
|
||||
switch (piece_priority)
|
||||
{
|
||||
case 2: return prio - 1;
|
||||
case 3: return 1;
|
||||
}
|
||||
return prio;
|
||||
}
|
||||
|
||||
bool ordered(int limit) const
|
||||
{
|
||||
return peer_count >= (unsigned)limit;
|
||||
}
|
||||
|
||||
|
||||
bool operator!=(piece_pos p) const
|
||||
{ return index != p.index || peer_count != p.peer_count; }
|
||||
|
||||
|
@ -272,27 +296,23 @@ namespace libtorrent
|
|||
|
||||
};
|
||||
|
||||
BOOST_STATIC_ASSERT(sizeof(piece_pos) == sizeof(char) * 4);
|
||||
|
||||
bool is_ordered(int priority) const
|
||||
{
|
||||
return priority >= m_sequenced_download_threshold * 2;
|
||||
}
|
||||
|
||||
void add(int index);
|
||||
void move(bool downloading, bool filtered, int vec_index, int elem_index);
|
||||
void remove(bool downloading, bool filtered, int vec_index, int elem_index);
|
||||
std::vector<std::vector<int> >& pick_piece_info_vector(bool downloading
|
||||
, bool filtered);
|
||||
void move(int vec_index, int elem_index);
|
||||
// void remove(int vec_index, int elem_index);
|
||||
|
||||
std::vector<std::vector<int> > const& pick_piece_info_vector(
|
||||
bool downloading, bool filtered) const;
|
||||
|
||||
int add_interesting_blocks_free(const std::vector<int>& piece_list
|
||||
, const std::vector<bool>& pieces
|
||||
, std::vector<piece_block>& interesting_blocks
|
||||
, int num_blocks, bool prefer_whole_pieces) const;
|
||||
|
||||
int add_interesting_blocks_partial(const std::vector<int>& piece_list
|
||||
, const std::vector<bool>& pieces
|
||||
, std::vector<piece_block>& interesting_blocks
|
||||
, std::vector<piece_block>& backup_blocks
|
||||
, int num_blocks, bool prefer_whole_pieces
|
||||
, tcp::endpoint peer) const;
|
||||
int add_interesting_blocks(const std::vector<int>& piece_list
|
||||
, const std::vector<bool>& pieces
|
||||
, std::vector<piece_block>& interesting_blocks
|
||||
, std::vector<piece_block>& backup_blocks
|
||||
, int num_blocks, bool prefer_whole_pieces
|
||||
, tcp::endpoint peer) const;
|
||||
|
||||
|
||||
// this vector contains all pieces we don't have.
|
||||
|
@ -300,14 +320,11 @@ namespace libtorrent
|
|||
// that no peer have, the vector at index 1 contains
|
||||
// all pieces that exactly one peer have, index 2 contains
|
||||
// all pieces exactly two peers have and so on.
|
||||
// this is not entirely true. The availibility of a piece
|
||||
// is adjusted depending on its priority. But the principle
|
||||
// is that the higher index, the lower priority a piece has.
|
||||
std::vector<std::vector<int> > m_piece_info;
|
||||
|
||||
// this vector has the same structure as m_piece_info
|
||||
// but only contains pieces we are currently downloading
|
||||
// they have higher priority than pieces we aren't downloading
|
||||
// during piece picking
|
||||
std::vector<std::vector<int> > m_downloading_piece_info;
|
||||
|
||||
// this maps indices to number of peers that has this piece and
|
||||
// index into the m_piece_info vectors.
|
||||
// piece_pos::we_have_index means that we have the piece, so it
|
||||
|
|
|
@ -167,7 +167,7 @@ namespace libtorrent
|
|||
dht_settings()
|
||||
: max_peers_reply(50)
|
||||
, search_branching(5)
|
||||
, service_port(6881)
|
||||
, service_port(0)
|
||||
, max_fail_count(20)
|
||||
{}
|
||||
|
||||
|
@ -180,6 +180,7 @@ namespace libtorrent
|
|||
int search_branching;
|
||||
|
||||
// the listen port for the dht. This is a UDP port.
|
||||
// zero means use the same as the tcp interface
|
||||
int service_port;
|
||||
|
||||
// the maximum number of times a node can fail
|
||||
|
|
|
@ -34,8 +34,8 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#define TORRENT_VERSION_HPP_INCLUDED
|
||||
|
||||
#define LIBTORRENT_VERSION_MAJOR 0
|
||||
#define LIBTORRENT_VERSION_MINOR 12
|
||||
#define LIBTORRENT_VERSION_MINOR 13
|
||||
|
||||
#define LIBTORRENT_VERSION "0.12.0.0"
|
||||
#define LIBTORRENT_VERSION "0.13.0.0"
|
||||
|
||||
#endif
|
||||
|
|
|
@ -3,8 +3,8 @@ lib_LTLIBRARIES = libtorrent.la
|
|||
libtorrent_la_SOURCES = allocate_resources.cpp \
|
||||
bandwidth_manager.cpp entry.cpp escape_string.cpp \
|
||||
peer_connection.cpp bt_peer_connection.cpp web_peer_connection.cpp \
|
||||
piece_picker.cpp policy.cpp session.cpp session_impl.cpp sha1.cpp stat.cpp \
|
||||
storage.cpp torrent.cpp torrent_handle.cpp \
|
||||
natpmp.cpp piece_picker.cpp policy.cpp session.cpp session_impl.cpp sha1.cpp \
|
||||
stat.cpp storage.cpp torrent.cpp torrent_handle.cpp \
|
||||
torrent_info.cpp tracker_manager.cpp \
|
||||
http_tracker_connection.cpp udp_tracker_connection.cpp \
|
||||
alert.cpp identify_client.cpp ip_filter.cpp file.cpp metadata_transfer.cpp \
|
||||
|
@ -50,6 +50,7 @@ $(top_srcdir)/include/libtorrent/peer.hpp \
|
|||
$(top_srcdir)/include/libtorrent/peer_connection.hpp \
|
||||
$(top_srcdir)/include/libtorrent/bt_peer_connection.hpp \
|
||||
$(top_srcdir)/include/libtorrent/web_peer_connection.hpp \
|
||||
$(top_srcdir)/include/libtorrent/natpmp.hpp \
|
||||
$(top_srcdir)/include/libtorrent/peer_id.hpp \
|
||||
$(top_srcdir)/include/libtorrent/peer_info.hpp \
|
||||
$(top_srcdir)/include/libtorrent/peer_request.hpp \
|
||||
|
|
|
@ -163,7 +163,11 @@ namespace libtorrent
|
|||
void bt_peer_connection::write_dht_port(int listen_port)
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
#ifdef TORRENT_VERBOSE_LOGGING
|
||||
using namespace boost::posix_time;
|
||||
(*m_logger) << to_simple_string(second_clock::universal_time())
|
||||
<< " ==> DHT_PORT [ " << listen_port << " ]\n";
|
||||
#endif
|
||||
buffer::interval packet = allocate_send_buffer(7);
|
||||
detail::write_uint32(3, packet.begin);
|
||||
detail::write_uint8(msg_dht_port, packet.begin);
|
||||
|
@ -1287,7 +1291,7 @@ namespace libtorrent
|
|||
|
||||
#ifndef TORRENT_DISABLE_DHT
|
||||
if (m_supports_dht_port && m_ses.m_dht)
|
||||
write_dht_port(m_ses.kad_settings().service_port);
|
||||
write_dht_port(m_ses.get_dht_settings().service_port);
|
||||
#endif
|
||||
|
||||
m_client_version = identify_client(pid);
|
||||
|
|
|
@ -0,0 +1,324 @@
|
|||
#include <libtorrent/natpmp.hpp>
|
||||
#include <libtorrent/io.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
#include <asio/ip/host_name.hpp>
|
||||
|
||||
using boost::bind;
|
||||
using namespace libtorrent;
|
||||
using boost::posix_time::microsec_clock;
|
||||
|
||||
natpmp::natpmp(io_service& ios, portmap_callback_t const& cb)
|
||||
: m_callback(cb)
|
||||
, m_currently_mapping(-1)
|
||||
, m_retry_count(0)
|
||||
, m_socket(ios)
|
||||
, m_send_timer(ios)
|
||||
, m_refresh_timer(ios)
|
||||
, m_disabled(false)
|
||||
{
|
||||
m_mappings[0].protocol = 2; // tcp
|
||||
m_mappings[1].protocol = 1; // udp
|
||||
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
m_log.open("natpmp.log", std::ios::in | std::ios::out | std::ios::trunc);
|
||||
#endif
|
||||
|
||||
udp::resolver r(ios);
|
||||
udp::resolver::iterator i = r.resolve(udp::resolver::query(asio::ip::host_name(), "0"));
|
||||
for (;i != udp::resolver_iterator(); ++i)
|
||||
{
|
||||
if (i->endpoint().address().is_v4()) break;
|
||||
}
|
||||
|
||||
if (i == udp::resolver_iterator()) return;
|
||||
address_v4 local = i->endpoint().address().to_v4();
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
m_log << to_simple_string(microsec_clock::universal_time())
|
||||
<< " local ip: " << local.to_string() << std::endl;
|
||||
#endif
|
||||
|
||||
if ((local.to_ulong() & 0xff000000) != 0x0a000000
|
||||
&& (local.to_ulong() & 0xfff00000) != 0xac100000
|
||||
&& (local.to_ulong() & 0xffff0000) != 0xaca80000)
|
||||
{
|
||||
// the local address seems to be an external
|
||||
// internet address. Assume it is not behind a NAT
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
m_log << "not on a NAT. disable NAT-PMP" << std::endl;
|
||||
#endif
|
||||
m_disabled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// assume the router is located on the local
|
||||
// network as x.x.x.1
|
||||
// TODO: find a better way to figure out the router IP
|
||||
m_nat_endpoint = udp::endpoint(
|
||||
address_v4((local.to_ulong() & 0xffffff00) | 1), 5351);
|
||||
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
m_log << "assuming router is at: " << m_nat_endpoint.address().to_string() << std::endl;
|
||||
#endif
|
||||
|
||||
m_socket.open(udp::v4());
|
||||
m_socket.bind(udp::endpoint());
|
||||
}
|
||||
|
||||
void natpmp::set_mappings(int tcp, int udp)
|
||||
{
|
||||
if (m_disabled) return;
|
||||
update_mapping(0, tcp);
|
||||
update_mapping(1, udp);
|
||||
}
|
||||
|
||||
void natpmp::update_mapping(int i, int port)
|
||||
{
|
||||
natpmp::mapping& m = m_mappings[i];
|
||||
if (port <= 0) return;
|
||||
if (m.local_port != port)
|
||||
m.need_update = true;
|
||||
|
||||
m.local_port = port;
|
||||
// prefer the same external port as the local port
|
||||
if (m.external_port == 0) m.external_port = port;
|
||||
|
||||
if (m_currently_mapping == -1)
|
||||
{
|
||||
// the socket is not currently in use
|
||||
// send out a mapping request
|
||||
m_retry_count = 0;
|
||||
send_map_request(i);
|
||||
m_socket.async_receive_from(asio::buffer(&m_response_buffer, 16)
|
||||
, m_remote, bind(&natpmp::on_reply, this, _1, _2));
|
||||
}
|
||||
}
|
||||
|
||||
void natpmp::send_map_request(int i) try
|
||||
{
|
||||
using namespace libtorrent::detail;
|
||||
using boost::posix_time::milliseconds;
|
||||
|
||||
assert(m_currently_mapping == -1
|
||||
|| m_currently_mapping == i);
|
||||
m_currently_mapping = i;
|
||||
mapping& m = m_mappings[i];
|
||||
char buf[12];
|
||||
char* out = buf;
|
||||
write_uint8(0, out); // NAT-PMP version
|
||||
write_uint8(m.protocol, out); // map "protocol"
|
||||
write_uint16(0, out); // reserved
|
||||
write_uint16(m.local_port, out); // private port
|
||||
write_uint16(m.external_port, out); // requested public port
|
||||
int ttl = m.external_port == 0 ? 0 : 3600;
|
||||
write_uint32(ttl, out); // port mapping lifetime
|
||||
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
m_log << to_simple_string(microsec_clock::universal_time())
|
||||
<< " ==> port map request: " << (m.protocol == 1 ? "udp" : "tcp")
|
||||
<< " local: " << m.local_port << " external: " << m.external_port
|
||||
<< " ttl: " << ttl << std::endl;
|
||||
#endif
|
||||
|
||||
m_socket.send_to(asio::buffer(buf, 12), m_nat_endpoint);
|
||||
// linear back-off instead of exponential
|
||||
++m_retry_count;
|
||||
m_send_timer.expires_from_now(milliseconds(250 * m_retry_count));
|
||||
m_send_timer.async_wait(bind(&natpmp::resend_request, this, i, _1));
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::string err = e.what();
|
||||
}
|
||||
|
||||
void natpmp::resend_request(int i, asio::error_code const& e)
|
||||
{
|
||||
using boost::posix_time::hours;
|
||||
if (e) return;
|
||||
if (m_retry_count >= 9)
|
||||
{
|
||||
m_mappings[i].need_update = false;
|
||||
// try again in two hours
|
||||
m_mappings[i].expires
|
||||
= boost::posix_time::second_clock::universal_time() + hours(2);
|
||||
return;
|
||||
}
|
||||
send_map_request(i);
|
||||
}
|
||||
|
||||
void natpmp::on_reply(asio::error_code const& e
|
||||
, std::size_t bytes_transferred)
|
||||
{
|
||||
using namespace libtorrent::detail;
|
||||
using boost::posix_time::seconds;
|
||||
if (e) return;
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
if (m_remote != m_nat_endpoint)
|
||||
{
|
||||
m_socket.async_receive_from(asio::buffer(&m_response_buffer, 16)
|
||||
, m_remote, bind(&natpmp::on_reply, this, _1, _2));
|
||||
return;
|
||||
}
|
||||
|
||||
m_send_timer.cancel();
|
||||
|
||||
assert(m_currently_mapping >= 0);
|
||||
int i = m_currently_mapping;
|
||||
mapping& m = m_mappings[i];
|
||||
|
||||
char* in = m_response_buffer;
|
||||
int version = read_uint8(in);
|
||||
int cmd = read_uint8(in);
|
||||
int result = read_uint16(in);
|
||||
int time = read_uint32(in);
|
||||
int private_port = read_uint16(in);
|
||||
int public_port = read_uint16(in);
|
||||
int lifetime = read_uint32(in);
|
||||
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
m_log << to_simple_string(microsec_clock::universal_time())
|
||||
<< " <== port map response: " << (cmd - 128 == 1 ? "udp" : "tcp")
|
||||
<< " local: " << private_port << " external: " << public_port
|
||||
<< " ttl: " << lifetime << std::endl;
|
||||
#endif
|
||||
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
if (version != 0)
|
||||
{
|
||||
m_log << "*** unexpected version: " << version << std::endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
if (private_port != m.local_port)
|
||||
{
|
||||
m_log << "*** unexpected local port: " << private_port << std::endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
if (cmd != 128 + m.protocol)
|
||||
{
|
||||
m_log << "*** unexpected protocol: " << (cmd - 128) << std::endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (public_port == 0 || lifetime == 0)
|
||||
{
|
||||
// this means the mapping was
|
||||
// successfully closed
|
||||
m.local_port = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m.expires = boost::posix_time::second_clock::universal_time()
|
||||
+ seconds(int(lifetime * 0.7f));
|
||||
m.external_port = public_port;
|
||||
}
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
m_log << "*** ERROR: " << result << std::endl;
|
||||
#endif
|
||||
std::stringstream errmsg;
|
||||
errmsg << "NAT router reports error (" << result << ") ";
|
||||
switch (result)
|
||||
{
|
||||
case 1: errmsg << "Unsupported protocol version"; break;
|
||||
case 2: errmsg << "Not authorized to create port map (enable NAT-PMP on your router)"; break;
|
||||
case 3: errmsg << "Network failure"; break;
|
||||
case 4: errmsg << "Out of resources"; break;
|
||||
case 5: errmsg << "Unsupported opcpde"; break;
|
||||
}
|
||||
throw std::runtime_error(errmsg.str());
|
||||
}
|
||||
|
||||
int tcp_port = 0;
|
||||
int udp_port = 0;
|
||||
if (m.protocol == 1) udp_port = m.external_port;
|
||||
else tcp_port = public_port;
|
||||
m_callback(tcp_port, udp_port, "");
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
using boost::posix_time::hours;
|
||||
// try again in two hours
|
||||
m_mappings[m_currently_mapping].expires
|
||||
= boost::posix_time::second_clock::universal_time() + hours(2);
|
||||
m_callback(0, 0, e.what());
|
||||
}
|
||||
int i = m_currently_mapping;
|
||||
m_currently_mapping = -1;
|
||||
m_mappings[i].need_update = false;
|
||||
update_expiration_timer();
|
||||
try_next_mapping(i);
|
||||
}
|
||||
|
||||
void natpmp::update_expiration_timer()
|
||||
{
|
||||
using boost::posix_time::seconds;
|
||||
boost::posix_time::ptime now = boost::posix_time::second_clock::universal_time();
|
||||
boost::posix_time::ptime min_expire = now + seconds(3600);
|
||||
int min_index = -1;
|
||||
for (int i = 0; i < 2; ++i)
|
||||
if (m_mappings[i].expires < min_expire
|
||||
&& m_mappings[i].local_port != 0)
|
||||
{
|
||||
min_expire = m_mappings[i].expires;
|
||||
min_index = i;
|
||||
}
|
||||
|
||||
if (min_index >= 0)
|
||||
{
|
||||
m_refresh_timer.expires_from_now(min_expire - now);
|
||||
m_refresh_timer.async_wait(bind(&natpmp::mapping_expired, this, _1, min_index));
|
||||
}
|
||||
}
|
||||
|
||||
void natpmp::mapping_expired(asio::error_code const& e, int i)
|
||||
{
|
||||
if (e) return;
|
||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||
m_log << "*** mapping " << i << " expired, updating" << std::endl;
|
||||
#endif
|
||||
refresh_mapping(i);
|
||||
}
|
||||
|
||||
void natpmp::refresh_mapping(int i)
|
||||
{
|
||||
m_mappings[i].need_update = true;
|
||||
if (m_currently_mapping == -1)
|
||||
{
|
||||
// the socket is not currently in use
|
||||
// send out a mapping request
|
||||
m_retry_count = 0;
|
||||
send_map_request(i);
|
||||
m_socket.async_receive_from(asio::buffer(&m_response_buffer, 16)
|
||||
, m_remote, bind(&natpmp::on_reply, this, _1, _2));
|
||||
}
|
||||
}
|
||||
|
||||
void natpmp::try_next_mapping(int i)
|
||||
{
|
||||
++i;
|
||||
if (i >= 2) i = 0;
|
||||
if (m_mappings[i].need_update)
|
||||
refresh_mapping(i);
|
||||
}
|
||||
|
||||
void natpmp::close()
|
||||
{
|
||||
if (m_disabled) return;
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
if (m_mappings[i].local_port == 0)
|
||||
continue;
|
||||
m_mappings[i].external_port = 0;
|
||||
refresh_mapping(i);
|
||||
}
|
||||
}
|
||||
|
|
@ -226,30 +226,21 @@ namespace libtorrent
|
|||
|
||||
// build a vector of all pieces
|
||||
m_num_pieces = 0;
|
||||
std::vector<int> piece_list;
|
||||
for (int i = 0; i < (int)m_have_piece.size(); ++i)
|
||||
bool interesting = false;
|
||||
for (int i = 0; i < int(m_have_piece.size()); ++i)
|
||||
{
|
||||
if (m_have_piece[i])
|
||||
{
|
||||
++m_num_pieces;
|
||||
piece_list.push_back(i);
|
||||
t->peer_has(i);
|
||||
// if the peer has a piece and we don't, the peer is interesting
|
||||
if (!t->have_piece(i)
|
||||
&& !t->picker().piece_priority(i) == 0)
|
||||
interesting = true;
|
||||
}
|
||||
}
|
||||
|
||||
// let the torrent know which pieces the
|
||||
// peer has, in a shuffled order
|
||||
bool interesting = false;
|
||||
for (std::vector<int>::reverse_iterator i = piece_list.rbegin();
|
||||
i != piece_list.rend(); ++i)
|
||||
{
|
||||
int index = *i;
|
||||
t->peer_has(index);
|
||||
if (!t->have_piece(index)
|
||||
&& !t->picker().is_filtered(index))
|
||||
interesting = true;
|
||||
}
|
||||
|
||||
if (piece_list.size() == m_have_piece.size())
|
||||
if (m_num_pieces == int(m_have_piece.size()))
|
||||
{
|
||||
#ifdef TORRENT_VERBOSE_LOGGING
|
||||
(*m_logger) << " *** THIS IS A SEED ***\n";
|
||||
|
@ -654,7 +645,7 @@ namespace libtorrent
|
|||
if (!t->have_piece(index)
|
||||
&& !t->is_seed()
|
||||
&& !is_interesting()
|
||||
&& !t->picker().is_filtered(index))
|
||||
&& t->picker().piece_priority(index) != 0)
|
||||
t->get_policy().peer_is_interesting(*this);
|
||||
}
|
||||
|
||||
|
@ -714,8 +705,7 @@ namespace libtorrent
|
|||
m_have_piece[i] = true;
|
||||
++m_num_pieces;
|
||||
t->peer_has(i);
|
||||
if (!t->have_piece(i)
|
||||
&& !t->picker().is_filtered(i))
|
||||
if (!t->have_piece(i) && t->picker().piece_priority(i) != 0)
|
||||
interesting = true;
|
||||
}
|
||||
else if (!have && m_have_piece[i])
|
||||
|
|
|
@ -54,7 +54,6 @@ namespace libtorrent
|
|||
|
||||
piece_picker::piece_picker(int blocks_per_piece, int total_num_blocks)
|
||||
: m_piece_info(2)
|
||||
, m_downloading_piece_info(2)
|
||||
, m_piece_map((total_num_blocks + blocks_per_piece-1) / blocks_per_piece)
|
||||
, m_num_filtered(0)
|
||||
, m_num_have_filtered(0)
|
||||
|
@ -93,40 +92,17 @@ namespace libtorrent
|
|||
#ifndef NDEBUG
|
||||
m_files_checked_called = true;
|
||||
#endif
|
||||
// build a vector of all the pieces we don't have
|
||||
std::vector<int> piece_list;
|
||||
piece_list.reserve(std::count(pieces.begin(), pieces.end(), false));
|
||||
|
||||
for (std::vector<bool>::const_iterator i = pieces.begin();
|
||||
i != pieces.end(); ++i)
|
||||
{
|
||||
if (*i) continue;
|
||||
int index = static_cast<int>(i - pieces.begin());
|
||||
if (m_piece_map[index].filtered)
|
||||
m_piece_map[index].index = 0;
|
||||
if (m_piece_map[index].filtered())
|
||||
{
|
||||
++m_num_filtered;
|
||||
--m_num_have_filtered;
|
||||
m_piece_map[index].index = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
piece_list.push_back(index);
|
||||
}
|
||||
}
|
||||
|
||||
// add the pieces to the piece_picker
|
||||
for (std::vector<int>::reverse_iterator i = piece_list.rbegin();
|
||||
i != piece_list.rend(); ++i)
|
||||
{
|
||||
int index = *i;
|
||||
assert(index >= 0);
|
||||
assert(index < (int)m_piece_map.size());
|
||||
assert(m_piece_map[index].index == piece_pos::we_have_index);
|
||||
assert(m_piece_map[index].peer_count == 0);
|
||||
assert(m_piece_info.size() == 2);
|
||||
|
||||
add(index);
|
||||
assert(m_piece_map[index].index != piece_pos::we_have_index);
|
||||
}
|
||||
|
||||
// if we have fast resume info
|
||||
|
@ -160,6 +136,8 @@ namespace libtorrent
|
|||
if (sequenced_download_threshold == m_sequenced_download_threshold)
|
||||
return;
|
||||
|
||||
assert(sequenced_download_threshold > 0);
|
||||
|
||||
int old_limit = m_sequenced_download_threshold;
|
||||
m_sequenced_download_threshold = sequenced_download_threshold;
|
||||
|
||||
|
@ -169,9 +147,9 @@ namespace libtorrent
|
|||
if (i->priority(old_limit) != i->priority(m_sequenced_download_threshold))
|
||||
{
|
||||
piece_pos& p = *i;
|
||||
if (p.index == piece_pos::we_have_index) continue;
|
||||
int prev_priority = p.priority(old_limit);
|
||||
move(p.downloading, p.filtered, prev_priority, p.index);
|
||||
if (prev_priority == 0) continue;
|
||||
move(prev_priority, p.index);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -217,6 +195,8 @@ namespace libtorrent
|
|||
{
|
||||
assert(sizeof(piece_pos) == 4);
|
||||
|
||||
assert(m_piece_info.empty() || m_piece_info[0].empty());
|
||||
|
||||
if (t != 0)
|
||||
assert((int)m_piece_map.size() == t->torrent_file().num_pieces());
|
||||
|
||||
|
@ -226,7 +206,7 @@ namespace libtorrent
|
|||
i != m_piece_map.end(); ++i)
|
||||
{
|
||||
int index = static_cast<int>(i - m_piece_map.begin());
|
||||
if (i->filtered)
|
||||
if (i->filtered())
|
||||
{
|
||||
if (i->index != piece_pos::we_have_index)
|
||||
++num_filtered;
|
||||
|
@ -274,40 +254,27 @@ namespace libtorrent
|
|||
// make sure there's no entry
|
||||
// with this index. (there shouldn't
|
||||
// be since the piece_map is piece_pos::we_have_index)
|
||||
for (std::vector<std::vector<int> >::const_iterator i = m_piece_info.begin();
|
||||
i != m_piece_info.end(); ++i)
|
||||
for (int i = 0; i < int(m_piece_info.size()); ++i)
|
||||
{
|
||||
for (std::vector<int>::const_iterator j= i->begin();
|
||||
j != i->end(); ++j)
|
||||
for (int j = 0; j < int(m_piece_info[i].size()); ++j)
|
||||
{
|
||||
assert(*j != index);
|
||||
assert(m_piece_info[i][j] != index);
|
||||
}
|
||||
}
|
||||
|
||||
for (std::vector<std::vector<int> >::const_iterator i = m_downloading_piece_info.begin();
|
||||
i != m_downloading_piece_info.end(); ++i)
|
||||
{
|
||||
for (std::vector<int>::const_iterator j = i->begin();
|
||||
j != i->end(); ++j)
|
||||
{
|
||||
assert(*j != index);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else if (!i->filtered)
|
||||
else if (!i->filtered())
|
||||
{
|
||||
if (t != 0)
|
||||
assert(!t->have_piece(index));
|
||||
|
||||
const std::vector<std::vector<int> >& c_vec = pick_piece_info_vector(i->downloading, i->filtered);
|
||||
assert(i->priority(m_sequenced_download_threshold) < (int)c_vec.size());
|
||||
const std::vector<int>& vec = c_vec[i->priority(m_sequenced_download_threshold)];
|
||||
if (i->index >= vec.size())
|
||||
assert(i->priority(m_sequenced_download_threshold) < int(m_piece_info.size()));
|
||||
int prio = i->priority(m_sequenced_download_threshold);
|
||||
if (prio > 0)
|
||||
{
|
||||
assert(false);
|
||||
const std::vector<int>& vec = m_piece_info[prio];
|
||||
assert (i->index < vec.size());
|
||||
assert(vec[i->index] == index);
|
||||
}
|
||||
assert(vec[i->index] == index);
|
||||
}
|
||||
|
||||
std::vector<downloading_piece>::const_iterator down
|
||||
|
@ -330,6 +297,7 @@ namespace libtorrent
|
|||
|
||||
float piece_picker::distributed_copies() const
|
||||
{
|
||||
// TODO: this is completely broken now
|
||||
const float num_pieces = static_cast<float>(m_piece_map.size());
|
||||
|
||||
for (int i = 0; i < (int)m_piece_info.size(); ++i)
|
||||
|
@ -346,40 +314,25 @@ namespace libtorrent
|
|||
return 1.f;
|
||||
}
|
||||
|
||||
std::vector<std::vector<int> >& piece_picker::pick_piece_info_vector(
|
||||
bool downloading, bool filtered)
|
||||
{
|
||||
assert(!filtered);
|
||||
return downloading?m_downloading_piece_info:m_piece_info;
|
||||
}
|
||||
|
||||
std::vector<std::vector<int> > const& piece_picker::pick_piece_info_vector(
|
||||
bool downloading, bool filtered) const
|
||||
{
|
||||
assert(!filtered);
|
||||
return downloading?m_downloading_piece_info:m_piece_info;
|
||||
}
|
||||
|
||||
void piece_picker::add(int index)
|
||||
{
|
||||
assert(index >= 0);
|
||||
assert(index < (int)m_piece_map.size());
|
||||
assert(index < int(m_piece_map.size()));
|
||||
piece_pos& p = m_piece_map[index];
|
||||
assert(!p.filtered);
|
||||
|
||||
std::vector<std::vector<int> >& dst_vec = pick_piece_info_vector(
|
||||
p.downloading, p.filtered);
|
||||
assert(!p.filtered());
|
||||
assert(!p.have());
|
||||
|
||||
int priority = p.priority(m_sequenced_download_threshold);
|
||||
if ((int)dst_vec.size() <= priority)
|
||||
dst_vec.resize(priority + 1);
|
||||
assert(priority > 0);
|
||||
if (int(m_piece_info.size()) <= priority)
|
||||
m_piece_info.resize(priority + 1);
|
||||
|
||||
assert((int)dst_vec.size() > priority);
|
||||
assert(int(m_piece_info.size()) > priority);
|
||||
|
||||
if (p.ordered(m_sequenced_download_threshold))
|
||||
if (is_ordered(priority))
|
||||
{
|
||||
// the piece should be inserted ordered, not randomly
|
||||
std::vector<int>& v = dst_vec[priority];
|
||||
std::vector<int>& v = m_piece_info[priority];
|
||||
// assert(is_sorted(v.begin(), v.end()/*, std::greater<int>()*/));
|
||||
std::vector<int>::iterator i = std::lower_bound(v.begin(), v.end()
|
||||
, index/*, std::greater<int>()*/);
|
||||
|
@ -393,75 +346,63 @@ namespace libtorrent
|
|||
}
|
||||
// assert(is_sorted(v.begin(), v.end()/*, std::greater<int>()*/));
|
||||
}
|
||||
else if (dst_vec[priority].size() < 2)
|
||||
else if (m_piece_info[priority].size() < 2)
|
||||
{
|
||||
p.index = dst_vec[priority].size();
|
||||
dst_vec[priority].push_back(index);
|
||||
p.index = m_piece_info[priority].size();
|
||||
m_piece_info[priority].push_back(index);
|
||||
}
|
||||
else
|
||||
{
|
||||
// find a random position in the destination vector where we will place
|
||||
// this entry.
|
||||
int dst_index = rand() % dst_vec[priority].size();
|
||||
int dst_index = rand() % m_piece_info[priority].size();
|
||||
|
||||
// copy the entry at that position to the back
|
||||
m_piece_map[dst_vec[priority][dst_index]].index
|
||||
= dst_vec[priority].size();
|
||||
dst_vec[priority].push_back(dst_vec[priority][dst_index]);
|
||||
m_piece_map[m_piece_info[priority][dst_index]].index
|
||||
= m_piece_info[priority].size();
|
||||
m_piece_info[priority].push_back(m_piece_info[priority][dst_index]);
|
||||
|
||||
// and then replace the one at dst_index with the one we're moving.
|
||||
// this procedure is to make sure there's no ordering when pieces
|
||||
// are moved in sequenced order.
|
||||
p.index = dst_index;
|
||||
dst_vec[priority][p.index] = index;
|
||||
m_piece_info[priority][p.index] = index;
|
||||
}
|
||||
}
|
||||
|
||||
// will update the piece with the given properties (downloading, filtered,
|
||||
// priority, elem_index) to place it at the correct position in the
|
||||
// vectors.
|
||||
void piece_picker::move(bool downloading, bool filtered, int priority
|
||||
, int elem_index)
|
||||
// will update the piece with the given properties (priority, elem_index)
|
||||
// to place it at the correct position in the vectors.
|
||||
void piece_picker::move(int priority, int elem_index)
|
||||
{
|
||||
assert(!filtered);
|
||||
assert(priority >= 0);
|
||||
assert(priority > 0);
|
||||
assert(elem_index >= 0);
|
||||
assert(elem_index != piece_pos::we_have_index);
|
||||
std::vector<std::vector<int> >& src_vec(pick_piece_info_vector(
|
||||
downloading, filtered));
|
||||
assert(m_files_checked_called);
|
||||
|
||||
assert((int)src_vec.size() > priority);
|
||||
assert((int)src_vec[priority].size() > elem_index);
|
||||
assert(int(m_piece_info.size()) > priority);
|
||||
assert(int(m_piece_info[priority].size()) > elem_index);
|
||||
|
||||
int index = src_vec[priority][elem_index];
|
||||
int index = m_piece_info[priority][elem_index];
|
||||
// update the piece_map
|
||||
piece_pos& p = m_piece_map[index];
|
||||
int new_priority = p.priority(m_sequenced_download_threshold);
|
||||
|
||||
if (p.downloading == downloading
|
||||
&& p.filtered == filtered
|
||||
&& new_priority == priority)
|
||||
if (new_priority == priority) return;
|
||||
|
||||
if (int(m_piece_info.size()) <= new_priority
|
||||
&& new_priority > 0)
|
||||
{
|
||||
assert(p.ordered(m_sequenced_download_threshold));
|
||||
return;
|
||||
m_piece_info.resize(new_priority + 1);
|
||||
assert(int(m_piece_info.size()) > new_priority);
|
||||
}
|
||||
|
||||
std::vector<std::vector<int> >& dst_vec(pick_piece_info_vector(
|
||||
p.downloading, p.filtered));
|
||||
|
||||
assert(&dst_vec != &src_vec || new_priority != priority);
|
||||
|
||||
if ((int)dst_vec.size() <= new_priority)
|
||||
if (new_priority == 0)
|
||||
{
|
||||
dst_vec.resize(new_priority + 1);
|
||||
assert((int)dst_vec.size() > new_priority);
|
||||
// this means the piece should not have an entry
|
||||
}
|
||||
|
||||
if (p.ordered(m_sequenced_download_threshold))
|
||||
else if (is_ordered(new_priority))
|
||||
{
|
||||
// the piece should be inserted ordered, not randomly
|
||||
std::vector<int>& v = dst_vec[new_priority];
|
||||
std::vector<int>& v = m_piece_info[new_priority];
|
||||
// assert(is_sorted(v.begin(), v.end()/*, std::greater<int>()*/));
|
||||
std::vector<int>::iterator i = std::lower_bound(v.begin(), v.end()
|
||||
, index/*, std::greater<int>()*/);
|
||||
|
@ -475,35 +416,35 @@ namespace libtorrent
|
|||
}
|
||||
// assert(is_sorted(v.begin(), v.end()/*, std::greater<int>()*/));
|
||||
}
|
||||
else if (dst_vec[new_priority].size() < 2)
|
||||
else if (m_piece_info[new_priority].size() < 2)
|
||||
{
|
||||
p.index = dst_vec[new_priority].size();
|
||||
dst_vec[new_priority].push_back(index);
|
||||
p.index = m_piece_info[new_priority].size();
|
||||
m_piece_info[new_priority].push_back(index);
|
||||
}
|
||||
else
|
||||
{
|
||||
// find a random position in the destination vector where we will place
|
||||
// this entry.
|
||||
int dst_index = rand() % dst_vec[new_priority].size();
|
||||
int dst_index = rand() % m_piece_info[new_priority].size();
|
||||
|
||||
// copy the entry at that position to the back
|
||||
m_piece_map[dst_vec[new_priority][dst_index]].index
|
||||
= dst_vec[new_priority].size();
|
||||
dst_vec[new_priority].push_back(dst_vec[new_priority][dst_index]);
|
||||
m_piece_map[m_piece_info[new_priority][dst_index]].index
|
||||
= m_piece_info[new_priority].size();
|
||||
m_piece_info[new_priority].push_back(m_piece_info[new_priority][dst_index]);
|
||||
|
||||
// and then replace the one at dst_index with the one we're moving.
|
||||
// this procedure is to make sure there's no ordering when pieces
|
||||
// are moved in sequenced order.
|
||||
p.index = dst_index;
|
||||
dst_vec[new_priority][p.index] = index;
|
||||
m_piece_info[new_priority][p.index] = index;
|
||||
}
|
||||
assert(p.index < dst_vec[p.priority(m_sequenced_download_threshold)].size());
|
||||
assert(dst_vec[p.priority(m_sequenced_download_threshold)][p.index] == index);
|
||||
assert(new_priority == 0 || p.index < m_piece_info[p.priority(m_sequenced_download_threshold)].size());
|
||||
assert(new_priority == 0 || m_piece_info[p.priority(m_sequenced_download_threshold)][p.index] == index);
|
||||
|
||||
if (priority >= m_sequenced_download_threshold)
|
||||
if (is_ordered(priority))
|
||||
{
|
||||
// remove the element from the source vector and preserve the order
|
||||
std::vector<int>& v = src_vec[priority];
|
||||
std::vector<int>& v = m_piece_info[priority];
|
||||
v.erase(v.begin() + elem_index);
|
||||
for (std::vector<int>::iterator i = v.begin() + elem_index;
|
||||
i != v.end(); ++i)
|
||||
|
@ -516,44 +457,42 @@ namespace libtorrent
|
|||
{
|
||||
// this will remove elem from the source vector without
|
||||
// preserving order, but the order is random anyway
|
||||
int replace_index = src_vec[priority][elem_index] = src_vec[priority].back();
|
||||
int replace_index = m_piece_info[priority][elem_index] = m_piece_info[priority].back();
|
||||
if (index != replace_index)
|
||||
{
|
||||
// update the entry we moved from the back
|
||||
m_piece_map[replace_index].index = elem_index;
|
||||
|
||||
assert((int)src_vec[priority].size() > elem_index);
|
||||
assert(int(m_piece_info[priority].size()) > elem_index);
|
||||
// this may not necessarily be the case. If we've just updated the threshold and are updating
|
||||
// the piece map
|
||||
// assert((int)m_piece_map[replace_index].priority(m_sequenced_download_threshold) == priority);
|
||||
assert((int)m_piece_map[replace_index].index == elem_index);
|
||||
assert(src_vec[priority][elem_index] == replace_index);
|
||||
assert(int(m_piece_map[replace_index].index) == elem_index);
|
||||
assert(m_piece_info[priority][elem_index] == replace_index);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert((int)src_vec[priority].size() == elem_index+1);
|
||||
assert(int(m_piece_info[priority].size()) == elem_index+1);
|
||||
}
|
||||
|
||||
src_vec[priority].pop_back();
|
||||
m_piece_info[priority].pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
void piece_picker::remove(bool downloading, bool filtered, int priority
|
||||
, int elem_index)
|
||||
/*
|
||||
void piece_picker::remove(int priority, int elem_index)
|
||||
{
|
||||
assert(!filtered);
|
||||
assert(priority >= 0);
|
||||
assert(priority > 0);
|
||||
assert(elem_index >= 0);
|
||||
assert(m_files_checked_called);
|
||||
|
||||
std::vector<std::vector<int> >& src_vec(pick_piece_info_vector(downloading, filtered));
|
||||
assert(int(m_piece_info.size()) > priority);
|
||||
assert(int(m_piece_info[priority].size()) > elem_index);
|
||||
|
||||
assert((int)src_vec.size() > priority);
|
||||
assert((int)src_vec[priority].size() > elem_index);
|
||||
int index = m_piece_info[priority][elem_index];
|
||||
|
||||
int index = src_vec[priority][elem_index];
|
||||
piece_pos& p = m_piece_map[index];
|
||||
|
||||
if (downloading)
|
||||
if (p.downloading)
|
||||
{
|
||||
std::vector<downloading_piece>::iterator i
|
||||
= std::find_if(m_downloads.begin(),
|
||||
|
@ -562,12 +501,11 @@ namespace libtorrent
|
|||
assert(i != m_downloads.end());
|
||||
m_downloads.erase(i);
|
||||
}
|
||||
piece_pos& p = m_piece_map[index];
|
||||
|
||||
p.downloading = 0;
|
||||
if (p.ordered(m_sequenced_download_threshold))
|
||||
if (is_ordered(priority))
|
||||
{
|
||||
std::vector<int>& v = src_vec[priority];
|
||||
// assert(is_sorted(v.begin(), v.end()/*, std::greater<int>()*/));
|
||||
std::vector<int>& v = m_piece_info[priority];
|
||||
std::vector<int>::iterator i = v.begin() + elem_index;
|
||||
v.erase(i);
|
||||
i = v.begin() + elem_index;
|
||||
|
@ -576,20 +514,18 @@ namespace libtorrent
|
|||
--m_piece_map[*i].index;
|
||||
assert(v[m_piece_map[*i].index] == *i);
|
||||
}
|
||||
// assert(is_sorted(v.begin(), v.end()/*, std::greater<int>()*/));
|
||||
}
|
||||
else
|
||||
{
|
||||
// this will remove elem from the vector without
|
||||
// preserving order
|
||||
index = src_vec[priority][elem_index] = src_vec[priority].back();
|
||||
index = m_piece_info[priority][elem_index] = m_piece_info[priority].back();
|
||||
// update the entry we moved from the back
|
||||
if ((int)src_vec[priority].size() > elem_index+1)
|
||||
m_piece_map[index].index = elem_index;
|
||||
src_vec[priority].pop_back();
|
||||
m_piece_map[index].index = elem_index;
|
||||
m_piece_info[priority].pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
void piece_picker::restore_piece(int index)
|
||||
{
|
||||
TORRENT_PIECE_PICKER_INVARIANT_CHECK;
|
||||
|
@ -607,34 +543,39 @@ namespace libtorrent
|
|||
assert(i != m_downloads.end());
|
||||
m_downloads.erase(i);
|
||||
|
||||
m_piece_map[index].downloading = 0;
|
||||
piece_pos& p = m_piece_map[index];
|
||||
if (p.filtered) return;
|
||||
move(true, p.filtered, p.priority(m_sequenced_download_threshold), p.index);
|
||||
int priority = p.priority(m_sequenced_download_threshold);
|
||||
p.downloading = 0;
|
||||
move(priority, p.index);
|
||||
}
|
||||
|
||||
void piece_picker::inc_refcount(int i)
|
||||
{
|
||||
TORRENT_PIECE_PICKER_INVARIANT_CHECK;
|
||||
// TORRENT_PIECE_PICKER_INVARIANT_CHECK;
|
||||
assert(i >= 0);
|
||||
assert(i < (int)m_piece_map.size());
|
||||
assert(m_files_checked_called);
|
||||
|
||||
int index = m_piece_map[i].index;
|
||||
int prev_priority = m_piece_map[i].priority(m_sequenced_download_threshold);
|
||||
|
||||
assert(m_piece_map[i].peer_count < 2048);
|
||||
m_piece_map[i].peer_count++;
|
||||
assert(m_piece_map[i].peer_count != 0);
|
||||
|
||||
piece_pos& p = m_piece_map[i];
|
||||
int index = p.index;
|
||||
int prev_priority = p.priority(m_sequenced_download_threshold);
|
||||
|
||||
assert(p.peer_count < piece_pos::max_peer_count);
|
||||
p.peer_count++;
|
||||
assert(p.peer_count != 0);
|
||||
|
||||
// if we have the piece or if it's filtered
|
||||
// we don't have to move any entries in the piece_info vector
|
||||
if (index == piece_pos::we_have_index || p.filtered
|
||||
|| p.priority(m_sequenced_download_threshold) == prev_priority) return;
|
||||
if (p.priority(m_sequenced_download_threshold) == prev_priority) return;
|
||||
|
||||
move(p.downloading, p.filtered, prev_priority, index);
|
||||
if (prev_priority == 0)
|
||||
{
|
||||
add(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
move(prev_priority, index);
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
// integrity_check();
|
||||
|
@ -644,25 +585,23 @@ namespace libtorrent
|
|||
|
||||
void piece_picker::dec_refcount(int i)
|
||||
{
|
||||
TORRENT_PIECE_PICKER_INVARIANT_CHECK;
|
||||
// TORRENT_PIECE_PICKER_INVARIANT_CHECK;
|
||||
|
||||
assert(m_files_checked_called);
|
||||
assert(i >= 0);
|
||||
assert(i < (int)m_piece_map.size());
|
||||
|
||||
int prev_priority = m_piece_map[i].priority(m_sequenced_download_threshold);
|
||||
int index = m_piece_map[i].index;
|
||||
assert(m_piece_map[i].peer_count > 0);
|
||||
|
||||
if (m_piece_map[i].peer_count > 0)
|
||||
m_piece_map[i].peer_count--;
|
||||
|
||||
piece_pos& p = m_piece_map[i];
|
||||
int prev_priority = p.priority(m_sequenced_download_threshold);
|
||||
int index = p.index;
|
||||
assert(p.peer_count > 0);
|
||||
|
||||
if (index == piece_pos::we_have_index || p.filtered
|
||||
|| p.priority(m_sequenced_download_threshold) == prev_priority) return;
|
||||
if (p.peer_count > 0)
|
||||
p.peer_count--;
|
||||
|
||||
move(p.downloading, p.filtered, prev_priority, index);
|
||||
if (p.priority(m_sequenced_download_threshold) == prev_priority) return;
|
||||
|
||||
move(prev_priority, index);
|
||||
}
|
||||
|
||||
// this is used to indicate that we succesfully have
|
||||
|
@ -675,46 +614,92 @@ namespace libtorrent
|
|||
assert(index >= 0);
|
||||
assert(index < (int)m_piece_map.size());
|
||||
|
||||
int info_index = m_piece_map[index].index;
|
||||
int priority = m_piece_map[index].priority(m_sequenced_download_threshold);
|
||||
|
||||
assert(m_piece_map[index].downloading == 1);
|
||||
|
||||
assert(info_index != piece_pos::we_have_index);
|
||||
piece_pos& p = m_piece_map[index];
|
||||
if (p.filtered)
|
||||
int info_index = p.index;
|
||||
int priority = p.priority(m_sequenced_download_threshold);
|
||||
|
||||
assert(p.downloading == 1);
|
||||
assert(!p.have());
|
||||
if (p.filtered())
|
||||
{
|
||||
--m_num_filtered;
|
||||
++m_num_have_filtered;
|
||||
return;
|
||||
}
|
||||
if (info_index == piece_pos::we_have_index) return;
|
||||
remove(p.downloading, p.filtered, priority, info_index);
|
||||
p.index = piece_pos::we_have_index;
|
||||
if (p.have()) return;
|
||||
p.set_have();
|
||||
if (p.downloading)
|
||||
{
|
||||
std::vector<downloading_piece>::iterator i
|
||||
= std::find_if(m_downloads.begin(),
|
||||
m_downloads.end(),
|
||||
has_index(index));
|
||||
assert(i != m_downloads.end());
|
||||
m_downloads.erase(i);
|
||||
p.downloading = 0;
|
||||
}
|
||||
assert(p.priority(m_sequenced_download_threshold) == 0);
|
||||
move(priority, info_index);
|
||||
}
|
||||
|
||||
|
||||
void piece_picker::mark_as_filtered(int index)
|
||||
void piece_picker::set_piece_priority(int index, int new_piece_priority)
|
||||
{
|
||||
TORRENT_PIECE_PICKER_INVARIANT_CHECK;
|
||||
assert(new_piece_priority >= 0);
|
||||
assert(new_piece_priority <= 3);
|
||||
assert(index >= 0);
|
||||
assert(index < (int)m_piece_map.size());
|
||||
|
||||
piece_pos& p = m_piece_map[index];
|
||||
if (p.filtered == 1) return;
|
||||
p.filtered = 1;
|
||||
if (p.index != piece_pos::we_have_index)
|
||||
|
||||
// if the priority isn't changed, don't do anything
|
||||
if (new_piece_priority == int(p.piece_priority)) return;
|
||||
|
||||
if (new_piece_priority == piece_pos::filter_priority
|
||||
&& p.piece_priority != piece_pos::filter_priority)
|
||||
{
|
||||
++m_num_filtered;
|
||||
remove(p.downloading, false, p.priority(m_sequenced_download_threshold), p.index);
|
||||
assert(p.filtered == 1);
|
||||
// the piece just got filtered
|
||||
if (p.have()) ++m_num_have_filtered;
|
||||
else ++m_num_filtered;
|
||||
|
||||
if (p.downloading)
|
||||
{
|
||||
std::vector<downloading_piece>::iterator i
|
||||
= std::find_if(m_downloads.begin(),
|
||||
m_downloads.end(),
|
||||
has_index(index));
|
||||
assert(i != m_downloads.end());
|
||||
m_downloads.erase(i);
|
||||
}
|
||||
p.downloading = 0;
|
||||
}
|
||||
else if (new_piece_priority != piece_pos::filter_priority
|
||||
&& p.piece_priority == piece_pos::filter_priority)
|
||||
{
|
||||
// the piece just got unfiltered
|
||||
if (p.have()) --m_num_have_filtered;
|
||||
else --m_num_filtered;
|
||||
}
|
||||
assert(m_num_filtered >= 0);
|
||||
assert(m_num_have_filtered >= 0);
|
||||
|
||||
int prev_priority = p.priority(m_sequenced_download_threshold);
|
||||
p.piece_priority = new_piece_priority;
|
||||
int new_priority = p.priority(m_sequenced_download_threshold);
|
||||
|
||||
if (new_priority == prev_priority) return;
|
||||
|
||||
if (prev_priority == 0)
|
||||
{
|
||||
add(index);
|
||||
}
|
||||
else
|
||||
{
|
||||
++m_num_have_filtered;
|
||||
move(prev_priority, p.index);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// this function can be used for pieces that we don't
|
||||
// have, but have marked as filtered (so we didn't
|
||||
// want to download them) but later want to enable for
|
||||
|
@ -727,8 +712,8 @@ namespace libtorrent
|
|||
assert(index < (int)m_piece_map.size());
|
||||
|
||||
piece_pos& p = m_piece_map[index];
|
||||
if (p.filtered == 0) return;
|
||||
p.filtered = 0;
|
||||
if (!p.filtered()) return;
|
||||
p.filtered(false);
|
||||
if (p.index != piece_pos::we_have_index)
|
||||
{
|
||||
--m_num_filtered;
|
||||
|
@ -741,13 +726,13 @@ namespace libtorrent
|
|||
assert(m_num_have_filtered >= 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool piece_picker::is_filtered(int index) const
|
||||
*/
|
||||
int piece_picker::piece_priority(int index) const
|
||||
{
|
||||
assert(index >= 0);
|
||||
assert(index < (int)m_piece_map.size());
|
||||
|
||||
return m_piece_map[index].filtered == 1;
|
||||
return m_piece_map[index].piece_priority;
|
||||
}
|
||||
|
||||
void piece_picker::filtered_pieces(std::vector<bool>& mask) const
|
||||
|
@ -757,7 +742,7 @@ namespace libtorrent
|
|||
for (std::vector<piece_pos>::const_iterator i = m_piece_map.begin(),
|
||||
end(m_piece_map.end()); i != end; ++i, ++j)
|
||||
{
|
||||
*j = i->filtered == 1;
|
||||
*j = i->filtered();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -781,59 +766,29 @@ namespace libtorrent
|
|||
// pieces that 0 other peers has.
|
||||
std::vector<std::vector<int> >::const_iterator free
|
||||
= m_piece_info.begin() + 1;
|
||||
assert(m_downloading_piece_info.begin()
|
||||
!= m_downloading_piece_info.end());
|
||||
|
||||
std::vector<std::vector<int> >::const_iterator partial
|
||||
= m_downloading_piece_info.begin() + 1;
|
||||
|
||||
std::vector<piece_block> backup_blocks;
|
||||
|
||||
// this loop will loop from pieces with 1 peer and up
|
||||
// this loop will loop from pieces with priority 1 and up
|
||||
// until we either reach the end of the piece list or
|
||||
// has filled the interesting_blocks with num_blocks
|
||||
// blocks.
|
||||
|
||||
// it iterates over two ranges simultaneously. The pieces that are
|
||||
// partially downloaded or partially requested, and the pieces that
|
||||
// hasn't been requested at all. The default is to prioritize pieces
|
||||
// that are partially requested/downloaded, so the loop will first
|
||||
// look for blocks among those pieces. And it will also take two steps
|
||||
// in that range when iterating. This has the effect that partial pieces
|
||||
// doesn't have to be as rare as non-requested pieces in order to be
|
||||
// prefered.
|
||||
|
||||
|
||||
// When prefer_whole_pieces is set (usually set when downloading from
|
||||
// fast peers) the partial pieces will not be prioritized, but actually
|
||||
// ignored as long as possible.
|
||||
|
||||
while((free != m_piece_info.end())
|
||||
|| (partial != m_downloading_piece_info.end()))
|
||||
while (free != m_piece_info.end())
|
||||
{
|
||||
if (partial != m_downloading_piece_info.end())
|
||||
{
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
num_blocks = add_interesting_blocks_partial(*partial, pieces
|
||||
, interesting_blocks, backup_blocks, num_blocks
|
||||
, prefer_whole_pieces, peer);
|
||||
assert(num_blocks >= 0);
|
||||
if (num_blocks == 0) return;
|
||||
++partial;
|
||||
if (partial == m_downloading_piece_info.end()) break;
|
||||
}
|
||||
}
|
||||
|
||||
if (free != m_piece_info.end())
|
||||
{
|
||||
num_blocks = add_interesting_blocks_free(*free, pieces
|
||||
, interesting_blocks, num_blocks, prefer_whole_pieces);
|
||||
assert(num_blocks >= 0);
|
||||
if (num_blocks == 0) return;
|
||||
++free;
|
||||
}
|
||||
num_blocks = add_interesting_blocks(*free, pieces
|
||||
, interesting_blocks, backup_blocks, num_blocks
|
||||
, prefer_whole_pieces, peer);
|
||||
assert(num_blocks >= 0);
|
||||
if (num_blocks == 0) return;
|
||||
++free;
|
||||
}
|
||||
|
||||
// TODO: what's up with this?
|
||||
if (!prefer_whole_pieces) return;
|
||||
assert(num_blocks > 0);
|
||||
|
||||
|
@ -867,112 +822,90 @@ namespace libtorrent
|
|||
}
|
||||
}
|
||||
|
||||
int piece_picker::add_interesting_blocks_free(std::vector<int> const& piece_list
|
||||
int piece_picker::add_interesting_blocks(std::vector<int> const& piece_list
|
||||
, std::vector<bool> const& pieces
|
||||
, std::vector<piece_block>& interesting_blocks
|
||||
, int num_blocks, bool prefer_whole_pieces) const
|
||||
{
|
||||
for (std::vector<int>::const_iterator i = piece_list.begin();
|
||||
i != piece_list.end(); ++i)
|
||||
{
|
||||
assert(*i >= 0);
|
||||
assert(*i < (int)m_piece_map.size());
|
||||
assert(m_piece_map[*i].downloading == 0);
|
||||
|
||||
// if the peer doesn't have the piece
|
||||
// skip it
|
||||
if (!pieces[*i]) continue;
|
||||
|
||||
int piece_blocks = blocks_in_piece(*i);
|
||||
if (!prefer_whole_pieces && piece_blocks > num_blocks)
|
||||
piece_blocks = num_blocks;
|
||||
for (int j = 0; j < piece_blocks; ++j)
|
||||
{
|
||||
interesting_blocks.push_back(piece_block(*i, j));
|
||||
}
|
||||
num_blocks -= (std::min)(piece_blocks, num_blocks);
|
||||
assert(num_blocks >= 0);
|
||||
if (num_blocks == 0) return num_blocks;
|
||||
}
|
||||
return num_blocks;
|
||||
}
|
||||
|
||||
int piece_picker::add_interesting_blocks_partial(std::vector<int> const& piece_list
|
||||
, const std::vector<bool>& pieces
|
||||
, std::vector<piece_block>& interesting_blocks
|
||||
, std::vector<piece_block>& backup_blocks
|
||||
, int num_blocks, bool prefer_whole_pieces
|
||||
, tcp::endpoint peer) const
|
||||
{
|
||||
assert(num_blocks > 0);
|
||||
|
||||
for (std::vector<int>::const_iterator i = piece_list.begin();
|
||||
i != piece_list.end(); ++i)
|
||||
{
|
||||
assert(*i >= 0);
|
||||
assert(*i < (int)m_piece_map.size());
|
||||
|
||||
// if the peer doesn't have the piece
|
||||
// skip it
|
||||
if (!pieces[*i]) continue;
|
||||
|
||||
assert(m_piece_map[*i].downloading == 1);
|
||||
|
||||
// calculate the number of blocks in this
|
||||
// piece. It's always m_blocks_per_piece, except
|
||||
// in the last piece.
|
||||
int num_blocks_in_piece = blocks_in_piece(*i);
|
||||
|
||||
std::vector<downloading_piece>::const_iterator p
|
||||
= std::find_if(m_downloads.begin(), m_downloads.end(), has_index(*i));
|
||||
assert(p != m_downloads.end());
|
||||
|
||||
// this means that this partial piece has
|
||||
// been downloaded/requested partially from
|
||||
// another peer that isn't us. And since
|
||||
// we prefer whole pieces, add this piece's
|
||||
// blocks to the backup list. If the prioritized
|
||||
// blocks aren't enough, blocks from this list
|
||||
// will be picked.
|
||||
if (prefer_whole_pieces
|
||||
&& !exclusively_requested_from(*p, num_blocks_in_piece, peer))
|
||||
if (m_piece_map[*i].downloading == 1)
|
||||
{
|
||||
if ((int)backup_blocks.size() >= num_blocks) continue;
|
||||
std::vector<downloading_piece>::const_iterator p
|
||||
= std::find_if(m_downloads.begin(), m_downloads.end(), has_index(*i));
|
||||
assert(p != m_downloads.end());
|
||||
|
||||
// this means that this partial piece has
|
||||
// been downloaded/requested partially from
|
||||
// another peer that isn't us. And since
|
||||
// we prefer whole pieces, add this piece's
|
||||
// blocks to the backup list. If the prioritized
|
||||
// blocks aren't enough, blocks from this list
|
||||
// will be picked.
|
||||
if (prefer_whole_pieces
|
||||
&& !exclusively_requested_from(*p, num_blocks_in_piece, peer))
|
||||
{
|
||||
if ((int)backup_blocks.size() >= num_blocks) continue;
|
||||
for (int j = 0; j < num_blocks_in_piece; ++j)
|
||||
{
|
||||
if (p->finished_blocks[j] == 1) continue;
|
||||
if (p->requested_blocks[j] == 1
|
||||
&& p->info[j].peer == peer) continue;
|
||||
backup_blocks.push_back(piece_block(*i, j));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int j = 0; j < num_blocks_in_piece; ++j)
|
||||
{
|
||||
if (p->finished_blocks[j] == 1) continue;
|
||||
if (p->requested_blocks[j] == 1
|
||||
&& p->info[j].peer == peer) continue;
|
||||
backup_blocks.push_back(piece_block(*i, j));
|
||||
// this block is interesting (we don't have it
|
||||
// yet). But it may already have been requested
|
||||
// from another peer. We have to add it anyway
|
||||
// to allow the requester to determine if the
|
||||
// block should be requested from more than one
|
||||
// peer. If it is being downloaded, we continue
|
||||
// to look for blocks until we have num_blocks
|
||||
// blocks that have not been requested from any
|
||||
// other peer.
|
||||
interesting_blocks.push_back(piece_block(*i, j));
|
||||
if (p->requested_blocks[j] == 0)
|
||||
{
|
||||
// we have found a block that's free to download
|
||||
num_blocks--;
|
||||
if (prefer_whole_pieces) continue;
|
||||
assert(num_blocks >= 0);
|
||||
if (num_blocks == 0) return num_blocks;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
assert(num_blocks >= 0 || prefer_whole_pieces);
|
||||
if (num_blocks < 0) num_blocks = 0;
|
||||
}
|
||||
|
||||
for (int j = 0; j < num_blocks_in_piece; ++j)
|
||||
else
|
||||
{
|
||||
if (p->finished_blocks[j] == 1) continue;
|
||||
if (p->requested_blocks[j] == 1
|
||||
&& p->info[j].peer == peer) continue;
|
||||
// this block is interesting (we don't have it
|
||||
// yet). But it may already have been requested
|
||||
// from another peer. We have to add it anyway
|
||||
// to allow the requester to determine if the
|
||||
// block should be requested from more than one
|
||||
// peer. If it is being downloaded, we continue
|
||||
// to look for blocks until we have num_blocks
|
||||
// blocks that have not been requested from any
|
||||
// other peer.
|
||||
interesting_blocks.push_back(piece_block(*i, j));
|
||||
if (p->requested_blocks[j] == 0)
|
||||
if (!prefer_whole_pieces && num_blocks_in_piece > num_blocks)
|
||||
num_blocks_in_piece = num_blocks;
|
||||
for (int j = 0; j < num_blocks_in_piece; ++j)
|
||||
{
|
||||
// we have found a block that's free to download
|
||||
num_blocks--;
|
||||
if (prefer_whole_pieces) continue;
|
||||
assert(num_blocks >= 0);
|
||||
if (num_blocks == 0) return num_blocks;
|
||||
interesting_blocks.push_back(piece_block(*i, j));
|
||||
}
|
||||
num_blocks -= (std::min)(num_blocks_in_piece, num_blocks);
|
||||
}
|
||||
assert(num_blocks >= 0 || prefer_whole_pieces);
|
||||
if (num_blocks < 0) num_blocks = 0;
|
||||
assert(num_blocks >= 0);
|
||||
if (num_blocks == 0) return num_blocks;
|
||||
}
|
||||
return num_blocks;
|
||||
|
@ -1046,8 +979,9 @@ namespace libtorrent
|
|||
piece_pos& p = m_piece_map[block.piece_index];
|
||||
if (p.downloading == 0)
|
||||
{
|
||||
int prio = p.priority(m_sequenced_download_threshold);
|
||||
p.downloading = 1;
|
||||
move(false, p.filtered, p.priority(m_sequenced_download_threshold), p.index);
|
||||
move(prio, p.index);
|
||||
|
||||
downloading_piece dp;
|
||||
dp.index = block.piece_index;
|
||||
|
@ -1076,12 +1010,13 @@ namespace libtorrent
|
|||
assert(block.block_index < blocks_in_piece(block.piece_index));
|
||||
|
||||
piece_pos& p = m_piece_map[block.piece_index];
|
||||
if (p.index == piece_pos::we_have_index || p.filtered) return;
|
||||
int prio = p.priority(m_sequenced_download_threshold);
|
||||
if (prio == 0) return;
|
||||
|
||||
if (p.downloading == 0)
|
||||
{
|
||||
p.downloading = 1;
|
||||
move(false, p.filtered, p.priority(m_sequenced_download_threshold), p.index);
|
||||
move(prio, p.index);
|
||||
|
||||
downloading_piece dp;
|
||||
dp.index = block.piece_index;
|
||||
|
@ -1203,9 +1138,10 @@ namespace libtorrent
|
|||
if (i->requested_blocks.count() == 0)
|
||||
{
|
||||
m_downloads.erase(i);
|
||||
m_piece_map[block.piece_index].downloading = 0;
|
||||
piece_pos& p = m_piece_map[block.piece_index];
|
||||
move(true, p.filtered, p.priority(m_sequenced_download_threshold), p.index);
|
||||
int prio = p.priority(m_sequenced_download_threshold);
|
||||
p.downloading = 0;
|
||||
move(prio, p.index);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -480,6 +480,7 @@ namespace libtorrent { namespace detail
|
|||
, m_tracker_manager(m_settings)
|
||||
, m_listen_port_range(listen_port_range)
|
||||
, m_listen_interface(address::from_string(listen_interface), listen_port_range.first)
|
||||
, m_external_listen_port(0)
|
||||
, m_abort(false)
|
||||
, m_max_uploads(-1)
|
||||
, m_max_connections(-1)
|
||||
|
@ -487,6 +488,11 @@ namespace libtorrent { namespace detail
|
|||
, m_incoming_connection(false)
|
||||
, m_files(40)
|
||||
, m_last_tick(microsec_clock::universal_time())
|
||||
#ifndef TORRENT_DISABLE_DHT
|
||||
, m_dht_same_port(true)
|
||||
, m_external_udp_port(0)
|
||||
#endif
|
||||
, m_natpmp(m_io_service, bind(&session_impl::on_port_mapping, this, _1, _2, _3))
|
||||
, m_timer(m_io_service)
|
||||
, m_checker_impl(*this)
|
||||
{
|
||||
|
@ -619,6 +625,7 @@ namespace libtorrent { namespace detail
|
|||
m_listen_socket->open(m_listen_interface.protocol());
|
||||
m_listen_socket->bind(m_listen_interface);
|
||||
m_listen_socket->listen();
|
||||
m_external_listen_port = m_listen_interface.port();
|
||||
break;
|
||||
}
|
||||
catch (asio::system_error& e)
|
||||
|
@ -672,9 +679,11 @@ namespace libtorrent { namespace detail
|
|||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
if (m_listen_socket)
|
||||
{
|
||||
(*m_logger) << "listening on port: " << m_listen_interface.port() << "\n";
|
||||
(*m_logger) << "listening on port: " << m_listen_interface.port()
|
||||
<< " external port: " << m_external_listen_port << "\n";
|
||||
}
|
||||
#endif
|
||||
m_natpmp.set_mappings(m_listen_interface.port(), 0);
|
||||
if (m_listen_socket) async_accept();
|
||||
}
|
||||
|
||||
|
@ -956,7 +965,8 @@ namespace libtorrent { namespace detail
|
|||
if (t.should_request())
|
||||
{
|
||||
tracker_request req = t.generate_tracker_request();
|
||||
req.listen_port = m_listen_interface.port();
|
||||
assert(m_external_listen_port > 0);
|
||||
req.listen_port = m_external_listen_port;
|
||||
req.key = m_key;
|
||||
m_tracker_manager.queue_request(m_strand, req, t.tracker_login()
|
||||
, m_listen_interface.address(), i->second);
|
||||
|
@ -1064,6 +1074,8 @@ namespace libtorrent { namespace detail
|
|||
while (!m_abort);
|
||||
|
||||
deadline_timer tracker_timer(m_io_service);
|
||||
// this will remove the port mappings
|
||||
m_natpmp.close();
|
||||
|
||||
session_impl::mutex_t::scoped_lock l(m_mutex);
|
||||
|
||||
|
@ -1080,7 +1092,8 @@ namespace libtorrent { namespace detail
|
|||
&& !i->second->trackers().empty())
|
||||
{
|
||||
tracker_request req = i->second->generate_tracker_request();
|
||||
req.listen_port = m_listen_interface.port();
|
||||
assert(m_external_listen_port > 0);
|
||||
req.listen_port = m_external_listen_port;
|
||||
req.key = m_key;
|
||||
std::string login = i->second->tracker_login();
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
|
@ -1368,7 +1381,8 @@ namespace libtorrent { namespace detail
|
|||
{
|
||||
tracker_request req = t.generate_tracker_request();
|
||||
assert(req.event == tracker_request::stopped);
|
||||
req.listen_port = m_listen_interface.port();
|
||||
assert(m_external_listen_port > 0);
|
||||
req.listen_port = m_external_listen_port;
|
||||
req.key = m_key;
|
||||
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
|
@ -1434,12 +1448,16 @@ namespace libtorrent { namespace detail
|
|||
m_listen_socket.reset();
|
||||
|
||||
#ifndef TORRENT_DISABLE_DHT
|
||||
if (m_listen_interface.address() != new_interface.address()
|
||||
if ((m_listen_interface.address() != new_interface.address()
|
||||
|| m_dht_same_port)
|
||||
&& m_dht)
|
||||
{
|
||||
if (m_dht_same_port)
|
||||
m_dht_settings.service_port = new_interface.port();
|
||||
// the listen interface changed, rebind the dht listen socket as well
|
||||
m_dht->rebind(new_interface.address()
|
||||
, m_dht_settings.service_port);
|
||||
m_natpmp.set_mappings(0, m_dht_settings.service_port);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -1461,7 +1479,31 @@ namespace libtorrent { namespace detail
|
|||
unsigned short session_impl::listen_port() const
|
||||
{
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
return m_listen_interface.port();
|
||||
return m_external_listen_port;
|
||||
}
|
||||
|
||||
void session_impl::on_port_mapping(int tcp_port, int udp_port
|
||||
, std::string const& errmsg)
|
||||
{
|
||||
#ifndef TORRENT_DISABLE_DHT
|
||||
if (udp_port != 0)
|
||||
{
|
||||
m_external_udp_port = udp_port;
|
||||
m_dht_settings.service_port = udp_port;
|
||||
// TODO: generate successful port map alert
|
||||
}
|
||||
#endif
|
||||
|
||||
if (tcp_port != 0)
|
||||
{
|
||||
m_external_listen_port = tcp_port;
|
||||
// TODO: generate successful port map alert
|
||||
}
|
||||
|
||||
if (!errmsg.empty())
|
||||
{
|
||||
// TODO: generate port map failure alert
|
||||
}
|
||||
}
|
||||
|
||||
session_status session_impl::status() const
|
||||
|
@ -1512,6 +1554,11 @@ namespace libtorrent { namespace detail
|
|||
m_dht->stop();
|
||||
m_dht = 0;
|
||||
}
|
||||
if (m_dht_settings.service_port == 0)
|
||||
m_dht_same_port = true;
|
||||
m_dht_settings.service_port = m_listen_interface.port();
|
||||
m_external_udp_port = m_dht_settings.service_port;
|
||||
m_natpmp.set_mappings(0, m_dht_settings.service_port);
|
||||
m_dht = new dht::dht_tracker(m_io_service
|
||||
, m_dht_settings, m_listen_interface.address()
|
||||
, startup_state);
|
||||
|
@ -1528,13 +1575,23 @@ namespace libtorrent { namespace detail
|
|||
void session_impl::set_dht_settings(dht_settings const& settings)
|
||||
{
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
if (settings.service_port != m_dht_settings.service_port
|
||||
// only change the dht listen port in case the settings
|
||||
// contains a vaiid port, and if it is different from
|
||||
// the current setting
|
||||
if (settings.service_port != 0)
|
||||
m_dht_same_port = false;
|
||||
if (!m_dht_same_port
|
||||
&& settings.service_port != m_dht_settings.service_port
|
||||
&& m_dht)
|
||||
{
|
||||
m_dht->rebind(m_listen_interface.address()
|
||||
, settings.service_port);
|
||||
m_natpmp.set_mappings(0, m_dht_settings.service_port);
|
||||
m_external_udp_port = settings.service_port;
|
||||
}
|
||||
m_dht_settings = settings;
|
||||
if (m_dht_same_port)
|
||||
m_dht_settings.service_port = m_listen_interface.port();
|
||||
}
|
||||
|
||||
entry session_impl::dht_state() const
|
||||
|
|
|
@ -493,8 +493,9 @@ namespace libtorrent
|
|||
// TODO: There should be a way to abort an announce operation on the dht.
|
||||
// when the torrent is destructed
|
||||
boost::weak_ptr<torrent> self(shared_from_this());
|
||||
assert(m_ses.m_external_listen_port > 0);
|
||||
m_ses.m_dht->announce(m_torrent_file.info_hash()
|
||||
, m_ses.m_listen_interface.port()
|
||||
, m_ses.m_external_listen_port
|
||||
, m_ses.m_strand.wrap(bind(&torrent::on_dht_announce_response_disp, self, _1)));
|
||||
}
|
||||
|
||||
|
@ -720,7 +721,7 @@ namespace libtorrent
|
|||
int corr = m_torrent_file.piece_size(last_piece)
|
||||
- m_torrent_file.piece_length();
|
||||
total_done += corr;
|
||||
if (!m_picker->is_filtered(last_piece))
|
||||
if (m_picker->piece_priority(last_piece) != 0)
|
||||
wanted_done += corr;
|
||||
}
|
||||
|
||||
|
@ -768,7 +769,7 @@ namespace libtorrent
|
|||
corr += m_torrent_file.piece_size(last_piece) % m_block_size;
|
||||
}
|
||||
total_done += corr;
|
||||
if (!m_picker->is_filtered(index))
|
||||
if (m_picker->piece_priority(index) != 0)
|
||||
wanted_done += corr;
|
||||
}
|
||||
|
||||
|
@ -816,7 +817,7 @@ namespace libtorrent
|
|||
i != downloading_piece.end(); ++i)
|
||||
{
|
||||
total_done += i->second;
|
||||
if (!m_picker->is_filtered(i->first.piece_index))
|
||||
if (m_picker->piece_priority(i->first.piece_index) != 0)
|
||||
wanted_done += i->second;
|
||||
}
|
||||
|
||||
|
@ -1026,8 +1027,7 @@ namespace libtorrent
|
|||
|
||||
// TODO: update peer's interesting-bit
|
||||
|
||||
if (filter) m_picker->mark_as_filtered(index);
|
||||
else m_picker->mark_as_unfiltered(index);
|
||||
m_picker->set_piece_priority(index, filter ? 1 : 0);
|
||||
}
|
||||
|
||||
void torrent::filter_pieces(std::vector<bool> const& bitmask)
|
||||
|
@ -1042,23 +1042,15 @@ namespace libtorrent
|
|||
|
||||
// TODO: update peer's interesting-bit
|
||||
|
||||
std::vector<int> state;
|
||||
state.reserve(100);
|
||||
int index = 0;
|
||||
for (std::vector<bool>::const_iterator i = bitmask.begin()
|
||||
, end(bitmask.end()); i != end; ++i, ++index)
|
||||
{
|
||||
if (m_picker->is_filtered(index) == *i) continue;
|
||||
if ((m_picker->piece_priority(index) == 0) == *i) continue;
|
||||
if (*i)
|
||||
m_picker->mark_as_filtered(index);
|
||||
m_picker->set_piece_priority(index, 0);
|
||||
else
|
||||
state.push_back(index);
|
||||
}
|
||||
|
||||
for (std::vector<int>::reverse_iterator i = state.rbegin();
|
||||
i != state.rend(); ++i)
|
||||
{
|
||||
m_picker->mark_as_unfiltered(*i);
|
||||
m_picker->set_piece_priority(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1072,7 +1064,7 @@ namespace libtorrent
|
|||
assert(index >= 0);
|
||||
assert(index < m_torrent_file.num_pieces());
|
||||
|
||||
return m_picker->is_filtered(index);
|
||||
return m_picker->piece_priority(index) == 0;
|
||||
}
|
||||
|
||||
void torrent::filtered_pieces(std::vector<bool>& bitmask) const
|
||||
|
@ -1189,19 +1181,12 @@ namespace libtorrent
|
|||
{
|
||||
assert(p->associated_torrent().lock().get() == this);
|
||||
|
||||
std::vector<int> piece_list;
|
||||
const std::vector<bool>& pieces = p->get_bitfield();
|
||||
|
||||
for (std::vector<bool>::const_iterator i = pieces.begin();
|
||||
i != pieces.end(); ++i)
|
||||
{
|
||||
if (*i) piece_list.push_back(static_cast<int>(i - pieces.begin()));
|
||||
}
|
||||
|
||||
for (std::vector<int>::reverse_iterator i = piece_list.rbegin();
|
||||
i != piece_list.rend(); ++i)
|
||||
{
|
||||
peer_lost(*i);
|
||||
if (*i) peer_lost(static_cast<int>(i - pieces.begin()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2623,7 +2608,7 @@ namespace libtorrent
|
|||
int filtered_pieces = m_picker->num_filtered()
|
||||
+ m_picker->num_have_filtered();
|
||||
int last_piece_index = m_torrent_file.num_pieces() - 1;
|
||||
if (m_picker->is_filtered(last_piece_index))
|
||||
if (m_picker->piece_priority(last_piece_index) == 0)
|
||||
{
|
||||
st.total_wanted -= m_torrent_file.piece_size(last_piece_index);
|
||||
--filtered_pieces;
|
||||
|
|
Loading…
Reference in New Issue