diff --git a/docs/manual.rst b/docs/manual.rst index e432b896f..38a0859b1 100644 --- a/docs/manual.rst +++ b/docs/manual.rst @@ -1482,11 +1482,42 @@ ones with lower tier will always be tried before the one with higher tier number std::string url; boost::uint8_t tier; boost::uint8_t fail_limit; + boost::uint8_t fails; + + enum tracker_source + { + source_torrent = 1, + source_client = 2, + source_magnet_link = 4, + source_tex = 8 + }; + boost::uint8_t source; + + bool verified:1; + bool updating:1; + bool start_sent:1; + bool complete_sent:1; }; ``fail_limit`` is the max number of failures to announce to this tracker in a row, before this tracker is not used anymore. +``fails`` is the number of times in a row we have failed to announce to this +tracker. + +``source`` is a bitmask specifying which sources we got this tracker from. + +``verified`` is set to true the first time we receive a valid response +from this tracker. + +``updating`` is true while we're waiting for a response from the tracker. + +``start_sent`` is set to true when we get a valid response from an announce +with event=started. If it is set, we won't send start in the subsequent +announces. + +``complete_sent`` is set to true when we send a event=completed. + total_size() piece_length() piece_size() num_pieces() ----------------------------------------------------- @@ -3057,6 +3088,8 @@ that will be sent to the tracker. The user-agent is a good way to identify your int auto_manage_startup; bool rate_limit_ip_overhead; + + bool announce_to_all_trackers; }; ``user_agent`` this is the client identification to the tracker. @@ -3341,6 +3374,14 @@ have a fair chance to start downloading. If ``rate_limit_ip_overhead`` is set to true, the estimated TCP/IP overhead is drained from the rate limiters, to avoid exceeding the limits with the total traffic +``announce_to_all_trackers`` controls how multi tracker torrents are +treated. If this is set to true, all trackers in the same tier are +announced to in parallel. If all trackers in tier 0 fails, all trackers +in tier 1 are announced as well. This is the uTorrent behavior. If it's +set to false, the behavior is as defined by the multi tracker +specification. It defaults to false, which is the same behavior previous +versions of libtorrent has had as well. + pe_settings =========== diff --git a/examples/client_test.cpp b/examples/client_test.cpp index b8c300c8f..9d4a5b831 100644 --- a/examples/client_test.cpp +++ b/examples/client_test.cpp @@ -154,6 +154,7 @@ void clear_home() #endif +bool print_trackers = false; bool print_peers = false; bool print_log = false; bool print_downloads = false; @@ -849,6 +850,7 @@ int main(int ac, char* av[]) settings.user_agent = "client_test/" LIBTORRENT_VERSION; settings.urlseed_wait_retry = wait_retry; + settings.announce_to_all_trackers = true; settings.outgoing_ports.first = bind_port_start; settings.outgoing_ports.second = bind_port_end; @@ -1214,6 +1216,7 @@ int main(int ac, char* av[]) } // toggle displays + if (c == 't') print_trackers = !print_trackers; if (c == 'i') print_peers = !print_peers; if (c == 'l') print_log = !print_log; if (c == 'd') print_downloads = !print_downloads; @@ -1348,7 +1351,7 @@ int main(int ac, char* av[]) << std::hex << s.seed_rank << std::dec << " " << s.last_scrape << "\n" << esc("0"); - if (torrent_index != active_torrent && s.state != torrent_status::seeding) continue; + if (torrent_index != active_torrent && s.state == torrent_status::seeding) continue; char const* progress_bar_color = "33"; // yellow if (s.state == torrent_status::checking_files || s.state == torrent_status::downloading_metadata) @@ -1388,6 +1391,7 @@ int main(int ac, char* av[]) << to_string(t.seconds(), 2) << esc("0") << " "; out << "tracker: " << esc("36") << s.current_tracker << esc("0") << "\n"; + if (torrent_index != active_torrent) continue; active_handle = h; } @@ -1451,6 +1455,23 @@ int main(int ac, char* av[]) if (print_peers && !peers.empty()) print_peer_info(out, peers); + if (print_trackers) + { + std::vector tr = h.trackers(); + ptime now = time_now(); + for (std::vector::iterator i = tr.begin() + , end(tr.end()); i != end; ++i) + { + std::string url = i->url; + url.resize(55, ' '); + out << to_string(i->tier, 2) << " " << url << " " + << to_string(i->fails, 3) << " " << (i->verified?"OK ":"- "); + if (i->updating) out << "updating"; + else out << to_string(total_seconds(i->next_announce - now), 8); + out << "\n"; + } + } + if (print_downloads) { h.get_download_queue(queue); diff --git a/include/libtorrent/session_settings.hpp b/include/libtorrent/session_settings.hpp index d8c974396..d5251da49 100644 --- a/include/libtorrent/session_settings.hpp +++ b/include/libtorrent/session_settings.hpp @@ -144,6 +144,7 @@ namespace libtorrent , prioritize_partial_pieces(false) , auto_manage_startup(120) , rate_limit_ip_overhead(true) + , announce_to_all_trackers(false) {} // this is the user agent that will be sent to the tracker @@ -456,6 +457,11 @@ namespace libtorrent // drained from the rate limiters, to avoid exceeding // the limits with the total traffic bool rate_limit_ip_overhead; + + // if set to true, multi tracker torrents are treated + // the same way uTorrent treats them. It defaults to + // false in order to comply with the extension definition. + bool announce_to_all_trackers; }; #ifndef TORRENT_DISABLE_DHT diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index eb0a17c63..18400b82d 100644 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -81,6 +81,8 @@ namespace libtorrent class piece_manager; struct torrent_plugin; struct bitfield; + struct announce_entry; + struct tracker_request; namespace aux { @@ -409,6 +411,8 @@ namespace libtorrent // announce ourself at the last time we tried to announce const tcp::endpoint& current_tracker() const; + announce_entry* find_tracker(tracker_request const& r); + // -------------------------------------------- // PIECE MANAGEMENT @@ -586,7 +590,7 @@ namespace libtorrent { return m_trackers; } void replace_trackers(std::vector const& urls); - void add_tracker(announce_entry const& urls); + void add_tracker(announce_entry const& url); torrent_handle get_handle(); @@ -656,8 +660,9 @@ namespace libtorrent void on_piece_verified(int ret, disk_io_job const& j , boost::function f); - void try_next_tracker(tracker_request const& req); int prioritize_tracker(int tracker_index); + int deprioritize_tracker(int tracker_index); + void on_country_lookup(error_code const& error, tcp::resolver::iterator i , boost::intrusive_ptr p) const; bool request_bandwidth_from_session(int channel) const; @@ -718,9 +723,6 @@ namespace libtorrent // the object. piece_manager* m_storage; - // the time of next tracker announce - ptime m_next_tracker_announce; - #ifndef NDEBUG public: #endif @@ -757,7 +759,7 @@ namespace libtorrent // used for tracker announces deadline_timer m_tracker_timer; - void restart_tracker_timer(ptime announce_at); + void update_tracker_timer(); static void on_tracker_announce_disp(boost::weak_ptr p , error_code const& e); @@ -895,10 +897,6 @@ namespace libtorrent // torrent object, these points are called connect_points. int m_deficit_counter; - // the number number of seconds between requests - // from the tracker - boost::int16_t m_duration; - // the sequence number for this torrent, this is a // monotonically increasing number for each added torrent boost::int16_t m_sequence_number; @@ -906,10 +904,6 @@ namespace libtorrent // the index to the last tracker that worked boost::int8_t m_last_working_tracker; - // the tracker that is currently (or was last) - // tried - boost::int8_t m_currently_trying_tracker; - // the number of connection attempts that has // failed in a row, this is currently used to // determine the timeout until next try. @@ -979,27 +973,30 @@ namespace libtorrent // this is true while tracker announcing is enabled // is is disabled while paused and checking files bool m_announcing:1; - - // this is true if event start has been sent to the tracker - bool m_start_sent:1; - - // this is true if event completed has been sent to the tracker - bool m_complete_sent:1; }; inline ptime torrent::next_announce() const { - return m_next_tracker_announce; + return m_tracker_timer.expires_at(); } inline void torrent::force_tracker_request() { - if (!is_paused()) announce_with_tracker(); + if (is_paused()) return; + ptime now = time_now(); + for (std::vector::iterator i = m_trackers.begin() + , end(m_trackers.end()); i != end; ++i) + i->next_announce = now; + update_tracker_timer(); } inline void torrent::force_tracker_request(ptime t) { - if (!is_paused()) restart_tracker_timer(t); + if (is_paused()) return; + for (std::vector::iterator i = m_trackers.begin() + , end(m_trackers.end()); i != end; ++i) + i->next_announce = t; + update_tracker_timer(); } inline void torrent::set_tracker_login( diff --git a/include/libtorrent/torrent_handle.hpp b/include/libtorrent/torrent_handle.hpp index 6499527a3..1e773f7ce 100644 --- a/include/libtorrent/torrent_handle.hpp +++ b/include/libtorrent/torrent_handle.hpp @@ -329,7 +329,7 @@ namespace libtorrent void clear_error() const; - std::vector const& trackers() const; + std::vector trackers() const; void replace_trackers(std::vector const&) const; void add_tracker(announce_entry const&) const; diff --git a/include/libtorrent/torrent_info.hpp b/include/libtorrent/torrent_info.hpp index 63a7a60d3..a6127e534 100644 --- a/include/libtorrent/torrent_info.hpp +++ b/include/libtorrent/torrent_info.hpp @@ -66,15 +66,34 @@ namespace libtorrent namespace gr = boost::gregorian; namespace fs = boost::filesystem; + enum + { + // wait 60 seconds before retrying a failed tracker + tracker_retry_delay_min = 10 + // when tracker_failed_max trackers + // has failed, wait 60 minutes instead + , tracker_retry_delay_max = 60 * 60 + }; + struct TORRENT_EXPORT announce_entry { announce_entry(std::string const& u) - : url(u), tier(0) - , fail_limit(3), fails(0) + : url(u) + , tier(0) + , fail_limit(3) + , fails(0) + , source(0) , verified(false) + , updating(false) + , start_sent(false) + , complete_sent(false) {} std::string url; + + // the time of next tracker announce + ptime next_announce; + boost::uint8_t tier; // the number of times this tracker can fail // in a row before it's removed. 0 means unlimited @@ -83,12 +102,56 @@ namespace libtorrent // the number of times in a row this tracker has failed boost::uint8_t fails; + enum tracker_source + { + source_torrent = 1, + source_client = 2, + source_magnet_link = 4, + source_tex = 8 + }; + // where did we get this tracker from + boost::uint8_t source; + // is set to true if we have ever received a response from // this tracker bool verified:1; - bool can_announce() const - { return fails < fail_limit || fail_limit == 0; } + // true if we're currently trying to announce with + // this tracker + bool updating:1; + + // this is true if event start has been sent to the tracker + bool start_sent:1; + + // this is true if event completed has been sent to the tracker + bool complete_sent:1; + + void reset() + { + start_sent = false; + next_announce = min_time(); + } + + void failed() + { + ++fails; + int delay = (std::min)(tracker_retry_delay_min + int(fails) * int(fails) * tracker_retry_delay_min + , int(tracker_retry_delay_max)); + next_announce = time_now() + seconds(delay); + updating = false; + } + + bool can_announce(ptime now) const + { + return now >= next_announce + && (fails < fail_limit || fail_limit == 0) + && !updating; + } + + bool is_working() const + { + return fails == 0; + } }; #ifndef BOOST_NO_EXCEPTIONS diff --git a/src/lt_trackers.cpp b/src/lt_trackers.cpp index 46408b572..3805309ba 100644 --- a/src/lt_trackers.cpp +++ b/src/lt_trackers.cpp @@ -221,6 +221,7 @@ namespace libtorrent { namespace announce_entry e(added->list_string_value_at(i)); if (e.url.empty()) continue; e.fail_limit = 3; + e.source = announce_entry::source_tex; m_torrent.add_tracker(e); #ifdef TORRENT_VERBOSE_LOGGING log_line << e.url << " "; diff --git a/src/torrent.cpp b/src/torrent.cpp index eda4420a7..4049e8892 100644 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -86,16 +86,6 @@ using libtorrent::aux::session_impl; namespace { - enum - { - // wait 60 seconds before retrying a failed tracker - tracker_retry_delay_min = 60 - // when tracker_failed_max trackers - // has failed, wait 10 minutes instead - , tracker_retry_delay_max = 10 * 60 - , tracker_failed_max = 5 - }; - struct find_peer_by_ip { find_peer_by_ip(tcp::endpoint const& a, const torrent* t) @@ -156,7 +146,6 @@ namespace libtorrent , m_last_scrape(min_time()) , m_torrent_file(tf) , m_storage(0) - , m_next_tracker_announce(time_now()) , m_host_resolver(ses.m_io_service) , m_lsd_announce_timer(ses.m_io_service) , m_tracker_timer(ses.m_io_service) @@ -182,11 +171,8 @@ namespace libtorrent , m_complete(-1) , m_incomplete(-1) , m_deficit_counter(0) - , m_duration(1800) , m_sequence_number(seq) , m_last_working_tracker(-1) - , m_currently_trying_tracker(-1) - , m_failed_trackers(0) , m_time_scaler(0) , m_abort(false) , m_paused(paused) @@ -201,8 +187,6 @@ namespace libtorrent , m_has_incoming(false) , m_files_checked(false) , m_announcing(false) - , m_start_sent(false) - , m_complete_sent(false) { if (resume_data) m_resume_data.swap(*resume_data); @@ -237,7 +221,6 @@ namespace libtorrent , m_last_scrape(min_time()) , m_torrent_file(new torrent_info(info_hash)) , m_storage(0) - , m_next_tracker_announce(time_now()) , m_host_resolver(ses.m_io_service) , m_lsd_announce_timer(ses.m_io_service) , m_tracker_timer(ses.m_io_service) @@ -262,11 +245,8 @@ namespace libtorrent , m_complete(-1) , m_incomplete(-1) , m_deficit_counter(0) - , m_duration(1800) , m_sequence_number(seq) , m_last_working_tracker(-1) - , m_currently_trying_tracker(-1) - , m_failed_trackers(0) , m_time_scaler(0) , m_abort(false) , m_paused(paused) @@ -281,8 +261,6 @@ namespace libtorrent , m_has_incoming(false) , m_files_checked(false) , m_announcing(false) - , m_start_sent(false) - , m_complete_sent(false) { if (resume_data) m_resume_data.swap(*resume_data); @@ -304,6 +282,7 @@ namespace libtorrent { m_trackers.push_back(announce_entry(tracker_url)); m_trackers.back().fail_limit = 0; + m_trackers.back().source = announce_entry::source_magnet_link; m_torrent_file->add_tracker(tracker_url); } } @@ -354,8 +333,13 @@ namespace libtorrent // don't announce private torrents if (m_torrent_file->is_valid() && m_torrent_file->priv()) return false; if (m_trackers.empty()) return true; + + int verified_trackers = 0; + for (std::vector::const_iterator i = m_trackers.begin() + , end(m_trackers.end()); i != end; ++i) + if (i->verified) ++verified_trackers; - return m_failed_trackers > 0 || !m_ses.settings().use_dht_as_fallback; + return verified_trackers == 0 || !m_settings.use_dht_as_fallback; } #endif @@ -919,16 +903,8 @@ namespace libtorrent if (m_trackers.empty()) return; - restart_tracker_timer(time_now() + seconds(tracker_retry_delay_max)); - if (m_abort) e = tracker_request::stopped; - if (e == tracker_request::none) - { - if (!m_start_sent) e = tracker_request::started; - if (!m_complete_sent && is_seed()) e = tracker_request::completed; - } - tracker_request req; req.info_hash = m_torrent_file->info_hash(); req.pid = m_ses.get_peer_id(); @@ -942,11 +918,6 @@ namespace libtorrent if (ep != tcp::endpoint()) req.ipv6 = ep.address().to_string(ec); - if (m_currently_trying_tracker == -1) m_currently_trying_tracker = 0; - TORRENT_ASSERT(m_currently_trying_tracker >= 0); - TORRENT_ASSERT(m_currently_trying_tracker < int(m_trackers.size())); - req.url = m_trackers[m_currently_trying_tracker].url; - TORRENT_ASSERT(m_trackers[m_currently_trying_tracker].can_announce()); // if we are aborting. we don't want any new peers req.num_want = (req.event == tracker_request::stopped) ?0:m_settings.num_want; @@ -955,24 +926,51 @@ namespace libtorrent ?0:m_ses.m_listen_sockets.front().external_port; req.key = m_ses.m_key; -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - if (m_abort) - { - boost::shared_ptr tl(new aux::tracker_logger(m_ses)); - m_ses.m_tracker_manager.queue_request(m_ses.m_io_service, m_ses.m_half_open, req - , tracker_login(), m_ses.m_listen_interface.address(), tl); - } - else -#endif - m_ses.m_tracker_manager.queue_request(m_ses.m_io_service, m_ses.m_half_open, req - , tracker_login(), m_ses.m_listen_interface.address() - , m_abort?boost::shared_ptr():shared_from_this()); + ptime now = time_now(); - if (m_ses.m_alerts.should_post()) + int tier = INT_MAX; + for (int i = 0; i < m_trackers.size(); ++i) { - m_ses.m_alerts.post_alert( - tracker_announce_alert(get_handle(), req.url, req.event)); + announce_entry& ae = m_trackers[i]; + if (ae.tier > tier) break; + if (ae.is_working()) tier = ae.tier; + if (!ae.can_announce(now)) + { + if (ae.is_working() && !m_settings.announce_to_all_trackers) break; + continue; + } + + req.url = ae.url; + req.event = e; + if (req.event == tracker_request::none) + { + if (!ae.start_sent) req.event = tracker_request::started; + if (!ae.complete_sent && is_seed()) req.event = tracker_request::completed; + } + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + if (m_abort) + { + boost::shared_ptr tl(new aux::tracker_logger(m_ses)); + m_ses.m_tracker_manager.queue_request(m_ses.m_io_service, m_ses.m_half_open, req + , tracker_login(), m_ses.m_listen_interface.address(), tl); + } + else +#endif + m_ses.m_tracker_manager.queue_request(m_ses.m_io_service, m_ses.m_half_open, req + , tracker_login(), m_ses.m_listen_interface.address() + , m_abort?boost::shared_ptr():shared_from_this()); + ae.updating = true; + + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert( + tracker_announce_alert(get_handle(), req.url, req.event)); + } + + if (ae.is_working() && !m_settings.announce_to_all_trackers) break; } + update_tracker_timer(); } void torrent::scrape_tracker() @@ -980,7 +978,6 @@ namespace libtorrent if (m_trackers.empty()) return; int i = m_last_working_tracker; - if (i == -1) i = m_currently_trying_tracker; if (i == -1) i = 0; tracker_request req; @@ -1037,42 +1034,36 @@ namespace libtorrent if (external_ip != address()) m_ses.set_external_address(external_ip); - if (!m_start_sent && r.event == tracker_request::started) - m_start_sent = true; - if (!m_complete_sent && r.event == tracker_request::completed) - m_complete_sent = true; + ptime now = time_now(); - m_failed_trackers = 0; + if (interval < m_settings.min_announce_interval) + interval = m_settings.min_announce_interval; - if (interval < m_ses.settings().min_announce_interval) - interval = m_ses.settings().min_announce_interval; - - if (m_currently_trying_tracker != -1) + announce_entry* ae = find_tracker(r); + if (ae) { - TORRENT_ASSERT(m_currently_trying_tracker >= 0); - TORRENT_ASSERT(m_currently_trying_tracker < int(m_trackers.size())); - TORRENT_ASSERT(m_trackers[m_currently_trying_tracker].url == r.url); - - m_trackers[m_currently_trying_tracker].verified = true; - - m_last_working_tracker - = prioritize_tracker(m_currently_trying_tracker); - m_currently_trying_tracker = 0; - TORRENT_ASSERT(m_trackers[m_currently_trying_tracker].url == r.url); + if (!ae->start_sent && r.event == tracker_request::started) + ae->start_sent = true; + if (!ae->complete_sent && r.event == tracker_request::completed) + ae->complete_sent = true; + ae->verified = true; + ae->updating = false; + ae->fails = 0; + ae->next_announce = now + seconds(interval); + int tracker_index = ae - &m_trackers[0]; + m_last_working_tracker = prioritize_tracker(tracker_index); } - - m_duration = interval; - restart_tracker_timer(time_now() + seconds(m_duration)); + update_tracker_timer(); if (complete >= 0) m_complete = complete; if (incomplete >= 0) m_incomplete = incomplete; if (complete >= 0 && incomplete >= 0) - m_last_scrape = time_now(); + m_last_scrape = now; #if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING std::stringstream s; s << "TRACKER RESPONSE:\n" - "interval: " << m_duration << "\n" + "interval: " << interval << "\n" "peers:\n"; for (std::vector::const_iterator i = peer_list.begin(); i != peer_list.end(); ++i) @@ -1117,16 +1108,6 @@ namespace libtorrent get_handle(), peer_list.size(), r.url)); } m_got_tracker_response = true; - - // when the tracker succeeds, reset the fails-in-a-row counter - if (m_currently_trying_tracker != -1) - { - TORRENT_ASSERT(m_currently_trying_tracker >= 0); - TORRENT_ASSERT(m_currently_trying_tracker < int(m_trackers.size())); - TORRENT_ASSERT(m_trackers[m_currently_trying_tracker].url == r.url); - - m_trackers[m_currently_trying_tracker].fails = 0; - } } void torrent::on_peer_name_lookup(error_code const& e, tcp::resolver::iterator host @@ -2083,7 +2064,10 @@ namespace libtorrent { m_trackers = urls; m_last_working_tracker = -1; - m_currently_trying_tracker = -1; + for (std::vector::iterator i = m_trackers.begin() + , end(m_trackers.end()); i != end; ++i) + if (i->source == 0) i->source = announce_entry::source_client; + if (!m_trackers.empty()) start_announcing(); else stop_announcing(); } @@ -2092,12 +2076,16 @@ namespace libtorrent { std::vector::iterator k = std::find_if(m_trackers.begin() , m_trackers.end(), boost::bind(&announce_entry::url, _1) == url.url); - if (k != m_trackers.end()) return; + if (k != m_trackers.end()) + { + k->source |= url.source; + return; + } k = std::upper_bound(m_trackers.begin(), m_trackers.end(), url , boost::bind(&announce_entry::tier, _1) < boost::bind(&announce_entry::tier, _2)); - if (k - m_trackers.begin() < m_currently_trying_tracker) ++m_currently_trying_tracker; if (k - m_trackers.begin() < m_last_working_tracker) ++m_last_working_tracker; - m_trackers.insert(k, url); + k = m_trackers.insert(k, url); + if (k->source == 0) k->source = announce_entry::source_client; if (!m_trackers.empty()) start_announcing(); } @@ -3454,7 +3442,16 @@ namespace libtorrent m_picker.reset(); set_state(torrent_status::seeding); - if (!m_complete_sent && m_announcing) announce_with_tracker(); + if (!m_announcing) return; + + ptime now = time_now(); + for (std::vector::iterator i = m_trackers.begin() + , end(m_trackers.end()); i != end; ++i) + { + if (i->complete_sent) continue; + i->next_announce = now; + } + announce_with_tracker(); } // this will move the tracker with the given index @@ -3470,67 +3467,30 @@ namespace libtorrent while (index > 0 && m_trackers[index].tier == m_trackers[index-1].tier) { - std::swap(m_trackers[index].url, m_trackers[index-1].url); + using std::swap; + swap(m_trackers[index], m_trackers[index-1]); + if (m_last_working_tracker == index) ++m_last_working_tracker; --index; } return index; } - void torrent::try_next_tracker(tracker_request const& req) + int torrent::deprioritize_tracker(int index) { INVARIANT_CHECK; - if (m_trackers.empty()) + TORRENT_ASSERT(index >= 0); + TORRENT_ASSERT(index < m_trackers.size()); + if (index >= (int)m_trackers.size()) return -1; + + while (index < m_trackers.size() - 1 && m_trackers[index].tier == m_trackers[index + 1].tier) { - m_currently_trying_tracker = -1; - return; + using std::swap; + swap(m_trackers[index], m_trackers[index + 1]); + if (m_last_working_tracker == index) --m_last_working_tracker; + ++index; } - - // increase tracker index until we find a tracker we can use - // or until we reach the end of the tracker list - do - { - ++m_currently_trying_tracker; - } while (m_currently_trying_tracker < m_trackers.size() - && !m_trackers[m_currently_trying_tracker].can_announce()); - - if (m_currently_trying_tracker < int(m_trackers.size())) - { - announce_with_tracker(req.event); - return; - } - - int delay = tracker_retry_delay_min - + (std::min)(int(m_failed_trackers), int(tracker_failed_max)) - * (tracker_retry_delay_max - tracker_retry_delay_min) - / tracker_failed_max; - - ++m_failed_trackers; - // if we've looped the tracker list, wait a bit before retrying - m_currently_trying_tracker = -1; - - // if we're stopping, just give up. Don't bother retrying - if (req.event == tracker_request::stopped) - return; - - restart_tracker_timer(time_now() + seconds(delay)); - -#ifndef TORRENT_DISABLE_DHT - if (m_abort) return; - - // only start the announce if we want to announce with the dht - ptime now = time_now(); - if (should_announce_dht() && now - m_last_dht_announce > minutes(14)) - { - // force the DHT to reannounce - m_last_dht_announce = now; - boost::weak_ptr self(shared_from_this()); - m_ses.m_dht->announce(m_torrent_file->info_hash() - , m_ses.m_listen_sockets.front().external_port - , bind(&torrent::on_dht_announce_response_disp, self, _1)); - } -#endif - + return index; } void torrent::files_checked() @@ -3562,7 +3522,9 @@ namespace libtorrent } else { - m_complete_sent = true; + for (std::vector::iterator i = m_trackers.begin() + , end(m_trackers.end()); i != end; ++i) + i->complete_sent = true; if (m_state != torrent_status::finished) finished(); } @@ -4206,14 +4168,31 @@ namespace libtorrent start_announcing(); } - void torrent::restart_tracker_timer(ptime announce_at) + void torrent::update_tracker_timer() { if (!m_announcing) return; - m_next_tracker_announce = announce_at; + ptime next_announce = max_time(); + int tier = INT_MAX; + for (std::vector::iterator i = m_trackers.begin() + , end(m_trackers.end()); i != end; ++i) + { + if (i->tier > tier) break; + if (i->is_working()) tier = i->tier; + if (i->fails >= i->fail_limit && i->fail_limit != 0) continue; + if (i->updating) continue; + if (i->next_announce < next_announce) next_announce = i->next_announce; + if (i->is_working() && !m_settings.announce_to_all_trackers) break; + } + if (next_announce == max_time()) return; + + // since we don't know if we have to re-issue the async_wait or not + // always do it +// if (m_tracker_timer.expires_at() <= next_announce) return; + error_code ec; boost::weak_ptr self(shared_from_this()); - m_tracker_timer.expires_at(m_next_tracker_announce, ec); + m_tracker_timer.expires_at(next_announce, ec); m_tracker_timer.async_wait(bind(&torrent::on_tracker_announce_disp, self, _1)); } @@ -4231,7 +4210,8 @@ namespace libtorrent if (!m_trackers.empty()) { // tell the tracker that we're back - m_start_sent = false; + std::for_each(m_trackers.begin(), m_trackers.end() + , bind(&announce_entry::reset, _1)); m_stat.clear(); announce_with_tracker(); } @@ -4258,8 +4238,11 @@ namespace libtorrent m_announcing = false; - if (!m_trackers.empty()) - announce_with_tracker(tracker_request::stopped); + ptime now = time_now(); + for (std::vector::iterator i = m_trackers.begin() + , end(m_trackers.end()); i != end; ++i) + i->next_announce = now; + announce_with_tracker(tracker_request::stopped); } void torrent::second_tick(stat& accumulator, float tick_interval) @@ -4447,6 +4430,15 @@ namespace libtorrent return m_tracker_address; } + announce_entry* torrent::find_tracker(tracker_request const& r) + { + std::vector::iterator i = std::find_if( + m_trackers.begin(), m_trackers.end() + , bind(&announce_entry::url, _1) == r.url); + if (i == m_trackers.end()) return 0; + return &*i; + } + void torrent::file_progress(std::vector& fp) const { fp.clear(); @@ -4683,19 +4675,19 @@ namespace libtorrent if (st.next_announce.is_negative() || is_paused()) st.next_announce = boost::posix_time::seconds(0); - st.announce_interval = boost::posix_time::seconds(m_duration); + st.announce_interval = boost::posix_time::seconds(0); if (m_last_working_tracker >= 0) { TORRENT_ASSERT(m_last_working_tracker < m_trackers.size()); - st.current_tracker - = m_trackers[m_last_working_tracker].url; + st.current_tracker = m_trackers[m_last_working_tracker].url; } - else if (m_currently_trying_tracker >= 0) + else { - TORRENT_ASSERT(m_currently_trying_tracker < m_trackers.size()); - st.current_tracker - = m_trackers[m_currently_trying_tracker].url; + std::vector::const_iterator i; + for (i = m_trackers.begin(); i != m_trackers.end(); ++i) + if (i->updating) break; + if (i != m_trackers.end()) st.current_tracker = i->url; } st.num_uploads = m_num_uploads; @@ -4797,18 +4789,17 @@ namespace libtorrent if (r.kind == tracker_request::announce_request) { + announce_entry* ae = find_tracker(r); + if (ae) + { + ae->failed(); + int tracker_index = ae - &m_trackers[0]; + deprioritize_tracker(tracker_index); + } if (m_ses.m_alerts.should_post()) { m_ses.m_alerts.post_alert(tracker_error_alert(get_handle() - , m_failed_trackers + 1, 0, r.url, "tracker timed out")); - } - if (m_currently_trying_tracker != -1) - { - TORRENT_ASSERT(m_currently_trying_tracker >= 0); - TORRENT_ASSERT(m_currently_trying_tracker < int(m_trackers.size())); - TORRENT_ASSERT(m_trackers[m_currently_trying_tracker].url == r.url); - - ++m_trackers[m_currently_trying_tracker].fails; + , ae?ae->fails:0, 0, r.url, "tracker timed out")); } } else if (r.kind == tracker_request::scrape_request) @@ -4819,9 +4810,7 @@ namespace libtorrent , r.url, "tracker timed out")); } } - - if (r.kind == tracker_request::announce_request) - try_next_tracker(r); + update_tracker_timer(); } // TODO: with some response codes, we should just consider @@ -4839,18 +4828,17 @@ namespace libtorrent #endif if (r.kind == tracker_request::announce_request) { + announce_entry* ae = find_tracker(r); + if (ae) + { + ae->failed(); + int tracker_index = ae - &m_trackers[0]; + deprioritize_tracker(tracker_index); + } if (m_ses.m_alerts.should_post()) { m_ses.m_alerts.post_alert(tracker_error_alert(get_handle() - , m_failed_trackers + 1, response_code, r.url, str)); - } - if (m_currently_trying_tracker != -1) - { - TORRENT_ASSERT(m_currently_trying_tracker >= 0); - TORRENT_ASSERT(m_currently_trying_tracker < int(m_trackers.size())); - TORRENT_ASSERT(m_trackers[m_currently_trying_tracker].url == r.url); - - ++m_trackers[m_currently_trying_tracker].fails; + , ae?ae->fails:0, response_code, r.url, str)); } } else if (r.kind == tracker_request::scrape_request) @@ -4860,9 +4848,7 @@ namespace libtorrent m_ses.m_alerts.post_alert(scrape_failed_alert(get_handle(), r.url, str)); } } - - if (r.kind == tracker_request::announce_request) - try_next_tracker(r); + update_tracker_timer(); } diff --git a/src/torrent_handle.cpp b/src/torrent_handle.cpp index 441f7ef69..4f8b3d81b 100644 --- a/src/torrent_handle.cpp +++ b/src/torrent_handle.cpp @@ -478,7 +478,7 @@ namespace libtorrent // ============ end deprecation =============== #endif - std::vector const& torrent_handle::trackers() const + std::vector torrent_handle::trackers() const { INVARIANT_CHECK; const static std::vector empty; diff --git a/src/torrent_info.cpp b/src/torrent_info.cpp index 4afd344a9..30a5cf00b 100644 --- a/src/torrent_info.cpp +++ b/src/torrent_info.cpp @@ -540,6 +540,7 @@ namespace libtorrent if (e.url.empty()) continue; e.tier = j; e.fail_limit = 0; + e.source = announce_entry::source_torrent; m_urls.push_back(e); } } @@ -565,6 +566,7 @@ namespace libtorrent { announce_entry e(torrent_file.dict_find_string_value("announce")); e.fail_limit = 0; + e.source = announce_entry::source_torrent; if (!e.url.empty()) m_urls.push_back(e); } @@ -638,11 +640,12 @@ namespace libtorrent { announce_entry e(url); e.tier = tier; + e.source = announce_entry::source_client; m_urls.push_back(e); using boost::bind; - std::sort(m_urls.begin(), m_urls.end(), boost::bind(std::less() - , bind(&announce_entry::tier, _1), bind(&announce_entry::tier, _2))); + std::sort(m_urls.begin(), m_urls.end(), bind(&announce_entry::tier, _1) + < bind(&announce_entry::tier, _2)); } #ifndef TORRENT_NO_DEPRECATE