From f770d1438d9d3d5a51763aab1cfa6550a053daf7 Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Sun, 16 Oct 2005 09:15:46 +0000 Subject: [PATCH] fixed bug introduced in latest storage changes (triggered when using metadata extension). improved commandline switches to client_test --- ChangeLog | 3 +- Jamfile | 2 + examples/Jamfile | 7 +- examples/client_test.cpp | 267 ++++++++++++++++++--------------- include/libtorrent/session.hpp | 2 +- include/libtorrent/torrent.hpp | 14 +- src/session.cpp | 15 ++ src/storage.cpp | 18 +-- src/torrent.cpp | 39 +++-- 9 files changed, 221 insertions(+), 146 deletions(-) diff --git a/ChangeLog b/ChangeLog index a1fc8b1ec..fdc3b7038 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,5 @@ - + * greatly improved the command line control of the example client_test + * fixed bug where upload rate limit was not being applied * files that are being checked will no longer stall files that don't need checking. * changed the way libtorrent identifies support for its excentions diff --git a/Jamfile b/Jamfile index 575e141f9..ba51f78af 100755 --- a/Jamfile +++ b/Jamfile @@ -36,6 +36,7 @@ project torrent multi msvc:/Zc:wchar_t msvc:/Zc:forScope + TORRENT_PROFILE : usage-requirements @@ -43,6 +44,7 @@ project torrent $(BOOST_ROOT) release:NDEBUG BOOST_ALL_NO_LIB + TORRENT_PROFILE ; diff --git a/examples/Jamfile b/examples/Jamfile index 34d6aef88..cc5185daa 100755 --- a/examples/Jamfile +++ b/examples/Jamfile @@ -1,10 +1,15 @@ +import modules ; + +BOOST_ROOT = [ modules.peek : BOOST_ROOT ] ; + use-project /torrent : .. ; +use-project /boost : $(BOOST_ROOT) ; project client_test : requirements multi /torrent ; -exe client_test : client_test.cpp ; +exe client_test : client_test.cpp /boost/program_options /boost/regex ; exe simple_client : simple_client.cpp ; exe dump_torrent : dump_torrent.cpp ; exe make_torrent : make_torrent.cpp ; diff --git a/examples/client_test.cpp b/examples/client_test.cpp index 644ef8ea1..19f4d49ba 100755 --- a/examples/client_test.cpp +++ b/examples/client_test.cpp @@ -43,6 +43,8 @@ POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include +#include #ifdef _MSC_VER #pragma warning(pop) @@ -77,14 +79,7 @@ bool sleep_and_input(char* c) return false; }; -void set_cursor(int x, int y) -{ - HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE); - COORD c = {x, y}; - SetConsoleCursorPosition(h, c); -} - -void clear() +void clear_home() { CONSOLE_SCREEN_BUFFER_INFO si; HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE); @@ -92,6 +87,7 @@ void clear() COORD c = {0, 0}; DWORD n; FillConsoleOutputCharacter(h, ' ', si.dwSize.X * si.dwSize.Y, c, &n); + SetConsoleCursorPosition(h, c); } #else @@ -139,14 +135,9 @@ bool sleep_and_input(char* c) return false; } -void set_cursor(int x, int y) +void clear_home() { - std::cout << "\033[" << y << ";" << x << "H"; -} - -void clear() -{ - std::cout << "\033[2J"; + std::cout << "\033[2J\033[0;0H"; } #endif @@ -258,27 +249,23 @@ void print_peer_info(std::ostream& out, std::vector const { out.fill(' '); out.width(2); - out << esc("32") << add_suffix(i->down_speed) << "/s " << esc("0") -// << "(" << add_suffix(i->total_download) << ") " - << esc("31") << add_suffix(i->up_speed) << "/s " << esc("0") -// << "(" << add_suffix(i->total_upload) << ") " -// << "ul:" << add_suffix(i->upload_limit) << "/s " -// << "uc:" << add_suffix(i->upload_ceiling) << "/s " -// << "df:" << ratio(i->total_download, i->total_upload) << " " + out << esc("32") << add_suffix(i->down_speed) << "/s " + << "(" << add_suffix(i->total_download) << ") " << esc("0") + << esc("31") << add_suffix(i->up_speed) << "/s " + << "(" << add_suffix(i->total_upload) << ") " << esc("0") << to_string(i->download_queue_length, 2, 2) << " " << to_string(i->upload_queue_length, 2, 2) << " " - << static_cast((i->flags & peer_info::interesting)?"I":"_") - << static_cast((i->flags & peer_info::choked)?"C":"_") - << static_cast((i->flags & peer_info::remote_interested)?"i":"_") - << static_cast((i->flags & peer_info::remote_choked)?"c":"_") - << static_cast((i->flags & peer_info::supports_extensions)?"e":"_") - << static_cast((i->flags & peer_info::local_connection)?"l":"r") << " "; + << ((i->flags & peer_info::interesting)?'I':'.') + << ((i->flags & peer_info::choked)?'C':'.') + << ((i->flags & peer_info::remote_interested)?'i':'.') + << ((i->flags & peer_info::remote_choked)?'c':'.') + << ((i->flags & peer_info::supports_extensions)?'e':'.') + << ((i->flags & peer_info::local_connection)?'l':'r') << " "; if (i->downloading_piece_index >= 0) { out << progress_bar( - i->downloading_progress / static_cast(i->downloading_total) - , 15); + i->downloading_progress / float(i->downloading_total), 15); } else { @@ -289,17 +276,107 @@ void print_peer_info(std::ostream& out, std::vector const } } -int main(int argc, char* argv[]) +void add_torrent(libtorrent::session& ses + , std::vector& handles + , char const* torrent + , float preferred_ratio + , boost::filesystem::path const& save_path) { using namespace libtorrent; - if (argc < 2) + TORRENT_CHECKPOINT("++ load torrent"); + std::ifstream in(torrent, std::ios_base::binary); + in.unsetf(std::ios_base::skipws); + entry e = bdecode(std::istream_iterator(in), std::istream_iterator()); + torrent_info t(e); + TORRENT_CHECKPOINT("-- load torrent"); + std::cout << t.name() << "\n"; + + TORRENT_CHECKPOINT("++ load resumedata"); + entry resume_data; + try { - std::cerr << "usage: ./client_test torrent-files ...\n" - "to stop the client, press q.\n"; + std::stringstream s; + s << t.name() << ".fastresume"; + boost::filesystem::ifstream resume_file(save_path / s.str(), std::ios_base::binary); + resume_file.unsetf(std::ios_base::skipws); + resume_data = bdecode( + std::istream_iterator(resume_file) + , std::istream_iterator()); + } + catch (invalid_encoding&) {} + catch (boost::filesystem::filesystem_error&) {} + TORRENT_CHECKPOINT("-- load resumedata"); + + handles.push_back(ses.add_torrent(e, save_path, resume_data, true, 16 * 1024)); + handles.back().set_max_connections(60); + handles.back().set_max_uploads(-1); + handles.back().set_ratio(preferred_ratio); +} + +int main(int ac, char* av[]) +{ + int listen_port; + float preferred_ratio; + int download_limit; + int upload_limit; + std::string save_path_str; + std::string log_level; + std::string ip_filter_file; + + namespace po = boost::program_options; + + po::options_description desc("supported options"); + desc.add_options() + ("help,h", "display this help message") + ("port,p", po::value(&listen_port)->default_value(6881) + , "set listening port") + ("ratio,r", po::value(&preferred_ratio)->default_value(0) + , "set the preferred upload/download ratio. 0 means infinite. Values " + "smaller than 1 are clamped to 1.") + ("max-download-rate,d", po::value(&download_limit)->default_value(0) + , "the maximum download rate given in kB/s. 0 means infinite.") + ("max-upload-rate,u", po::value(&upload_limit)->default_value(0) + , "the maximum upload rate given in kB/s. 0 means infinite.") + ("save-path,s", po::value(&save_path_str)->default_value("./") + , "the path where the downloaded file/folder should be placed.") + ("log-level,l", po::value(&log_level)->default_value("info") + , "sets the level at which events are logged [debug | info | warning].") + ("ip-filter,f", po::value(&ip_filter_file)->default_value("") + , "sets the path to the ip-filter file used to block access from certain " + "ips. ") + ("input-file,i", po::value< std::vector >() + , "adds an input .torrent file. At least one is required. arguments " + "without any flag are implicitly an input file. To start a torrentless " + "download, use @ instead of specifying a file.") + ; + + po::positional_options_description p; + p.add("input-file", -1); + + po::variables_map vm; + po::store(po::command_line_parser(ac, av). + options(desc).positional(p).run(), vm); + po::notify(vm); + + if (vm.count("help") || vm.count("input-file") == 0) + { + std::cout << desc << "\n"; return 1; } + // make sure the arguments stays within the usable limits + if (listen_port < 0 || listen_port > 65525) listen_port = 6881; + if (preferred_ratio != 0 && preferred_ratio < 1.f) preferred_ratio = 1.f; + upload_limit *= 1000; + download_limit *= 1000; + if (download_limit <= 0) download_limit = -1; + if (upload_limit <= 0) upload_limit = -1; + + using namespace libtorrent; + + std::vector const& input = vm["input-file"].as< std::vector >(); + namespace fs = boost::filesystem; fs::path::default_name_check(fs::no_check); @@ -317,11 +394,16 @@ int main(int argc, char* argv[]) std::vector handles; session ses; - ses.listen_on(std::make_pair(6880, 6889)); - //ses.set_upload_rate_limit(512 * 1024); + ses.set_download_rate_limit(download_limit); + ses.set_upload_rate_limit(upload_limit); + ses.listen_on(std::make_pair(listen_port, listen_port + 10)); ses.set_http_settings(settings); - ses.set_severity_level(alert::debug); -// ses.set_severity_level(alert::info); + if (log_level == "debug") + ses.set_severity_level(alert::debug); + else if (log_level == "warning") + ses.set_severity_level(alert::warning); + else + ses.set_severity_level(alert::info); // look for ipfilter.dat // poor man's parser @@ -342,7 +424,7 @@ int main(int argc, char* argv[]) // here ranges may overlap, and it is the last added // rule that has precedence for addresses that may fall // into more than one range. - std::ifstream in("ipfilter.dat"); + std::ifstream in(ip_filter_file.c_str()); ip_filter filter; while (in.good()) { @@ -366,54 +448,29 @@ int main(int argc, char* argv[]) } ses.set_ip_filter(filter); - - for (int i = 0; i < argc-1; ++i) + boost::filesystem::path save_path(save_path_str); + + // load the torrents given on the commandline + boost::regex ex("([0-9A-Fa-f]{40})@(.+)"); + for (std::vector::const_iterator i = input.begin(); + i != input.end(); ++i) { try { - boost::filesystem::path save_path("./"); - - if (std::string(argv[i+1]).substr(0, 7) == "http://") + // first see if this is a torrentless download + boost::cmatch what; + if (boost::regex_match(i->c_str(), what, ex)) { - sha1_hash info_hash = boost::lexical_cast(argv[i+2]); + sha1_hash info_hash = boost::lexical_cast(what[1]); - handles.push_back(ses.add_torrent(argv[i+1], info_hash, save_path)); + handles.push_back(ses.add_torrent(std::string(what[2]).c_str(), info_hash, save_path)); handles.back().set_max_connections(60); handles.back().set_max_uploads(-1); -// handles.back().set_ratio(1.1f); - ++i; - + handles.back().set_ratio(preferred_ratio); continue; } - std::ifstream in(argv[i+1], std::ios_base::binary); - in.unsetf(std::ios_base::skipws); - entry e = bdecode(std::istream_iterator(in), std::istream_iterator()); - torrent_info t(e); - t.print(std::cout); - - entry resume_data; - try - { - std::stringstream s; - s << t.name() << ".fastresume"; - boost::filesystem::ifstream resume_file(save_path / s.str(), std::ios_base::binary); - resume_file.unsetf(std::ios_base::skipws); - resume_data = bdecode( - std::istream_iterator(resume_file) - , std::istream_iterator()); - } - catch (invalid_encoding&) {} - catch (boost::filesystem::filesystem_error&) {} - - handles.push_back(ses.add_torrent(e, save_path, resume_data, true, 16 * 1024)); - handles.back().set_max_connections(60); - handles.back().set_max_uploads(-1); -// handles.back().set_ratio(1.02f); - -// std::vector ffilter(t.num_files(), true); -// ffilter[0] = false; -// handles.back().filter_files(ffilter); - + // if it's a torrent file, open it as usual + add_torrent(ses, handles, i->c_str(), preferred_ratio, save_path); } catch (std::exception& e) { @@ -423,6 +480,7 @@ int main(int argc, char* argv[]) if (handles.empty()) return 1; + // main loop std::vector peers; std::vector queue; @@ -441,9 +499,10 @@ int main(int argc, char* argv[]) i != handles.end(); ++i) { torrent_handle h = *i; - if (!h.get_torrent_info().is_valid()) continue; + if (!h.is_valid() || !h.has_metadata()) continue; h.pause(); + entry data = h.write_resume_data(); std::stringstream s; s << h.get_torrent_info().name() << ".fastresume"; @@ -458,34 +517,27 @@ int main(int argc, char* argv[]) if(c == 'r') { // force reannounce on all torrents - std::for_each( - handles.begin() - , handles.end() + std::for_each(handles.begin(), handles.end() , boost::bind(&torrent_handle::force_reannounce, _1)); } if(c == 'p') { // pause all torrents - std::for_each( - handles.begin() - , handles.end() + std::for_each(handles.begin(), handles.end() , boost::bind(&torrent_handle::pause, _1)); } if(c == 'u') { // unpause all torrents - std::for_each( - handles.begin() - , handles.end() + std::for_each(handles.begin(), handles.end() , boost::bind(&torrent_handle::resume, _1)); } if (c == 'i') print_peers = !print_peers; if (c == 'l') print_log = !print_log; if (c == 'd') print_downloads = !print_downloads; - } // loop through the alert queue to see if anything has happened. @@ -543,30 +595,10 @@ int main(int argc, char* argv[]) if (s.state != torrent_status::seeding) { - switch(s.state) - { - case torrent_status::queued_for_checking: - out << "queued "; - break; - case torrent_status::checking_files: - out << "checking "; - break; - case torrent_status::connecting_to_tracker: - out << "connecting to tracker "; - break; - case torrent_status::downloading_metadata: - out << "downloading metadata "; - break; - case torrent_status::downloading: - out << "downloading "; - break; - case torrent_status::finished: - out << "finished "; - break; - case torrent_status::seeding: - out << "seeding "; - break; - }; + static char const* state_str[] = + {"queued", "checking", "connecting", "downloading metadata" + , "downloading", "finished", "seeding"}; + out << state_str[s.state] << " "; } i->get_peer_info(peers); @@ -658,16 +690,17 @@ int main(int argc, char* argv[]) } } - clear(); - set_cursor(0, 0); + clear_home(); puts(out.str().c_str()); -// std::cout << out.str(); -// std::cout.flush(); } } catch (std::exception& e) { std::cout << e.what() << "\n"; } +#ifdef TORRENT_PROFILE + print_checkpoints(); +#endif return 0; } + diff --git a/include/libtorrent/session.hpp b/include/libtorrent/session.hpp index c92d043ff..e08d63cec 100755 --- a/include/libtorrent/session.hpp +++ b/include/libtorrent/session.hpp @@ -305,7 +305,7 @@ namespace libtorrent { public: - session(fingerprint const& print = fingerprint("LT", 0, 1, 0, 0)); + session(fingerprint const& print = fingerprint("LT", 0, 9, 1, 0)); session( fingerprint const& print , std::pair listen_port_range diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index ac061242b..ab3931665 100755 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -384,7 +384,8 @@ namespace libtorrent void set_max_connections(int limit); bool move_storage(boost::filesystem::path const& save_path); - bool valid_metadata() const { return m_storage.get() != 0; } + bool valid_metadata() const + { return m_storage.get() != 0 && m_connections_initialized; } std::vector const& metadata() const { return m_metadata; } bool received_metadata( @@ -546,6 +547,17 @@ namespace libtorrent // defaults to 16 kiB, but can be set by the user // when creating the torrent const int m_default_block_size; + + // this is set to false as long as the connections + // of this torrent hasn't been initialized. If we + // have metadata from the start, connections are + // initialized immediately, if we didn't have metadata, + // they are initialized right after files_checked(). + // valid_resume_data() will return false as long as + // the connections aren't initialized, to avoid + // them from altering the piece-picker before it + // has been initialized with files_checked(). + bool m_connections_initialized; }; inline boost::posix_time::ptime torrent::next_announce() const diff --git a/src/session.cpp b/src/session.cpp index cd2ab26fc..633acb61b 100755 --- a/src/session.cpp +++ b/src/session.cpp @@ -199,6 +199,13 @@ namespace libtorrent { namespace detail t->torrent_ptr->get_handle() , e.what())); } + if (t->torrent_ptr->num_peers()) + { + m_ses.m_torrents.insert(std::make_pair( + t->info_hash, t->torrent_ptr)); + t->torrent_ptr->abort(); + } + assert(!m_torrents.empty()); m_torrents.pop_front(); } @@ -282,6 +289,7 @@ namespace libtorrent { namespace detail // This will happen if the storage fails to initialize boost::mutex::scoped_lock l(m_ses.m_mutex); boost::mutex::scoped_lock l2(m_mutex); + if (m_ses.m_alerts.should_post(alert::fatal)) { m_ses.m_alerts.post_alert( @@ -291,6 +299,13 @@ namespace libtorrent { namespace detail } assert(!m_processing.empty()); + if (processing->torrent_ptr->num_peers()) + { + m_ses.m_torrents.insert(std::make_pair( + processing->info_hash, processing->torrent_ptr)); + processing->torrent_ptr->abort(); + } + processing.reset(); m_processing.pop_front(); if (!m_processing.empty()) diff --git a/src/storage.cpp b/src/storage.cpp index 803cde59a..f4691f843 100755 --- a/src/storage.cpp +++ b/src/storage.cpp @@ -329,7 +329,7 @@ namespace libtorrent { std::vector > get_filesizes( - const torrent_info& t, path p) + torrent_info const& t, path p) { p = complete(p); std::vector > sizes; @@ -469,7 +469,7 @@ namespace libtorrent file_pool storage::impl::files(40); - storage::storage(const torrent_info& info, const path& path) + storage::storage(torrent_info const& info, path const& path) : m_pimpl(new impl(info, path)) { assert(info.begin_files() != info.end_files()); @@ -787,8 +787,8 @@ namespace libtorrent public: impl( - const torrent_info& info - , const path& path); + torrent_info const& info + , path const& path); bool check_fastresume( detail::piece_checker_data& d @@ -870,7 +870,7 @@ namespace libtorrent // a bitmask representing the pieces we have std::vector m_have_piece; - const torrent_info& m_info; + torrent_info const& m_info; // slots that haven't had any file storage allocated std::vector m_unallocated_slots; @@ -929,8 +929,8 @@ namespace libtorrent }; piece_manager::impl::impl( - const torrent_info& info - , const path& save_path) + torrent_info const& info + , path const& save_path) : m_storage(info, save_path) , m_compact_mode(false) , m_fill_mode(true) @@ -942,8 +942,8 @@ namespace libtorrent } piece_manager::piece_manager( - const torrent_info& info - , const path& save_path) + torrent_info const& info + , path const& save_path) : m_pimpl(new impl(info, save_path)) { } diff --git a/src/torrent.cpp b/src/torrent.cpp index bd13924e3..e2d630f94 100755 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -221,6 +221,7 @@ namespace libtorrent , m_metadata_progress(0) , m_metadata_size(0) , m_default_block_size(block_size) + , m_connections_initialized(true) { m_uploads_quota.min = 2; m_connections_quota.min = 2; @@ -306,6 +307,7 @@ namespace libtorrent , m_metadata_progress(0) , m_metadata_size(0) , m_default_block_size(block_size) + , m_connections_initialized(false) { m_uploads_quota.min = 2; m_connections_quota.min = 2; @@ -1067,7 +1069,13 @@ namespace libtorrent } bool torrent::check_fastresume(detail::piece_checker_data& data) - { + { + if (!m_storage.get()) + { + // this means we have received the metadata through the + // metadata extension, and we have to initialize + init(); + } assert(m_storage.get()); return m_storage->check_fastresume(data, m_have_pieces, m_compact_mode); } @@ -1087,6 +1095,20 @@ namespace libtorrent , true); m_picker->files_checked(m_have_pieces, unfinished_pieces); + if (!m_connections_initialized) + { + m_connections_initialized = true; + // all peer connections have to initialize themselves now that the metadata + // is available + typedef std::map conn_map; + for (conn_map::iterator i = m_connections.begin() + , end(m_connections.end()); i != end; ++i) + { + try { i->second->init(); } + catch (std::exception&e) {} + // TODO: in case of an exception, close the connection + } + } } alert_manager& torrent::alerts() const @@ -1516,7 +1538,6 @@ namespace libtorrent entry metadata = bdecode(m_metadata.begin(), m_metadata.end()); m_torrent_file.parse_info_section(metadata); - init(); { boost::mutex::scoped_lock(m_checker.m_mutex); @@ -1543,20 +1564,6 @@ namespace libtorrent get_handle(), "metadata successfully received from swarm")); } - // all peer connections have to initialize themselves now that the metadata - // is available - // TODO: is it ok to initialize the connections before the file check? - typedef std::map conn_map; - for (conn_map::iterator i = m_connections.begin() - , end(m_connections.end()); i != end; ++i) - { - i->second->init(); - } - -#ifndef NDEBUG - m_picker->integrity_check(this); -#endif - // clear the storage for the bitfield std::vector().swap(m_have_metadata); std::vector().swap(m_requested_metadata);