diff --git a/docs/manual.html b/docs/manual.html index f0d33daa8..69e013bda 100755 --- a/docs/manual.html +++ b/docs/manual.html @@ -473,6 +473,7 @@ struct torrent_handle entry write_resume_data(); void force_reannounce(); void connect_peer(const address& adr) const; + void set_ratio(float ratio); boost::filsystem::path save_path() const; @@ -498,6 +499,13 @@ torrent. If the peer does not respond, or is not a member of this torrent, it wi be disconnected. No harm can be done by using this other than an unnecessary connection attempt is made. If the torrent is uninitialized or in queued or checking mode, this will throw invalid_handle.

+

set_ratio() sets the desired download / upload ratio. If set to 0, it is considered being +infinite. i.e. the client will always upload as much as it can, no matter how much it gets back +in return. With this setting it will work much like the standard clients.

+

Besides 0, the ration can be set to any number greater than or equal to 1. It means how much to +attempt to upload in return for each download. e.g. if set to 2, the client will try to upload +2 bytes for every byte received. The default setting for this is 0, which will make it work +as a standard client.

info_hash() returns the info hash for the torrent.

set_max_uploads() sets the maximum number of peers that's unchoked at the same time on this torrent. If you set this to -1, there will be no limit.

@@ -1264,6 +1272,49 @@ not trust the fast-resume data and just do the checking.

file format

+

The file format is a bencoded dictionary containing the following fields:

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + +
file-formatstring: "libtorrent resume file"
file-versioninteger: 1
info-hashstring, the info hash of the torrent this data is saved for.
blocks per pieceinteger, the number of blocks per piece. Must be: piece_size +/ (16 * 1024). Clamped to be within the range [1, 128]. It +is the number of blocks per (normal sized) piece. Usually +each piece is 16 * 1024 bytes in size.
slots

list of integers. The list mappes slots ti piece indices. It +tells which piece is on which slot. If piece index is -2 it +means it is free, that there's no piece there. If it is -1, +means the slot isn't allocated on disk yet. The pieces have +to meet the following requirements:

+
    +
  • if there's a slot at the position of the piece index, +the piece must be located in that slot.
  • +
+

TODO: finish

+
peers 
unfinished 

TODO: describe the file format

