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:
Arvid Norberg 2007-03-15 22:03:56 +00:00
parent 23c95e35a0
commit ecaa3068d5
19 changed files with 912 additions and 440 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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";
}

View File

@ -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 \

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 \

View File

@ -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);

324
src/natpmp.cpp Normal file
View File

@ -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);
}
}

View File

@ -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])

View File

@ -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);
}
}

View File

@ -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

View File

@ -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;