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