diff --git a/docs/manual.rst b/docs/manual.rst index 3633ea9dd..49e109d1c 100755 --- a/docs/manual.rst +++ b/docs/manual.rst @@ -471,6 +471,7 @@ Its declaration looks like this:: entry write_resume_data(); void force_reannounce(); void connect_peer(const address& adr) const; + void set_ratio(float ratio); boost::filsystem::path save_path() const; @@ -500,6 +501,14 @@ be disconnected. No harm can be done by using this other than an unnecessary con attempt is made. If the torrent is uninitialized or in queued or checking mode, this will throw invalid_handle_. +``set_ratio()`` sets the desired download / upload ratio. If set to 0, it is considered being +infinite. i.e. the client will always upload as much as it can, no matter how much it gets back +in return. With this setting it will work much like the standard clients. + +Besides 0, the ration can be set to any number greater than or equal to 1. It means how much to +attempt to upload in return for each download. e.g. if set to 2, the client will try to upload +2 bytes for every byte received. The default setting for this is 0, which will make it work +as a standard client. ``info_hash()`` returns the info hash for the torrent. @@ -1337,6 +1346,37 @@ not trust the fast-resume data and just do the checking. file format =========== +The file format is a bencoded dictionary containing the following fields: + ++----------------------+--------------------------------------------------------------+ +| ``file-format`` | string: "libtorrent resume file" | ++----------------------+--------------------------------------------------------------+ +| ``file-version`` | integer: 1 | ++----------------------+--------------------------------------------------------------+ +| ``info-hash`` | string, the info hash of the torrent this data is saved for. | ++----------------------+--------------------------------------------------------------+ +| ``blocks per piece`` | integer, the number of blocks per piece. Must be: piece_size | +| | / (16 * 1024). Clamped to be within the range [1, 128]. It | +| | is the number of blocks per (normal sized) piece. Usually | +| | each piece is 16 * 1024 bytes in size. | ++----------------------+--------------------------------------------------------------+ +| ``slots`` | list of integers. The list mappes slots ti piece indices. It | +| | tells which piece is on which slot. If piece index is -2 it | +| | means it is free, that there's no piece there. If it is -1, | +| | means the slot isn't allocated on disk yet. The pieces have | +| | to meet the following requirements: | +| | | +| | * if there's a slot at the position of the piece index, | +| | the piece must be located in that slot. | +| | | +| | TODO: finish | ++----------------------+--------------------------------------------------------------+ +| ``peers`` | | ++----------------------+--------------------------------------------------------------+ +| ``unfinished`` | | ++----------------------+--------------------------------------------------------------+ + + TODO: describe the file format extensions diff --git a/examples/client_test.cpp b/examples/client_test.cpp index f0da0d916..7139a337b 100755 --- a/examples/client_test.cpp +++ b/examples/client_test.cpp @@ -147,6 +147,38 @@ std::string to_string(float v, int width) return s.str(); } +std::string pos_to_string(float v, int width) +{ + std::stringstream s; + s.precision(width-1); + s.flags(std::ios_base::right); + s.width(width); + s.fill(' '); + s << fabs(v); + return s.str(); +} + +std::string ratio(float a, float b) +{ + std::stringstream s; + if (a > b) + { + if (b < 0.001f) s << " inf:1"; + else s << pos_to_string(a/b, 4) << ":1"; + } + else if (a < b) + { + if (a < 0.001f) s << " 1:inf"; + else s << "1:" << pos_to_string(b/a, 4); + } + else + { + s << " 1:1"; + } + + return s.str(); +} + std::string add_suffix(float val) { const char* prefix[] = {"B", "kB", "MB", "GB", "TB"}; @@ -207,6 +239,7 @@ int main(int argc, char* argv[]) entry resume_data; try { + // TODO: use a torrent-specific name here std::ifstream resume_file("test.fastresume", std::ios_base::binary); resume_file.unsetf(std::ios_base::skipws); resume_data = bdecode(std::istream_iterator(resume_file) @@ -217,6 +250,7 @@ int main(int argc, char* argv[]) handles.push_back(ses.add_torrent(t, "", resume_data)); handles.back().set_max_uploads(7); + handles.back().set_ratio(1); } catch (std::exception& e) { @@ -235,7 +269,7 @@ int main(int argc, char* argv[]) if (c == 'q') { entry data = handles.front().write_resume_data(); - + // TODO: use a torrent-specific name here std::ofstream out("test.fastresume", std::ios_base::binary); out.unsetf(std::ios_base::skipws); bencode(std::ostream_iterator(out), data); @@ -303,7 +337,7 @@ int main(int argc, char* argv[]) << "(" << add_suffix(total_down) << ") " << "u:" << add_suffix(up) << "/s " << "(" << add_suffix(total_up) << ") " - << "diff: " << add_suffix(total_down - total_up) << "\n"; + << "ratio: " << ratio(total_down, total_up) << "\n"; boost::posix_time::time_duration t = s.next_announce; out << "next announce: " << boost::posix_time::to_simple_string(t) << "\n"; @@ -316,7 +350,7 @@ int main(int argc, char* argv[]) << "(" << add_suffix(i->total_download) << ") " << "u: " << add_suffix(i->up_speed) << "/s " << "(" << add_suffix(i->total_upload) << ") " - << "df: " << add_suffix((int)i->total_download - (int)i->total_upload) << " " + << "df: " << ratio(i->total_download, i->total_upload) << " " << "f: " << static_cast((i->flags & peer_info::interesting)?"I":"_") << static_cast((i->flags & peer_info::choked)?"C":"_") diff --git a/include/libtorrent/entry.hpp b/include/libtorrent/entry.hpp index 6991f61c6..3df30aa35 100755 --- a/include/libtorrent/entry.hpp +++ b/include/libtorrent/entry.hpp @@ -152,49 +152,49 @@ namespace libtorrent integer_type& integer() { - if (m_type != int_t) throw type_error("invalid typ requested from entry"); + if (m_type != int_t) throw type_error("invalid type requested from entry"); return *reinterpret_cast(data); } const integer_type& integer() const { - if (m_type != int_t) throw type_error("invalid typ requested from entry"); + if (m_type != int_t) throw type_error("invalid type requested from entry"); return *reinterpret_cast(data); } string_type& string() { - if (m_type != string_t) throw type_error("invalid typ requested from entry"); + if (m_type != string_t) throw type_error("invalid type requested from entry"); return *reinterpret_cast(data); } const string_type& string() const { - if (m_type != string_t) throw type_error("invalid typ requested from entry"); + if (m_type != string_t) throw type_error("invalid type requested from entry"); return *reinterpret_cast(data); } list_type& list() { - if (m_type != list_t) throw type_error("invalid typ requested from entry"); + if (m_type != list_t) throw type_error("invalid type requested from entry"); return *reinterpret_cast(data); } const list_type& list() const { - if (m_type != list_t) throw type_error("invalid typ requested from entry"); + if (m_type != list_t) throw type_error("invalid type requested from entry"); return *reinterpret_cast(data); } dictionary_type& dict() { - if (m_type != dictionary_t) throw type_error("invalid typ requested from entry"); + if (m_type != dictionary_t) throw type_error("invalid type requested from entry"); return *reinterpret_cast(data); } const dictionary_type& dict() const { - if (m_type != dictionary_t) throw type_error("invalid typ requested from entry"); + if (m_type != dictionary_t) throw type_error("invalid type requested from entry"); return *reinterpret_cast(data); } diff --git a/include/libtorrent/peer_connection.hpp b/include/libtorrent/peer_connection.hpp index 93c788c5b..59f8fdc50 100755 --- a/include/libtorrent/peer_connection.hpp +++ b/include/libtorrent/peer_connection.hpp @@ -324,12 +324,7 @@ namespace libtorrent int send_quota_limit() const { return m_send_quota_limit; } - int share_diff() const - { - return m_free_upload - + m_statistics.total_payload_download() - - m_statistics.total_payload_upload(); - } + int share_diff() const; bool support_extensions() const { return m_supports_extensions; } diff --git a/include/libtorrent/policy.hpp b/include/libtorrent/policy.hpp index ebb18744e..f212f8ea6 100755 --- a/include/libtorrent/policy.hpp +++ b/include/libtorrent/policy.hpp @@ -40,6 +40,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/peer.hpp" #include "libtorrent/piece_picker.hpp" +#include "libtorrent/socket.hpp" namespace libtorrent { @@ -104,13 +105,13 @@ namespace libtorrent struct peer { - peer(const peer_id& pid); + peer(const peer_id& pid, const address& a); int total_download() const; int total_upload() const; - bool operator==(const peer_id& pid) const - { return id == pid; } + bool operator==(const address& pip) const + { return ip == pip; } // the id of the peer. This is needed to store information // about peers that aren't connected right now. This @@ -118,6 +119,9 @@ namespace libtorrent // will be saved a limited amount of time peer_id id; + // the ip/port pair this peer is or was connected on + address ip; + // the time when this peer was optimistically unchoked // the last time. boost::posix_time::ptime last_optimistically_unchoked; diff --git a/include/libtorrent/session.hpp b/include/libtorrent/session.hpp index 3e257f489..34a390dce 100755 --- a/include/libtorrent/session.hpp +++ b/include/libtorrent/session.hpp @@ -118,8 +118,10 @@ namespace libtorrent void parse_resume_data( const entry& rd , const torrent_info& info); + std::vector piece_map; std::vector unfinished_pieces; + std::vector
peers; // is filled in by storage::initialize_pieces() // and represents the progress. It should be a diff --git a/include/libtorrent/storage.hpp b/include/libtorrent/storage.hpp index cec37b6cc..9680c3423 100755 --- a/include/libtorrent/storage.hpp +++ b/include/libtorrent/storage.hpp @@ -95,6 +95,7 @@ namespace libtorrent , std::vector& pieces); void allocate_slots(int num_slots); + void mark_failed(int index); size_type read(char* buf, int piece_index, size_type offset, size_type size); void write(const char* buf, int piece_index, size_type offset, size_type size); diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index 81f0ae0f5..267e1aa35 100755 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -154,6 +154,12 @@ namespace libtorrent piece_manager& filesystem() { return m_storage; } + void set_ratio(float ratio) + { m_ratio = ratio; } + + float ratio() const + { return m_ratio; } + // -------------------------------------------- // PEER MANAGEMENT @@ -347,6 +353,11 @@ namespace libtorrent // true when the first tracker reponse // is received bool m_got_tracker_response; + + // the upload/download ratio that each peer + // tries to maintain. + // 0 is infinite + float m_ratio; }; } diff --git a/include/libtorrent/torrent_handle.hpp b/include/libtorrent/torrent_handle.hpp index 137a5602e..a2da87c6a 100755 --- a/include/libtorrent/torrent_handle.hpp +++ b/include/libtorrent/torrent_handle.hpp @@ -146,6 +146,10 @@ namespace libtorrent // manually connect a peer void connect_peer(const address& adr) const; + // valid ratios are 0 (infinite ratio) or [ 1.0 , inf ) + // the ratio is uploaded / downloaded. less than 1 is not allowed + void set_ratio(float up_down_ratio); + // TODO: add finish_file_allocation, which will force the // torrent to allocate storage for all pieces. diff --git a/src/entry.cpp b/src/entry.cpp index 631f0d54a..95df3ae9c 100755 --- a/src/entry.cpp +++ b/src/entry.cpp @@ -123,6 +123,7 @@ namespace libtorrent new (data) dictionary_type; break; default: + assert(m_type == undefined_t); m_type = undefined_t; } } @@ -166,6 +167,7 @@ namespace libtorrent call_destructor(reinterpret_cast(data)); break; default: + assert(m_type == undefined_t); break; } } diff --git a/src/peer_connection.cpp b/src/peer_connection.cpp index 9ff0ca56b..df4dd495a 100755 --- a/src/peer_connection.cpp +++ b/src/peer_connection.cpp @@ -703,6 +703,15 @@ namespace libtorrent m_extension_messages[i] = f->second.integer(); } } +#ifndef NDEBUG + (*m_logger) << "supported extensions:\n"; + for (entry::dictionary_type::const_iterator i = extensions.begin(); + i != extensions.end(); + ++i) + { + (*m_logger) << i->first << "\n"; + } +#endif } catch(invalid_encoding& e) { @@ -1037,6 +1046,20 @@ namespace libtorrent send_buffer_updated(); } + int peer_connection::share_diff() const + { + float ratio = m_torrent->ratio(); + + // if we have an infinite ratio, just say we have downloaded + // much more than we have uploaded. And we'll keep uploading. + if (ratio == 0.f) return 99999.f; + + return m_free_upload + + (m_statistics.total_payload_download() * ratio) + - m_statistics.total_payload_upload(); + } + + void peer_connection::second_tick() { m_statistics.second_tick(); @@ -1060,6 +1083,7 @@ namespace libtorrent } else { + float ratio = m_torrent->ratio(); // if we have downloaded too much, response with an // upload rate of 10 kB/s more than we dowlload // if we have uploaded too much, send with a rate of @@ -1067,16 +1091,21 @@ namespace libtorrent int bias = 0; if (diff > -2*m_torrent->block_size()) { - bias = m_statistics.download_rate() / 2; + bias = (m_statistics.download_rate() * ratio) / 2; if (bias < 10*1024) bias = 10*1024; } else { - bias = -m_statistics.download_rate() / 2; + bias = -(m_statistics.download_rate() * ratio) / 2; } m_send_quota_limit = m_statistics.download_rate() + bias; + // the maximum send_quota given our download rate from this peer if (m_send_quota_limit < 256) m_send_quota_limit = 256; + + // if the peer has been choked, send tha current piece + // as fast as possible + if (is_choked()) m_send_quota_limit = -1; } } diff --git a/src/policy.cpp b/src/policy.cpp index b5e0d1b0c..7921ce098 100755 --- a/src/policy.cpp +++ b/src/policy.cpp @@ -39,6 +39,10 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/socket.hpp" #include "libtorrent/peer_connection.hpp" +// TODO: move all alerts to a single header +// session.hpp is included just for the peer_error_alert +#include "libtorrent/session.hpp" + #if defined(_MSC_VER) && _MSC_VER < 1300 # define for if (false) {} else for #endif @@ -445,7 +449,8 @@ namespace libtorrent void policy::ban_peer(const peer_connection& c) { - std::vector::iterator i = std::find(m_peers.begin(), m_peers.end(), c.get_peer_id()); + std::vector::iterator i = + std::find(m_peers.begin(), m_peers.end(), c.get_socket()->sender()); assert(i != m_peers.end()); i->banned = true; @@ -454,7 +459,7 @@ namespace libtorrent bool policy::new_connection(peer_connection& c) { std::vector::iterator i - = std::find(m_peers.begin(), m_peers.end(), c.get_peer_id()); + = std::find(m_peers.begin(), m_peers.end(), c.get_socket()->sender()); if (i == m_peers.end()) { using namespace boost::posix_time; @@ -462,7 +467,7 @@ namespace libtorrent // we don't have ny info about this peer. // add a new entry - peer p(c.get_peer_id()); + peer p(c.get_peer_id(), c.get_socket()->sender()); m_peers.push_back(p); i = m_peers.end()-1; } @@ -481,7 +486,8 @@ namespace libtorrent { try { - std::vector::iterator i = std::find(m_peers.begin(), m_peers.end(), id); + std::vector::iterator i = + std::find(m_peers.begin(), m_peers.end(), remote); if (i == m_peers.end()) { using namespace boost::posix_time; @@ -489,7 +495,7 @@ namespace libtorrent // we don't have ny info about this peer. // add a new entry - peer p(id); + peer p(id, remote); m_peers.push_back(p); i = m_peers.end()-1; } @@ -508,8 +514,22 @@ namespace libtorrent i->connection = &m_torrent->connect_to_peer(remote, id); } - catch(network_error&) {} - catch(protocol_error&) {} + catch(network_error& e) + { + if (m_torrent->alerts().should_post(alert::debug)) + { + m_torrent->alerts().post_alert( + peer_error_alert(id, e.what())); + } + } + catch(protocol_error& e) + { + if (m_torrent->alerts().should_post(alert::debug)) + { + m_torrent->alerts().post_alert( + peer_error_alert(id, e.what())); + } + } } // this is called when we are choked by a peer @@ -599,7 +619,7 @@ namespace libtorrent void policy::connection_closed(const peer_connection& c) { std::vector::iterator i - = std::find(m_peers.begin(), m_peers.end(), c.get_peer_id()); + = std::find(m_peers.begin(), m_peers.end(), c.get_socket()->sender()); assert(i != m_peers.end()); @@ -631,7 +651,8 @@ namespace libtorrent #ifndef NDEBUG bool policy::has_connection(const peer_connection* p) { - return std::find(m_peers.begin(), m_peers.end(), p->get_peer_id()) != m_peers.end(); + return std::find(m_peers.begin(), m_peers.end(), p->get_socket()->sender()) + != m_peers.end(); } void policy::check_invariant() @@ -649,8 +670,11 @@ namespace libtorrent } #endif - policy::peer::peer(const peer_id& pid) + policy::peer::peer( + const peer_id& pid + , const address& a) : id(pid) + , ip(a) , last_optimistically_unchoked( boost::gregorian::date(1970,boost::gregorian::Jan,1)) , connected(boost::posix_time::second_clock::local_time()) diff --git a/src/session.cpp b/src/session.cpp index 596941653..41f68e4cb 100755 --- a/src/session.cpp +++ b/src/session.cpp @@ -296,6 +296,15 @@ namespace libtorrent m_ses->m_torrents.insert( std::make_pair(t->info_hash, t->torrent_ptr)).first; + + peer_id id; + std::fill(id.begin(), id.end(), 0); + for (std::vector
::const_iterator i = t->peers.begin(); + i != t->peers.end(); + ++i) + { + t->torrent_ptr->get_policy().peer_from_tracker(*i, id); + } } } catch(const std::exception& e) @@ -891,9 +900,6 @@ namespace libtorrent // if we don't have any resume data, return if (resume_data.type() == entry::undefined_t) return; - std::vector tmp_pieces; - std::vector tmp_unfinished; - entry rd = resume_data; try @@ -915,6 +921,7 @@ namespace libtorrent if (slots.size() > info.num_pieces()) return; + std::vector tmp_pieces; tmp_pieces.reserve(slots.size()); for (entry::list_type::const_iterator i = slots.begin(); i != slots.end(); @@ -931,8 +938,11 @@ namespace libtorrent if (num_blocks_per_piece > 128 || num_blocks_per_piece < 1) return; + // the unfinished pieces + const entry::list_type& unfinished = rd.dict()["unfinished"].list(); + std::vector tmp_unfinished; tmp_unfinished.reserve(unfinished.size()); for (entry::list_type::const_iterator i = unfinished.begin(); i != unfinished.end(); @@ -962,6 +972,21 @@ namespace libtorrent tmp_unfinished.push_back(p); } + // the peers + + entry::list_type& peer_list = rd.dict()["peers"].list(); + + std::vector
tmp_peers; + tmp_peers.reserve(peer_list.size()); + for (entry::list_type::iterator i = peer_list.begin(); + i != peer_list.end(); + ++i) + { + address a(i->dict()["ip"].string(), i->dict()["port"].integer()); + tmp_peers.push_back(a); + } + + peers.swap(tmp_peers); piece_map.swap(tmp_pieces); unfinished_pieces.swap(tmp_unfinished); } @@ -974,78 +999,4 @@ namespace libtorrent return; } } -/* - void detail::piece_checker_data::parse_resume_data( - const std::vector* rd - , const torrent_info& info) - { - piece_map.clear(); - unfinished_pieces.clear(); - - std::vector tmp_pieces; - std::vector tmp_unfinished; - - if (rd == 0) return; - - const std::vector& data = *rd; - - if (data.size() < 20 + 3 * 4) return; - std::vector::const_iterator ptr = data.begin(); - - sha1_hash info_hash; - for (int i = 0; i < 20; ++i) info_hash[i] = read_uchar(ptr); - if (info.info_hash() != info_hash) return; - - int num_slots = detail::read_int(ptr); - if (num_slots < 0) return; - if (data.size() < 20 + (3 + num_slots) * 4) return; - - tmp_pieces.reserve(num_slots); - for (int i = 0; i < num_slots; ++i) - { - int index = read_int(ptr); - if (index >= info.num_pieces() || index < -2) - return; - tmp_pieces.push_back(index); - } - - int num_blocks_per_piece = read_int(ptr); - if (num_blocks_per_piece > 128 || num_blocks_per_piece < 1) - return; - - int num_unfinished = read_int(ptr); - if (num_unfinished < 0) return; - if (data.size() != 20 + (1 + num_slots + 2 + num_unfinished) * 4 + num_unfinished * (num_blocks_per_piece / 8)) - return; - - tmp_unfinished.reserve(num_unfinished); - for (int i = 0; i < num_unfinished; ++i) - { - piece_picker::downloading_piece p; - p.index = detail::read_int(ptr); - p.finished_blocks.reset(); - p.requested_blocks.reset(); - - if (p.index < 0 - || p.index >= info.num_pieces()) - return; - - const int num_bitmask_bytes = std::max(num_blocks_per_piece / 8, 1); - for (int j = 0; j < num_bitmask_bytes; ++j) - { - unsigned char bits = read_uchar(ptr); - for (int k = 0; k < 8; ++k) - { - const int bit = j * 8 + k; - if (bits & (1 << k)) - p.finished_blocks[bit] = true; - } - } - tmp_unfinished.push_back(p); - } - - piece_map.swap(tmp_pieces); - unfinished_pieces.swap(tmp_unfinished); - } -*/ } diff --git a/src/storage.cpp b/src/storage.cpp index c22f3f87d..17914efc4 100755 --- a/src/storage.cpp +++ b/src/storage.cpp @@ -359,6 +359,7 @@ namespace libtorrent { , std::vector& pieces); void allocate_slots(int num_slots); + void mark_failed(int index); size_type read(char* buf, int piece_index, size_type offset, size_type size); void write(const char* buf, int piece_index, size_type offset, size_type size); @@ -432,6 +433,10 @@ namespace libtorrent { void piece_manager::impl::export_piece_map( std::vector& p) const { + // synchronization ------------------------------------------------------ + boost::recursive_mutex::scoped_lock lock(m_mutex); + // ---------------------------------------------------------------------- + p.clear(); std::vector::const_reverse_iterator last; for (last = m_slot_to_piece.rbegin(); @@ -455,8 +460,32 @@ namespace libtorrent { { m_pimpl->export_piece_map(p); } - - + + // TODO: daniel, make sure this function does everything it needs to do + void piece_manager::impl::mark_failed(int index) + { + // synchronization ------------------------------------------------------ + boost::recursive_mutex::scoped_lock lock(m_mutex); + // ---------------------------------------------------------------------- + + assert(index >= 0 && index < m_piece_to_slot.size()); + assert(m_piece_to_slot[index] >= 0); + + int slot = m_slot_to_piece[m_piece_to_slot[index]]; + + assert(slot >= 0); + + m_slot_to_piece[m_piece_to_slot[index]] = -2; + m_piece_to_slot[index] = -1; + m_free_slots.push_back(slot); + } + + void piece_manager::mark_failed(int index) + { + m_pimpl->mark_failed(index); + } + + piece_manager::size_type piece_manager::impl::read( char* buf , int piece_index diff --git a/src/torrent.cpp b/src/torrent.cpp index cf1e5eb3e..133e89946 100755 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -100,11 +100,18 @@ namespace const entry::dictionary_type& info = e.dict(); - // extract peer id + // extract peer id (if any) entry::dictionary_type::const_iterator i = info.find("peer id"); - if (i == info.end()) throw std::runtime_error("invalid response from tracker"); - if (i->second.string().length() != 20) throw std::runtime_error("invalid response from tracker"); - std::copy(i->second.string().begin(), i->second.string().end(), ret.id.begin()); + if (i != info.end()) + { + if (i->second.string().length() != 20) throw std::runtime_error("invalid response from tracker"); + std::copy(i->second.string().begin(), i->second.string().end(), ret.id.begin()); + } + else + { + // if there's no peer_id, just initialize it to a bunch of zeroes + std::fill_n(ret.id.begin(), 20, 0); + } // extract ip i = info.find("ip"); @@ -146,14 +153,17 @@ namespace return ret.str(); } - struct find_peer + struct find_peer_by_id { - find_peer(const peer_id& i, const torrent* t): id(i), tor(t) {} + find_peer_by_id(const peer_id& i, const torrent* t): id(i), tor(t) {} bool operator()(const detail::session_impl::connection_map::value_type& c) const { if (c.second->get_peer_id() != id) return false; if (tor != c.second->associated_torrent()) return false; + // have a special case for all zeros. We can have any number + // of peers with that id, since it's used to indicate no id. + if (std::count(id.begin(), id.end(), 0) == 20) return false; return true; } @@ -161,12 +171,31 @@ namespace { if (p->get_peer_id() != id) return false; if (tor != p->associated_torrent()) return false; + // have a special case for all zeros. We can have any number + // of peers with that id, since it's used to indicate no id. + if (std::count(id.begin(), id.end(), 0) == 20) return false; return true; } const peer_id& id; const torrent* tor; }; + + struct find_peer_by_ip + { + find_peer_by_ip(const address& a, const torrent* t): ip(a), tor(t) {} + + bool operator()(const detail::session_impl::connection_map::value_type& c) const + { + if (c.first->sender() != ip) return false; + if (tor != c.second->associated_torrent()) return false; + return true; + } + + const address& ip; + const torrent* tor; + }; + } namespace libtorrent @@ -193,6 +222,7 @@ namespace libtorrent , m_priority(.5) , m_num_pieces(0) , m_got_tracker_response(false) + , m_ratio(0.f) { assert(torrent_file.begin_files() != torrent_file.end_files()); m_have_pieces.resize(torrent_file.num_pieces(), false); @@ -246,9 +276,18 @@ namespace libtorrent address a(i->ip, i->port); // if we aleady have a connection to the person, don't make another one - if (std::find_if(m_ses.m_connections.begin(), - m_ses.m_connections.end(), - find_peer(i->id, this)) != m_ses.m_connections.end()) + if (std::find_if( + m_ses.m_connections.begin() + , m_ses.m_connections.end() + , find_peer_by_id(i->id, this)) != m_ses.m_connections.end()) + { + continue; + } + + if (std::find_if( + m_ses.m_connections.begin() + , m_ses.m_connections.end() + , find_peer_by_ip(a, this)) != m_ses.m_connections.end()) { continue; } @@ -273,12 +312,12 @@ namespace libtorrent { assert(std::count_if(m_connections.begin() , m_connections.end() - , find_peer(id, this)) <= 1); + , find_peer_by_id(id, this)) <= 1); return std::find_if( m_connections.begin() , m_connections.end() - , find_peer(id, this)) + , find_peer_by_id(id, this)) != m_connections.end(); } @@ -342,6 +381,7 @@ namespace libtorrent // start with redownloading the pieces that the client // that has sent the least number of pieces m_picker.restore_piece(index); + m_storage.mark_failed(index); // TODO: make sure restore_piece() works assert(m_have_pieces[index] == false); @@ -407,6 +447,10 @@ namespace libtorrent m_event = event_none; } + // extension that tells the tracker that + // we don't need any peer_id's in the response + request += "&no_peer_id=1"; + return request; } @@ -495,7 +539,8 @@ namespace libtorrent m_ses.m_connections.insert(std::make_pair(s, c)).first; // add the newly connected peer to this torrent's peer list - assert(std::find(m_connections.begin() + assert(std::find( + m_connections.begin() , m_connections.end() , boost::get_pointer(p->second)) == m_connections.end()); diff --git a/src/torrent_handle.cpp b/src/torrent_handle.cpp index 7990409fa..2aad03f63 100755 --- a/src/torrent_handle.cpp +++ b/src/torrent_handle.cpp @@ -207,7 +207,7 @@ namespace libtorrent // num unfinished pieces int num_unfinished = q.size(); - ret.dict()["unfinished"] = entry(entry::list_t); + ret.dict()["unfinished"] = entry::list_type(); entry::list_type& up = ret.dict()["unfinished"].list(); // info for each unfinished piece @@ -237,6 +237,27 @@ namespace libtorrent // push the struct onto the unfinished-piece list up.push_back(piece_struct); } + + // write local peers + + ret.dict()["peers"] = entry::list_type(); + entry::list_type& peer_list = ret.dict()["peers"].list(); + + for (torrent::peer_const_iterator i = t->begin(); + i != t->end(); + ++i) + { + // we cannot save remote connection + // since we don't know their listen port + if (!(*i)->is_local()) continue; + + address ip = (*i)->get_socket()->sender(); + entry::dictionary_type peer; + peer["ip"] = ip.as_string(); + peer["port"] = ip.port(); + peer_list.push_back(peer); + } + return ret; } @@ -300,6 +321,35 @@ namespace libtorrent t->force_tracker_request(); } + void torrent_handle::set_ratio(float ratio) + { + assert(ratio >= 0.f); + + if (m_ses == 0) throw invalid_handle(); + + if (ratio < 1.f && ratio > 0.f) + ratio = 1.f; + + { + boost::mutex::scoped_lock l(m_ses->m_mutex); + torrent* t = m_ses->find_torrent(m_info_hash); + if (t != 0) + { + t->set_ratio(ratio); + } + } + + if (m_chk) + { + boost::mutex::scoped_lock l(m_chk->m_mutex); + detail::piece_checker_data* d = m_chk->find_torrent(m_info_hash); + if (d != 0) + { + d->torrent_ptr->set_ratio(ratio); + } + } + } + void torrent_handle::get_peer_info(std::vector& v) const { v.clear();