diff --git a/docs/index.rst b/docs/index.rst index 99e735fc5..10dca632f 100755 --- a/docs/index.rst +++ b/docs/index.rst @@ -423,12 +423,24 @@ Its declaration looks like this:: void get_peer_info(std::vector& v); const torrent_info& get_torrent_info(); bool is_valid(); + + boost::filsystem::path save_path() const; + + sha1_hash info_hash() const; + + bool operator==(const torrent_handle&) const; + bool operator!=(const torrent_handle&) const; + bool operator<(const torrent_handle&) const; }; The default constructor will initialize the handle to an invalid state. Which means you cannot perform any operation on it, unless you first assign it a valid handle. If you try to perform any operation they will simply return. +``save_path()`` returns the path that were given to ``add_torrent()`` when this torrent +was started. + +``info_hash()`` returns the info hash for the torrent. status() ~~~~~~~~ @@ -453,6 +465,8 @@ It contains the following fields:: boost::posix_time::time_duration next_announce; std::size_t total_download; std::size_t total_upload; + float download_rate; + float upload_rate; std::vector pieces; std::size_t total_done; }; @@ -486,6 +500,10 @@ uploaded to all peers, accumulated, *this session* only. ``pieces`` is the bitmask that representw which pieces we have (set to true) and the pieces we don't have. +``download_rate`` and ``upload_rate`` are the total rates for all peers for this +torrent. These will usually have better precision than summing the rates from +all peers. + ``total_done`` is the total number of bytes of the file(s) that we have. get_download_queue() @@ -579,7 +597,8 @@ in the torrent. Each boolean tells you if the peer has that piece (if it's set t or if the peer miss that piece (set to false). ``upload_limit`` is the number of bytes per second we are allowed to send to this -peer every second. It may be -1 if there's no limit. +peer every second. It may be -1 if there's no limit. The upload limits of all peers +should sum up to the upload limit set by ``session::set_upload_limit``. get_torrent_info() @@ -657,6 +676,21 @@ that will be sent to the tracker. The user-agent is a good way to identify your int tracker_maximum_response_length; }; +``proxy_ip`` may be a hostname or ip to a http proxy to use. If this is +an empty string, no http proxy will be used. + +``proxy_port`` is the port on which the http proxy listens. If ``proxy_ip`` +is empty, this will be ignored. + +``proxy_login`` should be the login username for the http proxy, if this +empty, the http proxy will be trid to be used without authentication. + +``proxy_password`` the password string for the http proxy. + +``user_agent`` this is the client identification to the tracker. It will +be followed by the string "(libtorrent)" to identify that this library +is being used. This should be set to your client's name and version number. + ``tracker_timeout`` is the number of seconds the tracker connection will wait until it considers the tracker to have timed-out. Default value is 10 seconds. @@ -669,7 +703,6 @@ expand to 2 megs, it will be interrupted before the entire response has been uncompressed (given your limit is lower than 2 megs). Default limit is 1 megabyte. -TODO: finish document http_settings big_number ---------- diff --git a/examples/client_test.cpp b/examples/client_test.cpp index 4126f4e1d..f716b609f 100755 --- a/examples/client_test.cpp +++ b/examples/client_test.cpp @@ -152,7 +152,7 @@ int main(int argc, char* argv[]) using namespace libtorrent; // TEMPORARY - boost::filesystem::path::default_name_check(boost::filesystem::no_check); +// boost::filesystem::path::default_name_check(boost::filesystem::no_check); if (argc < 2) { @@ -172,7 +172,10 @@ int main(int argc, char* argv[]) { std::vector handles; session s(6881, "E\x1"); -// s.set_upload_rate_limit(20 * 1024); + + // limit upload rate to 100 kB/s +// s.set_upload_rate_limit(100 * 1024); + s.set_http_settings(settings); for (int i = 0; i < argc-1; ++i) { @@ -182,7 +185,6 @@ int main(int argc, char* argv[]) in.unsetf(std::ios_base::skipws); entry e = bdecode(std::istream_iterator(in), std::istream_iterator()); torrent_info t(e); -// t.convert_file_names(); t.print(std::cout); handles.push_back(s.add_torrent(t, boost::filesystem::path("", boost::filesystem::native))); } @@ -229,19 +231,11 @@ int main(int argc, char* argv[]) // calculate download and upload speeds i->get_peer_info(peers); - float down = 0.f; - float up = 0.f; + float down = s.download_rate; + float up = s.upload_rate; unsigned int total_down = s.total_download; unsigned int total_up = s.total_upload; int num_peers = peers.size(); - - for (std::vector::iterator i = peers.begin(); - i != peers.end(); - ++i) - { - down += i->down_speed; - up += i->up_speed; - } /* std::cout << boost::format("%f%% p:%d d:(%s) %s/s u:(%s) %s/s\n") % (s.progress*100) @@ -259,6 +253,22 @@ int main(int argc, char* argv[]) // std::cout << "next announce: " << boost::posix_time::to_simple_string(t) << "\n"; std::cout << "next announce: " << t.hours() << ":" << t.minutes() << ":" << t.seconds() << "\n"; + for (std::vector::iterator i = peers.begin(); + i != peers.end(); + ++i) + { + std::cout << "d: " << add_suffix(i->down_speed) << "/s (" << add_suffix(i->total_download) + << ") u: " << add_suffix(i->up_speed) << "/s (" << add_suffix(i->total_upload) + << ") ratio: " << static_cast(i->total_upload+1) / static_cast(i->total_download+1) + << " flags: " + << 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":"_") << "\n"; + + } + +/* i->get_download_queue(queue); for (std::vector::iterator i = queue.begin(); i != queue.end(); @@ -273,7 +283,9 @@ int main(int argc, char* argv[]) } std::cout << "\n"; } +*/ std::cout << "___________________________________\n"; + } } } diff --git a/include/libtorrent/peer_connection.hpp b/include/libtorrent/peer_connection.hpp index f98b12d59..7980ffbd8 100755 --- a/include/libtorrent/peer_connection.hpp +++ b/include/libtorrent/peer_connection.hpp @@ -87,7 +87,7 @@ namespace libtorrent // this is the constructor where the we are teh active part. The peer_conenction // should handshake and verify that the other end has the correct id peer_connection( - detail::session_impl* ses + detail::session_impl& ses , selector& sel , torrent* t , boost::shared_ptr s @@ -96,7 +96,7 @@ namespace libtorrent // with this constructor we have been contacted and we still don't know which torrent the // connection belongs to peer_connection( - detail::session_impl* ses + detail::session_impl& ses , selector& sel , boost::shared_ptr s); @@ -156,12 +156,7 @@ namespace libtorrent const stat& statistics() const { return m_statistics; } // is called once every second by the main loop - void second_tick() - { - m_statistics.second_tick(); - m_send_quota_left = m_send_quota; - if (m_send_quota > 0) send_buffer_updated(); - } + void second_tick(); boost::shared_ptr get_socket() const { return m_socket; } @@ -201,6 +196,9 @@ namespace libtorrent int trust_points() const { return m_trust_points; } + int send_quota_limit() const + { return m_send_quota_limit; } + #ifndef NDEBUG boost::shared_ptr m_logger; #endif @@ -250,13 +248,16 @@ namespace libtorrent std::vector m_recv_buffer; // this is the buffer where data that is - // to be sent is stored until + // to be sent is stored until it gets + // consumed by send() std::vector m_send_buffer; // timeouts boost::posix_time::ptime m_last_receive; boost::posix_time::ptime m_last_sent; + // the selector is used to add and remove this + // peer's socket from the writability monitor list. selector& m_selector; boost::shared_ptr m_socket; @@ -268,12 +269,14 @@ namespace libtorrent torrent* m_torrent; // this is set to false until the peer_id - // is received from the other end. Or is - // true if the conenction was actively - // opened from our side. + // is received from the other end. Or it is + // true from the start if the conenction + // was actively opened from our side. bool m_attached_to_torrent; - detail::session_impl* m_ses; + // a back reference to the session + // the peer belongs to. + detail::session_impl& m_ses; // is true if it was we that connected to the peer // and false if we got an incomming connection bool m_active; @@ -335,6 +338,12 @@ namespace libtorrent int m_send_quota; int m_send_quota_left; + // this is the maximum send quota we should give + // this peer given the current download rate + // and the current share ratio with this peer. + // this limit will maintain a 1:1 share ratio. + int m_send_quota_limit; + // for every valid piece we receive where this // peer was one of the participants, we increase // this value. For every invalid piece we receive diff --git a/include/libtorrent/stat.hpp b/include/libtorrent/stat.hpp index 85b75ceb9..67a48742b 100755 --- a/include/libtorrent/stat.hpp +++ b/include/libtorrent/stat.hpp @@ -47,8 +47,12 @@ namespace libtorrent stat() : m_downloaded(0) , m_uploaded(0) + , m_downloaded_protocol(0) + , m_uploaded_protocol(0) , m_total_download(0) , m_total_upload(0) + , m_total_download_protocol(0) + , m_total_upload_protocol(0) , m_peak_downloaded_per_second(0) , m_peak_uploaded_per_second(0) , m_mean_download_per_second(0) @@ -58,18 +62,44 @@ namespace libtorrent std::fill(m_upload_per_second_history, m_upload_per_second_history+history, 0); } + void operator+=(const stat& s) + { + m_downloaded += s.m_downloaded; + m_total_download += s.m_downloaded; + m_downloaded_protocol += s.m_downloaded_protocol; + m_total_download_protocol += s.m_downloaded_protocol; + + m_uploaded += s.m_uploaded; + m_total_upload += s.m_uploaded; + m_uploaded_protocol += s.m_uploaded_protocol; + m_total_upload_protocol += s.m_uploaded_protocol; + } + // TODO: these function should take two arguments // to be able to count both total data sent and also // count only the actual payload (not counting the // protocol chatter) - void received_bytes(int num_bytes) - { m_downloaded += num_bytes; m_total_download += num_bytes; } - void sent_bytes(int num_bytes) - { m_uploaded += num_bytes; m_total_upload += num_bytes; } + void received_bytes(int bytes_payload, int bytes_protocol) + { + m_downloaded += bytes_payload; + m_total_download += bytes_payload; + + m_downloaded_protocol += bytes_protocol; + m_total_download_protocol += bytes_protocol; + } + void sent_bytes(int bytes_payload, int bytes_protocol) + { + m_uploaded += bytes_payload; + m_total_upload += bytes_payload; + + m_uploaded_protocol += bytes_protocol; + m_total_upload_protocol += bytes_protocol; + } // should be called once every second void second_tick(); + // only counts the payload data! float upload_rate() const { return m_mean_upload_per_second; } float download_rate() const { return m_mean_download_per_second; } @@ -81,19 +111,34 @@ namespace libtorrent private: + + // history of download/upload speeds a few seconds back unsigned int m_download_per_second_history[history]; unsigned int m_upload_per_second_history[history]; // the accumulators we are adding the downloads/upploads - // to this second + // to this second. This only counts the actual payload + // and ignores the bytes sent as protocol chatter. unsigned int m_downloaded; unsigned int m_uploaded; + // the accumulators we are adding the downloads/upploads + // to this second. This only counts the protocol + // chatter and ignores the actual payload + unsigned int m_downloaded_protocol; + unsigned int m_uploaded_protocol; + // total download/upload counters + // only counting payload data unsigned int m_total_download; unsigned int m_total_upload; + // total download/upload counters + // only counting protocol chatter + unsigned int m_total_download_protocol; + unsigned int m_total_upload_protocol; + // peak mean download/upload rates unsigned int m_peak_downloaded_per_second; unsigned int m_peak_uploaded_per_second; diff --git a/include/libtorrent/storage.hpp b/include/libtorrent/storage.hpp index 1e17a5722..d83e8d5ee 100755 --- a/include/libtorrent/storage.hpp +++ b/include/libtorrent/storage.hpp @@ -236,14 +236,20 @@ namespace libtorrent piece_manager( const torrent_info& info - , const boost::filesystem::path& path); + , const boost::filesystem::path& path); - void check_pieces(boost::mutex& mutex, detail::piece_checker_data& data); + void check_pieces( + boost::mutex& mutex + , detail::piece_checker_data& data + , std::vector& pieces); void allocate_slots(int num_slots); size_type read(char* buf, int piece_index, size_type offset, size_type size); - size_type write(const 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); + + const boost::filesystem::path& save_path() const + { return m_save_path; } private: @@ -277,6 +283,8 @@ namespace libtorrent // * : the slot is assigned to this piece std::vector m_slot_to_piece; + boost::filesystem::path m_save_path; + // synchronization boost::mutex m_locked_pieces_monitor; boost::condition m_unlocked_pieces; diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index cd9c6799b..6bf07f834 100755 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -50,6 +50,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/policy.hpp" #include "libtorrent/storage.hpp" #include "libtorrent/url_handler.hpp" +#include "libtorrent/stat.hpp" namespace libtorrent { @@ -81,7 +82,12 @@ namespace libtorrent { public: - torrent(detail::session_impl* ses, const torrent_info& torrent_file); + typedef entry::integer_type size_type; + + torrent( + detail::session_impl& ses + , const torrent_info& torrent_file + , const boost::filesystem::path& save_path); ~torrent() {} void abort() { m_abort = true; m_event = event_stopped; } @@ -101,34 +107,11 @@ namespace libtorrent void print(std::ostream& os) const; - void allocate_files(detail::piece_checker_data* data, - boost::mutex& mutex, - const boost::filesystem::path& save_path); + void check_files(detail::piece_checker_data& data, + boost::mutex& mutex); - void uploaded_bytes(int num_bytes) { assert(num_bytes > 0); m_bytes_uploaded += num_bytes; } - void downloaded_bytes(int num_bytes) { assert(num_bytes > 0); m_bytes_downloaded += num_bytes; } - - int bytes_downloaded() const { return m_bytes_downloaded; } - int bytes_uploaded() const { return m_bytes_uploaded; } - int bytes_left() const - { - const std::vector& p = m_storage.pieces(); - int num_pieces = std::accumulate(p.begin(), p.end(), 0); - int total_blocks - = (m_torrent_file.total_size()+m_block_size-1)/m_block_size; - int blocks_per_piece - = m_torrent_file.piece_length() / m_block_size; - int unverified_blocks = m_picker.unverified_blocks(); - int blocks_we_have = num_pieces * blocks_per_piece; - const int last_piece = m_torrent_file.num_pieces()-1; - if (p[last_piece]) - { - blocks_we_have += m_picker.blocks_in_piece(last_piece) - - blocks_per_piece; - } - return m_torrent_file.total_size() - - (blocks_we_have + unverified_blocks) * m_block_size; - } + stat statistics() const { return m_stat; } + size_type bytes_left() const; torrent_status status() const; @@ -140,8 +123,8 @@ namespace libtorrent { return m_torrent_file; } policy& get_policy() { return *m_policy; } - storage* filesystem() { return &m_storage; } + piece_manager& filesystem() { return m_storage; } // -------------------------------------------- // PEER MANAGEMENT @@ -207,7 +190,8 @@ namespace libtorrent // PIECE MANAGEMENT // returns true if we have downloaded the given piece - bool have_piece(unsigned int index) const { return m_storage.have_piece(index); } + bool have_piece(unsigned int index) const + { return m_have_pieces[index]; } // when we get a have- or bitfield- messages, this is called for every // piece a peer has gained. @@ -234,11 +218,26 @@ namespace libtorrent piece_picker& picker() { return m_picker; } + + bool verify_piece(int piece_index); + // this is called from the peer_connection // each time a piece has failed the hash // test void piece_failed(int index); + float priority() const + { return m_priority; } + + void set_priority(float p) + { + assert(p >= 0.f && p <= 0.f); + m_priority = p; + } + + boost::filesystem::path save_path() const + { return m_storage.save_path(); } + // DEBUG #ifndef NDEBUG logger* spawn_logger(const char* title); @@ -273,11 +272,6 @@ namespace libtorrent void parse_response(const entry& e, std::vector& peer_list); - // TODO: Replace with stat-object - // total amount of bytes uploaded, downloaded - entry::integer_type m_bytes_uploaded; - entry::integer_type m_bytes_downloaded; - torrent_info m_torrent_file; piece_manager m_storage; @@ -294,11 +288,17 @@ namespace libtorrent std::vector m_connections; + // this is the upload and download statistics for the whole torrent. + // it's updated from all its peers once every second. + libtorrent::stat m_stat; + // ----------------------------- boost::shared_ptr m_policy; - detail::session_impl* m_ses; + // a back reference to the session + // this torrent belongs to. + detail::session_impl& m_ses; piece_picker m_picker; @@ -310,6 +310,19 @@ namespace libtorrent // second, and when it reaches 10, the policy::pulse() // is called and the time scaler is reset to 0. int m_time_scaler; + + // this is the priority of this torrent. It is used + // to weight the assigned upload bandwidth between peers + // it should be within the range [0, 1] + float m_priority; + + // the bitmask that says which pieces we have + std::vector m_have_pieces; + + // the number of pieces we have. The same as + // std::accumulate(m_have_pieces.begin(), + // m_have_pieces.end(), 0) + int m_num_pieces; }; } diff --git a/include/libtorrent/torrent_handle.hpp b/include/libtorrent/torrent_handle.hpp index 8bc31eb41..884dae8c5 100755 --- a/include/libtorrent/torrent_handle.hpp +++ b/include/libtorrent/torrent_handle.hpp @@ -78,6 +78,8 @@ namespace libtorrent // transferred this session! std::size_t total_download; std::size_t total_upload; + float download_rate; + float upload_rate; std::vector pieces; // the number of bytes of the file we have @@ -100,12 +102,12 @@ namespace libtorrent friend class session; torrent_handle(): m_ses(0) {} - void get_peer_info(std::vector& v); - torrent_status status(); - void get_download_queue(std::vector& queue); + void get_peer_info(std::vector& v) const; + torrent_status status() const; + void get_download_queue(std::vector& queue) const; - const torrent_info& get_torrent_info(); - bool is_valid(); + const torrent_info& get_torrent_info() const; + bool is_valid() const; // TODO: add force reannounce @@ -113,6 +115,20 @@ namespace libtorrent // to finish all pieces currently in the pipeline, and then // abort the torrent. + boost::filesystem::path save_path() const; + + const sha1_hash& info_hash() const + { return m_info_hash; } + + bool operator==(const torrent_handle& h) const + { return m_info_hash == h.m_info_hash; } + + bool operator!=(const torrent_handle& h) const + { return m_info_hash != h.m_info_hash; } + + bool operator<(const torrent_handle& h) const + { return m_info_hash < h.m_info_hash; } + private: torrent_handle(detail::session_impl* s, diff --git a/src/peer_connection.cpp b/src/peer_connection.cpp index e02188a60..7deb30214 100755 --- a/src/peer_connection.cpp +++ b/src/peer_connection.cpp @@ -72,7 +72,7 @@ namespace } libtorrent::peer_connection::peer_connection( - detail::session_impl* ses + detail::session_impl& ses , selector& sel , torrent* t , boost::shared_ptr s @@ -97,12 +97,14 @@ libtorrent::peer_connection::peer_connection( , m_choked(true) , m_send_quota(-1) , m_send_quota_left(-1) + , m_send_quota_limit(100) + , m_trust_points(0) { assert(!m_socket->is_blocking()); assert(m_torrent != 0); #ifndef NDEBUG - m_logger = m_ses->create_log(s->sender().as_string().c_str()); + m_logger = m_ses.create_log(s->sender().as_string().c_str()); #endif send_handshake(); @@ -119,7 +121,7 @@ libtorrent::peer_connection::peer_connection( } libtorrent::peer_connection::peer_connection( - detail::session_impl* ses + detail::session_impl& ses , selector& sel , boost::shared_ptr s) : m_state(read_protocol_length) @@ -142,11 +144,13 @@ libtorrent::peer_connection::peer_connection( , m_choked(true) , m_send_quota(-1) , m_send_quota_left(-1) + , m_send_quota_limit(100) + , m_trust_points(0) { assert(!m_socket->is_blocking()); #ifndef NDEBUG - m_logger = m_ses->create_log(s->sender().as_string().c_str()); + m_logger = m_ses.create_log(s->sender().as_string().c_str()); #endif // we are not attached to any torrent yet. @@ -170,13 +174,15 @@ libtorrent::peer_connection::~peer_connection() void libtorrent::peer_connection::set_send_quota(int num_bytes) { + assert(num_bytes <= m_send_quota_limit); assert(num_bytes >= 0); + if (num_bytes > m_send_quota_limit) num_bytes = m_send_quota_limit; + m_send_quota = num_bytes; m_send_quota_left = num_bytes; send_buffer_updated(); } - void libtorrent::peer_connection::send_handshake() { assert(m_send_buffer.size() == 0); @@ -214,13 +220,14 @@ void libtorrent::peer_connection::send_handshake() // peer id std::copy( - m_ses->get_peer_id().begin() - , m_ses->get_peer_id().end() + m_ses.get_peer_id().begin() + , m_ses.get_peer_id().end() , m_send_buffer.begin() + pos); #ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " ==> HANDSHAKE\n"; #endif + m_statistics.sent_bytes(0, m_send_buffer.size()); send_buffer_updated(); } @@ -236,6 +243,12 @@ void libtorrent::peer_connection::dispatch_message() // *************** CHOKE *************** case msg_choke: + + if (m_packet_size != 5) + throw protocol_error("'choke' message size != 5"); + + m_statistics.received_bytes(0, m_packet_size); + #ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " <== CHOKE\n"; #endif @@ -260,6 +273,11 @@ void libtorrent::peer_connection::dispatch_message() // *************** UNCHOKE *************** case msg_unchoke: + if (m_packet_size != 1) + throw protocol_error("'unchoke' message size != 1"); + + m_statistics.received_bytes(0, m_packet_size); + #ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " <== UNCHOKE\n"; #endif @@ -270,6 +288,11 @@ void libtorrent::peer_connection::dispatch_message() // *************** INTERESTED *************** case msg_interested: + if (m_packet_size != 1) + throw protocol_error("'interested' message size != 1"); + + m_statistics.received_bytes(0, m_packet_size); + #ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " <== INTERESTED\n"; #endif @@ -280,6 +303,11 @@ void libtorrent::peer_connection::dispatch_message() // *************** NOT INTERESTED *************** case msg_not_interested: + if (m_packet_size != 1) + throw protocol_error("'not interested' message size != 1"); + + m_statistics.received_bytes(0, m_packet_size); + #ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " <== NOT_INTERESTED\n"; #endif @@ -292,6 +320,11 @@ void libtorrent::peer_connection::dispatch_message() // *************** HAVE *************** case msg_have: { + if (m_packet_size != 5) + throw protocol_error("'have' message size != 5"); + + m_statistics.received_bytes(0, m_packet_size); + std::size_t index = read_int(&m_recv_buffer[1]); // if we got an invalid message, abort if (index >= m_have_piece.size()) @@ -325,6 +358,8 @@ void libtorrent::peer_connection::dispatch_message() if (m_packet_size - 1 != (m_have_piece.size() + 7) / 8) throw protocol_error("bitfield with invalid size"); + m_statistics.received_bytes(0, m_packet_size); + #ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " <== BITFIELD\n"; #endif @@ -361,6 +396,11 @@ void libtorrent::peer_connection::dispatch_message() // *************** REQUEST *************** case msg_request: { + if (m_packet_size != 13) + throw protocol_error("'request' message size != 13"); + + m_statistics.received_bytes(0, m_packet_size); + peer_request r; r.piece = read_int(&m_recv_buffer[1]); r.start = read_int(&m_recv_buffer[5]); @@ -440,7 +480,8 @@ void libtorrent::peer_connection::dispatch_message() (*m_logger) << m_socket->sender().as_string() << " <== PIECE [ piece: " << index << " | s: " << offset << " | l: " << len << " ]\n"; #endif - m_torrent->downloaded_bytes(len); + assert(m_packet_size > len); + m_statistics.received_bytes(len, m_packet_size - len); piece_picker& picker = m_torrent->picker(); piece_block block_finished(index, offset / m_torrent->block_size()); @@ -465,9 +506,7 @@ void libtorrent::peer_connection::dispatch_message() if (picker.is_finished(block_finished)) break; - m_receiving_piece.open(m_torrent->filesystem(), index, piece_file::out, offset); - m_receiving_piece.write(&m_recv_buffer[9], len); - m_receiving_piece.close(); + m_torrent->filesystem().write(&m_recv_buffer[9], index, offset, len); picker.mark_as_finished(block_finished, m_peer_id); @@ -476,9 +515,7 @@ void libtorrent::peer_connection::dispatch_message() // did we just finish the piece? if (picker.is_piece_finished(index)) { - m_receiving_piece.open(m_torrent->filesystem(), index, piece_file::in); - bool verified = m_torrent->filesystem()->verify_piece(m_receiving_piece); - m_receiving_piece.close(); + bool verified = m_torrent->verify_piece(index); if (verified) { m_torrent->announce_piece(index); @@ -496,6 +533,10 @@ void libtorrent::peer_connection::dispatch_message() // *************** CANCEL *************** case msg_cancel: { + if (m_packet_size != 13) + throw protocol_error("'cancel' message size != 13"); + m_statistics.received_bytes(0, m_packet_size); + peer_request r; r.piece = read_int(&m_recv_buffer[1]); r.start = read_int(&m_recv_buffer[5]); @@ -550,7 +591,7 @@ void libtorrent::peer_connection::cancel_block(piece_block block) m_send_buffer.resize(start_offset + 17); std::copy(buf, buf + 5, m_send_buffer.begin()+start_offset); - start_offset +=5; + start_offset += 5; // index write_int(block.piece_index, &m_send_buffer[start_offset]); @@ -567,6 +608,7 @@ void libtorrent::peer_connection::cancel_block(piece_block block) (*m_logger) << m_socket->sender().as_string() << " ==> CANCEL [ piece: " << block.piece_index << " | s: " << block_offset << " | l: " << block_size << " | " << block.block_index << " ]\n"; #endif assert(start_offset == m_send_buffer.size()); + m_statistics.sent_bytes(0, 17); send_buffer_updated(); } @@ -614,6 +656,7 @@ void libtorrent::peer_connection::request_block(piece_block block) (*m_logger) << m_socket->sender().as_string() << " ==> REQUEST [ piece: " << block.piece_index << " | s: " << block_offset << " | l: " << block_size << " | " << block.block_index << " ]\n"; #endif assert(start_offset == m_send_buffer.size()); + m_statistics.sent_bytes(0, 17); send_buffer_updated(); } @@ -634,6 +677,7 @@ void libtorrent::peer_connection::send_bitfield() if (m_torrent->have_piece(i)) m_send_buffer[old_size + 5 + (i>>3)] |= 1 << (7 - (i&7)); } + m_statistics.sent_bytes(0, packet_size); send_buffer_updated(); } @@ -646,6 +690,7 @@ void libtorrent::peer_connection::choke() #ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " ==> CHOKE\n"; #endif + m_statistics.sent_bytes(0, 5); send_buffer_updated(); } @@ -658,6 +703,7 @@ void libtorrent::peer_connection::unchoke() #ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " ==> UNCHOKE\n"; #endif + m_statistics.sent_bytes(0, 5); send_buffer_updated(); } @@ -670,6 +716,7 @@ void libtorrent::peer_connection::interested() #ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " ==> INTERESTED\n"; #endif + m_statistics.sent_bytes(0, 5); send_buffer_updated(); } @@ -682,20 +729,49 @@ void libtorrent::peer_connection::not_interested() #ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " ==> NOT_INTERESTED\n"; #endif + m_statistics.sent_bytes(0, 5); send_buffer_updated(); } void libtorrent::peer_connection::send_have(int index) { - char msg[9] = {0,0,0,5,msg_have}; + const int packet_size = 9; + char msg[packet_size] = {0,0,0,5,msg_have}; write_int(index, msg+5); - m_send_buffer.insert(m_send_buffer.end(), msg, msg+9); + m_send_buffer.insert(m_send_buffer.end(), msg, msg + packet_size); #ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " ==> HAVE [ piece: " << index << " ]\n"; #endif + m_statistics.sent_bytes(0, packet_size); send_buffer_updated(); } +void libtorrent::peer_connection::second_tick() +{ + m_statistics.second_tick(); + m_send_quota_left = m_send_quota; + if (m_send_quota > 0) send_buffer_updated(); + + // If the client sends more data + // we send it data faster, otherwise, slower. + // It will also depend on how much data the + // client has sent us. This is the mean to + // maintain a 1:1 share ratio with all peers. + + // TODO: make sure the rate is able to rise if + // both peers uses this technique! It could be + // enough to just have a constant positive bias + // of the send_quota_limit + int bias = (static_cast(m_statistics.total_download()) + - static_cast(m_statistics.total_upload())) / 1024; + + // the maximum send_quota given our download rate from this peer + int m_send_quota_limit = m_statistics.download_rate() + bias; + if (m_send_quota_limit < 500) m_send_quota_limit = 500; + + // TODO: temporary + m_send_quota_limit = 1024*1024; +} // -------------------------- // RECEIVE DATA @@ -731,7 +807,6 @@ void libtorrent::peer_connection::receive_data() if (received > 0) { - m_statistics.received_bytes(received); m_last_receive = boost::posix_time::second_clock::local_time(); m_recv_pos += received; @@ -741,6 +816,8 @@ void libtorrent::peer_connection::receive_data() switch(m_state) { case read_protocol_length: + m_statistics.received_bytes(0, received); + m_packet_size = reinterpret_cast(m_recv_buffer[0]); #ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " protocol length: " << m_packet_size << "\n"; @@ -761,6 +838,7 @@ void libtorrent::peer_connection::receive_data() case read_protocol_string: { + m_statistics.received_bytes(0, received); #ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " protocol: '" << std::string(m_recv_buffer.begin(), m_recv_buffer.end()) << "'\n"; #endif @@ -784,6 +862,7 @@ void libtorrent::peer_connection::receive_data() case read_info_hash: { + m_statistics.received_bytes(0, received); // ok, now we have got enough of the handshake. Is this connection // attached to a torrent? @@ -798,7 +877,7 @@ void libtorrent::peer_connection::receive_data() sha1_hash info_hash; std::copy(m_recv_buffer.begin()+8, m_recv_buffer.begin() + 28, (char*)info_hash.begin()); - m_torrent = m_ses->find_torrent(info_hash); + m_torrent = m_ses.find_torrent(info_hash); if (m_torrent == 0) { // we couldn't find the torrent! @@ -843,6 +922,7 @@ void libtorrent::peer_connection::receive_data() case read_peer_id: { + m_statistics.received_bytes(0, received); if (m_active) { // verify peer_id @@ -887,6 +967,8 @@ void libtorrent::peer_connection::receive_data() case read_packet_size: + m_statistics.received_bytes(0, received); + // convert from big endian to native byte order m_packet_size = read_int(&m_recv_buffer[0]); // don't accept packets larger than 1 MB @@ -916,8 +998,6 @@ void libtorrent::peer_connection::receive_data() case read_packet: - // TODO: dispatch should throw instead of returning status - // and instead of throwing network_error, throw protocol_error dispatch_message(); m_state = read_packet_size; @@ -974,18 +1054,8 @@ void libtorrent::peer_connection::send_data() throw network_error(0); } - m_sending_piece.open( - m_torrent->filesystem() - , r.piece - , piece_file::in - , r.start); #ifndef NDEBUG - assert(m_torrent->filesystem()->verify_piece(m_sending_piece) && "internal error"); - m_sending_piece.open( - m_torrent->filesystem() - , r.piece - , piece_file::in - , r.start); + assert(m_torrent->verify_piece(r.piece) && "internal error"); #endif const int send_buffer_offset = m_send_buffer.size(); const int packet_size = 4 + 5 + 4 + r.length; @@ -995,14 +1065,16 @@ void libtorrent::peer_connection::send_data() write_int(r.piece, &m_send_buffer[send_buffer_offset+5]); write_int(r.start, &m_send_buffer[send_buffer_offset+9]); - assert(r.start == m_sending_piece.tell()); - - m_sending_piece.read(&m_send_buffer[send_buffer_offset+13], r.length); + m_torrent->filesystem().read( + &m_send_buffer[send_buffer_offset+13] + , r.piece + , r.start + , r.length); #ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " ==> PIECE [ piece: " << r.piece << " | s: " << r.start << " | l: " << r.length << " ]\n"; #endif // let the torrent keep track of how much we have uploaded - m_torrent->uploaded_bytes(r.length); + m_statistics.sent_bytes(r.length, packet_size - r.length); } else { @@ -1053,7 +1125,6 @@ void libtorrent::peer_connection::send_data() if (sent > 0) { - m_statistics.sent_bytes(sent); if (m_send_quota_left != -1) { assert(m_send_quota_left >= sent); @@ -1106,6 +1177,7 @@ void libtorrent::peer_connection::keep_alive() char noop[] = {0,0,0,0}; m_send_buffer.insert(m_send_buffer.end(), noop, noop+4); m_last_sent = boost::posix_time::second_clock::local_time(); + m_statistics.sent_bytes(0, 4); #ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " ==> NOP\n"; #endif diff --git a/src/policy.cpp b/src/policy.cpp index 23bae5b0b..0270e7f84 100755 --- a/src/policy.cpp +++ b/src/policy.cpp @@ -238,7 +238,7 @@ namespace libtorrent c->choke(); } else if (uploaded - downloaded <= m_torrent->block_size() - && c->is_choked() && c->is_peer_interested()) + && c->is_choked() && c->is_interesting()) { // TODO: if we're not interested in this peer // we should only unchoke it if it' its turn @@ -352,6 +352,7 @@ namespace libtorrent { // if we're interested in the peer, we unchoke it // and hopes it will unchoke us too + if (c.is_interesting) c.unchoke(); } void policy::not_interested(peer_connection& c) diff --git a/src/session.cpp b/src/session.cpp index f88f11bf2..11ab062f5 100755 --- a/src/session.cpp +++ b/src/session.cpp @@ -60,6 +60,57 @@ namespace std }; #endif +namespace +{ + + // adjusts the upload rates of every peer connection + // to make sure the sum of all send quotas equals + // the given upload_limit. An upload limit of -1 means + // unlimited upload rate, but the rates of each peer + // has to be set anyway, since it depends on the download + // rate from the peer. + void control_upload_rates( + int upload_limit + , libtorrent::detail::session_impl::connection_map connections) + { + using namespace libtorrent; + + if (connections.empty()) return; + + assert(upload_limit != 0); + + if (upload_limit == -1) + { + for (detail::session_impl::connection_map::iterator i = connections.begin(); + i != connections.end(); + ++i) + { + // there's no limit, set the quota to max + // allowed + peer_connection& p = *i->second; + p.set_send_quota(p.send_quota_limit()); + } + return; + } + + // TODO: IMPLEMENT! + assert(false); + + +#ifndef NDEBUG + int sum = 0; + for (detail::session_impl::connection_map::iterator i = connections.begin(); + i != connections.end(); + ++i) + { + peer_connection& p = *i->second; + sum += p.send_quota(); + } + assert(sum == upload_limit); +#endif + } +} + namespace libtorrent { namespace detail @@ -93,7 +144,8 @@ namespace libtorrent try { - t->torrent_ptr->allocate_files(t, m_mutex, t->save_path); + assert(t != 0); + t->torrent_ptr->check_files(*t, m_mutex); // lock the session to add the new torrent boost::mutex::scoped_lock l(m_mutex); @@ -278,7 +330,7 @@ namespace libtorrent // TODO: filter ip:s boost::shared_ptr c( - new peer_connection(this, m_selector, s)); + new peer_connection(*this, m_selector, s)); if (m_upload_rate != -1) c->set_send_quota(0); m_connections.insert(std::make_pair(s, c)); @@ -299,7 +351,7 @@ namespace libtorrent // (*m_logger) << "readable: " << p->first->sender().as_string() << "\n"; p->second->receive_data(); } - catch(std::exception&) + catch(std::exception& e) { // the connection wants to disconnect for some reason, remove it // from the connection-list @@ -335,7 +387,6 @@ namespace libtorrent { assert(m_selector.is_writability_monitored(p->first)); assert(p->second->has_data()); - // (*m_logger) << "writable: " << p->first->sender().as_string() << "\n"; p->second->send_data(); } catch(std::exception&) @@ -390,17 +441,8 @@ namespace libtorrent // This should probably be done by accumulating the // left-over bandwidth to next second. Since the // the sockets consumes its data in rather big chunks. - if (m_upload_rate != -1 && !m_connections.empty()) - { - assert(m_upload_rate >= 0); - int share = m_upload_rate / m_connections.size(); - for (connection_map::iterator i = m_connections.begin(); - i != m_connections.end(); - ++i) - { - i->second->set_send_quota(share); - } - } + + control_upload_rates(m_upload_rate, m_connections); // do the second_tick() on each connection // this will update their statistics (download and upload speeds) @@ -409,8 +451,6 @@ namespace libtorrent for (connection_map::iterator i = m_connections.begin(); i != m_connections.end();) { - i->second->second_tick(); - connection_map::iterator j = i; ++i; // if this socket has timed out @@ -477,7 +517,7 @@ namespace libtorrent m_tracker_manager.tick(); boost::xtime t; boost::xtime_get(&t, boost::TIME_UTC); - t.nsec += 1000000; + t.nsec += 100000000; boost::thread::sleep(t); } @@ -583,7 +623,7 @@ namespace libtorrent // create the torrent and the data associated with // the checker thread and store it before starting // the thread - boost::shared_ptr torrent_ptr(new torrent(&m_impl, ti)); + boost::shared_ptr torrent_ptr(new torrent(m_impl, ti, save_path)); detail::piece_checker_data d; d.torrent_ptr = torrent_ptr; diff --git a/src/stat.cpp b/src/stat.cpp index 50c6ef908..fe0ea9fa5 100755 --- a/src/stat.cpp +++ b/src/stat.cpp @@ -51,10 +51,12 @@ void libtorrent::stat::second_tick() m_upload_per_second_history+history-1, m_upload_per_second_history+1); - m_download_per_second_history[0] = m_downloaded; - m_upload_per_second_history[0] = m_uploaded; + m_download_per_second_history[0] = m_downloaded + m_downloaded_protocol; + m_upload_per_second_history[0] = m_uploaded + m_uploaded_protocol; m_downloaded = 0; m_uploaded = 0; + m_downloaded_protocol = 0; + m_uploaded_protocol = 0; m_mean_download_per_second = std::accumulate(m_download_per_second_history, diff --git a/src/storage.cpp b/src/storage.cpp index b920c4199..b0df87f38 100755 --- a/src/storage.cpp +++ b/src/storage.cpp @@ -50,6 +50,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/torrent.hpp" #include "libtorrent/hasher.hpp" #include "libtorrent/session.hpp" +#include "libtorrent/peer_id.hpp" #if defined(_MSC_VER) #define for if (false) {} else for @@ -479,8 +480,8 @@ namespace { */ struct lazy_hash { - mutable sha1_hash digest; - mutable hasher h; + mutable libtorrent::sha1_hash digest; + mutable libtorrent::hasher h; mutable const char* data; std::size_t size; @@ -489,7 +490,7 @@ namespace { , size(size_) {} - const sha1_hash& get() const + const libtorrent::sha1_hash& get() const { if (data) { @@ -1286,7 +1287,9 @@ namespace libtorrent { storage::storage(const torrent_info& info, const fs::path& path) : m_info(info) , m_save_path(path) - {} + { + assert(info.begin_files() != info.end_files()); + } storage::size_type storage::read( char* buf @@ -1294,13 +1297,13 @@ namespace libtorrent { , size_type offset , size_type size) { - size_type start = slot * m_info->piece_length() + offset; + size_type start = slot * m_info.piece_length() + offset; // find the file iterator and file offset size_type file_offset = start; std::vector::const_iterator file_iter; - for (file_iter = m_info->begin_files();;) + for (file_iter = m_info.begin_files();;) { if (file_offset < file_iter->size) break; @@ -1311,7 +1314,7 @@ namespace libtorrent { fs::ifstream in( m_save_path / file_iter->path / file_iter->filename - , std::ios_base::binary + , std::ios_base::binary ); assert(file_offset < file_iter->size); @@ -1319,7 +1322,7 @@ namespace libtorrent { in.seekg(std::ios_base::beg, file_offset); size_type left_to_read = size; - size_type slot_size = m_info->piece_size(slot); + size_type slot_size = m_info.piece_size(slot); if (offset + left_to_read > slot_size) left_to_read = slot_size - offset; @@ -1363,13 +1366,13 @@ namespace libtorrent { void storage::write(const char* buf, int slot, size_type offset, size_type size) { - size_type start = slot * m_info->piece_length() + offset; + size_type start = slot * m_info.piece_length() + offset; // find the file iterator and file offset size_type file_offset = start; std::vector::const_iterator file_iter; - for (file_iter = m_info->begin_files();;) + for (file_iter = m_info.begin_files();;) { if (file_offset < file_iter->size) break; @@ -1388,7 +1391,7 @@ namespace libtorrent { out.seekp(std::ios_base::beg, file_offset); size_type left_to_write = size; - size_type slot_size = m_info->piece_size(slot); + size_type slot_size = m_info.piece_size(slot); if (offset + left_to_write > slot_size) left_to_write = slot_size - offset; @@ -1422,7 +1425,7 @@ namespace libtorrent { { ++file_iter; - assert(file_iter != m_info->end_files()); + assert(file_iter != m_info.end_files()); fs::path path = m_save_path / file_iter->path / file_iter->filename; @@ -1436,50 +1439,46 @@ namespace libtorrent { piece_manager::piece_manager( const torrent_info& info - , const fs::path& save_path) + , const fs::path& save_path) : m_storage(info, save_path) , m_info(info) + , m_save_path(save_path) { } - size_type piece_manager::read(char* buf, int piece_index, size_type offset, size_type size) + piece_manager::size_type piece_manager::read( + char* buf + , int piece_index + , piece_manager::size_type offset + , piece_manager::size_type size) { assert(m_piece_to_slot[piece_index] >= 0); int slot = m_piece_to_slot[piece_index]; return m_storage.read(buf, slot, offset, size); } - void piece_manager::write(const char* buf, int piece_index, size_type offset, size_type size) + void piece_manager::write( + const char* buf + , int piece_index + , piece_manager::size_type offset + , piece_manager::size_type size) { int slot = slot_for_piece(piece_index); m_storage.write(buf, slot, offset, size); } - void piece_manager::check_pieces(boost::mutex& mutex, detail::piece_checker_data& data) + void piece_manager::check_pieces( + boost::mutex& mutex + , detail::piece_checker_data& data + , std::vector& pieces) { // synchronization ------------------------------------------------------ boost::recursive_mutex::scoped_lock lock(m_mutex); // ---------------------------------------------------------------------- - // free up some memory - std::vector( - m_info.num_pieces(), false - ).swap(m_have_piece); - - std::vector( - m_info.num_pieces(), -1 - ).swap(m_allocated_pieces); - - std::vector( - m_info.num_pieces(), false - ).swap(m_locked_pieces); - - std::vector( - m_info.num_pieces(), -1 - ).swap(m_slot_to_piece); - - std::vector().swap(m_free_blocks); - std::vector().swap(m_free_pieces); + m_piece_to_slot.resize(m_info.num_pieces(), -1); + m_slot_to_piece.resize(m_info.num_pieces(), -1); + m_locked_pieces.resize(m_info.num_pieces(), false); m_bytes_left = m_info.total_size(); @@ -1503,7 +1502,7 @@ namespace libtorrent { { boost::mutex::scoped_lock lock(mutex); - data->progress = 0.f; + data.progress = 0.f; } for (torrent_info::file_iterator file_iter = m_info.begin_files(), @@ -1513,8 +1512,8 @@ namespace libtorrent { { boost::mutex::scoped_lock lock(mutex); - data->progress = (float)current_piece / m_info.num_pieces(); - if (data->abort) + data.progress = (float)current_piece / m_info.num_pieces(); + if (data.abort) return; } @@ -1615,19 +1614,19 @@ namespace libtorrent { for (int i = 0; i < m_info.num_pieces(); ++i) { - if (m_have_piece[i]) + if (pieces[i]) continue; const sha1_hash& hash = digest[ i == m_info.num_pieces() - 1]->get(); - if (equal_hash()(hash, m_info.hash_for_piece(i))) + if (hash == m_info.hash_for_piece(i)) { m_bytes_left -= m_info.piece_size(i); m_piece_to_slot[i] = current_piece; m_slot_to_piece[current_piece] = i; - m_have_piece[i] = true; + pieces[i] = true; found = true; break; } @@ -1665,9 +1664,9 @@ namespace libtorrent { std::cout << " num pieces: " << m_info.num_pieces() << "\n"; std::cout << " have_pieces: "; - print_bitmask(m_have_piece); + print_bitmask(pieces); std::cout << "\n"; - std::cout << std::count(m_have_piece.begin(), m_have_piece.end(), true) << "\n"; + std::cout << std::count(pieces.begin(), pieces.end(), true) << "\n"; check_invariant(); } @@ -1696,11 +1695,11 @@ namespace libtorrent { assert(!m_free_slots.empty()); } - std::vector::iterator iter( + std::vector::iterator iter( std::find( m_free_slots.begin() - , m_free_slots.end() - , piece_index)); + , m_free_slots.end() + , piece_index)); if (iter == m_free_slots.end()) { @@ -1720,7 +1719,7 @@ namespace libtorrent { } slot_index = *iter; - m_free_pieces.erase(iter); + m_free_slots.erase(iter); assert(m_slot_to_piece[slot_index] == -2); @@ -1770,7 +1769,7 @@ namespace libtorrent { // int last_piece_index = -1; - for (int i = 0; i < num; ++i, ++iter) + for (int i = 0; i < num_slots; ++i, ++iter) { if (iter == end_iter) break; @@ -1841,7 +1840,6 @@ namespace libtorrent { pos = 0; } */ - m_slot_to_piece[piece_pos / piece_size] = -2; } m_unallocated_slots.erase(m_unallocated_slots.begin(), iter); @@ -1852,15 +1850,15 @@ namespace libtorrent { */ } - void storage::check_invariant() const + void piece_manager::check_invariant() const { // synchronization ------------------------------------------------------ boost::recursive_mutex::scoped_lock lock(m_mutex); // ---------------------------------------------------------------------- for (int i = 0; i < m_info.num_pieces(); ++i) - { - if (m_allocated_pieces[i] != m_info.piece_length() * i) + { + if (m_piece_to_slot[i] != i) assert(m_slot_to_piece[i] < 0); } } @@ -1868,5 +1866,3 @@ namespace libtorrent { } // namespace libtorrent -#endif - diff --git a/src/torrent.cpp b/src/torrent.cpp index 96c0300b0..ba0ec0d8e 100755 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -143,13 +143,15 @@ namespace namespace libtorrent { - torrent::torrent(detail::session_impl* ses, const torrent_info& torrent_file) + torrent::torrent( + detail::session_impl& ses + , const torrent_info& torrent_file + , const boost::filesystem::path& save_path) : m_block_size(calculate_block_size(torrent_file)) , m_abort(false) , m_event(event_started) - , m_bytes_uploaded(0) - , m_bytes_downloaded(0) , m_torrent_file(torrent_file) + , m_storage(m_torrent_file, save_path) , m_next_request(boost::posix_time::second_clock::local_time()) , m_duration(1800) , m_policy(new policy(this)) @@ -159,7 +161,11 @@ namespace libtorrent , m_last_working_tracker(0) , m_currently_trying_tracker(0) , m_time_scaler(0) + , m_priority(.5) + , m_num_pieces(0) { + assert(torrent_file.begin_files() != torrent_file.end_files()); + m_have_pieces.resize(torrent_file.num_pieces(), false); } void torrent::tracker_response(const entry& e) @@ -199,15 +205,15 @@ namespace libtorrent ++i) { // don't make connections to ourself - if (i->id == m_ses->get_peer_id()) + if (i->id == m_ses.get_peer_id()) continue; 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(i->id, this)) != m_ses.m_connections.end()) { continue; } @@ -240,6 +246,21 @@ namespace libtorrent != m_connections.end(); } + torrent::size_type torrent::bytes_left() const + { + size_type have_bytes = m_num_pieces * m_torrent_file.piece_length(); + int last_piece = m_torrent_file.num_pieces()-1; + if (m_have_pieces[last_piece]) + { + have_bytes -= m_torrent_file.piece_length() + - m_torrent_file.piece_size(last_piece); + } + + return m_torrent_file.total_size() + - have_bytes; + } + + void torrent::piece_failed(int index) { std::vector downloaders; @@ -320,19 +341,19 @@ namespace libtorrent request += escape_string(reinterpret_cast(m_torrent_file.info_hash().begin()), 20); request += "&peer_id="; - request += escape_string(reinterpret_cast(m_ses->get_peer_id().begin()), 20); + request += escape_string(reinterpret_cast(m_ses.get_peer_id().begin()), 20); request += "&port="; request += boost::lexical_cast(port); request += "&uploaded="; - request += boost::lexical_cast(m_bytes_uploaded); + request += boost::lexical_cast(m_stat.total_upload()); request += "&downloaded="; - request += boost::lexical_cast(m_bytes_downloaded); + request += boost::lexical_cast(m_stat.total_download()); request += "&left="; - request += boost::lexical_cast(m_storage.bytes_left()); + request += boost::lexical_cast(bytes_left()); if (m_event != event_none) { @@ -409,13 +430,13 @@ namespace libtorrent s->connect(a); boost::shared_ptr c(new peer_connection( m_ses - , m_ses->m_selector + , m_ses.m_selector , this , s , id)); - if (m_ses->m_upload_rate != -1) c->set_send_quota(0); + if (m_ses.m_upload_rate != -1) c->set_send_quota(0); detail::session_impl::connection_map::iterator p = - m_ses->m_connections.insert(std::make_pair(s, c)).first; + 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() @@ -425,8 +446,8 @@ namespace libtorrent m_connections.push_back(boost::get_pointer(p->second)); - m_ses->m_selector.monitor_readability(s); - m_ses->m_selector.monitor_errors(s); + m_ses.m_selector.monitor_readability(s); + m_ses.m_selector.monitor_errors(s); // std::cout << "connecting to: " << a.as_string() << ":" << a.port() << "\n"; return c; } @@ -436,16 +457,16 @@ namespace libtorrent assert(std::find(m_connections.begin(), m_connections.end(), p) == m_connections.end()); m_connections.push_back(p); detail::session_impl::connection_map::iterator i - = m_ses->m_connections.find(p->get_socket()); - assert(i != m_ses->m_connections.end()); + = m_ses.m_connections.find(p->get_socket()); + assert(i != m_ses.m_connections.end()); if (!m_policy->new_connection(i->second)) throw network_error(0); } void torrent::close_all_connections() { - for (detail::session_impl::connection_map::iterator i = m_ses->m_connections.begin(); - i != m_ses->m_connections.end();) + for (detail::session_impl::connection_map::iterator i = m_ses.m_connections.begin(); + i != m_ses.m_connections.end();) { if (i->second->associated_torrent() == this) { @@ -456,7 +477,7 @@ namespace libtorrent assert(std::find(m_connections.begin(), m_connections.end(), pc) != m_connections.end()); detail::session_impl::connection_map::iterator j = i; ++i; - m_ses->m_connections.erase(j); + m_ses.m_connections.erase(j); assert(m_connections.size() + 1 == num_connections); assert(std::find(m_connections.begin(), m_connections.end(), pc) == m_connections.end()); } @@ -487,12 +508,11 @@ namespace libtorrent } } - void torrent::allocate_files(detail::piece_checker_data* data, - boost::mutex& mutex, - const boost::filesystem::path& save_path) + void torrent::check_files(detail::piece_checker_data& data, + boost::mutex& mutex) { - m_storage.initialize_pieces(this, save_path, data, mutex); - m_picker.files_checked(m_storage.pieces()); + m_storage.check_pieces(mutex, data, m_have_pieces); + m_picker.files_checked(m_have_pieces); #ifndef NDEBUG m_picker.integrity_check(this); #endif @@ -506,14 +526,48 @@ namespace libtorrent m_time_scaler = 0; m_policy->pulse(); } + + for (std::vector::iterator i = m_connections.begin(); + i != m_connections.end(); + ++i) + { + peer_connection* p = (*i); + const stat& s = p->statistics(); + m_stat += s; + p->second_tick(); + } + + m_stat.second_tick(); + } + + bool torrent::verify_piece(int piece_index) + { + size_type size = m_torrent_file.piece_size(piece_index); + std::vector buffer(size); + m_storage.read(&buffer[0], piece_index, size, 0); + + hasher h; + h.update(&buffer[0], size); + sha1_hash digest = h.final(); + + if (m_torrent_file.hash_for_piece(piece_index) != digest) + return false; + + if (!m_have_pieces[piece_index]) + m_num_pieces++; + m_have_pieces[piece_index] = true; + + assert(std::accumulate(m_have_pieces.begin(), m_have_pieces.end(), 0) + == m_num_pieces); + return true; } torrent_status torrent::status() const { torrent_status st; - const std::vector& p = m_storage.pieces(); - int num_pieces = std::accumulate(p.begin(), p.end(), 0); + const std::vector& p = m_have_pieces; + assert(std::accumulate(p.begin(), p.end(), 0) == m_num_pieces); int total_blocks = (m_torrent_file.total_size()+m_block_size-1)/m_block_size; @@ -522,7 +576,7 @@ namespace libtorrent int unverified_blocks = m_picker.unverified_blocks(); - int blocks_we_have = num_pieces * blocks_per_piece; + int blocks_we_have = m_num_pieces * blocks_per_piece; const int last_piece = m_torrent_file.num_pieces()-1; if (p[last_piece]) { @@ -530,8 +584,10 @@ namespace libtorrent - blocks_per_piece; } - st.total_download = m_bytes_downloaded; - st.total_upload = m_bytes_uploaded; + st.total_download = m_stat.total_download(); + st.total_upload = m_stat.total_upload(); + st.download_rate = m_stat.download_rate(); + st.upload_rate = m_stat.upload_rate(); st.progress = (blocks_we_have + unverified_blocks) / static_cast(total_blocks); @@ -540,10 +596,11 @@ namespace libtorrent // TODO: this is not accurate because it assumes the last // block is m_block_size bytes + // TODO: st.pieces could be a const pointer maybe? st.total_done = (blocks_we_have + unverified_blocks) * m_block_size; - st.pieces = m_storage.pieces(); + st.pieces = m_have_pieces; - if (num_pieces == p.size()) + if (m_num_pieces == p.size()) st.state = torrent_status::seeding; else st.state = torrent_status::downloading; @@ -554,7 +611,7 @@ namespace libtorrent #ifndef NDEBUG void torrent::debug_log(const std::string& line) { - (*m_ses->m_logger) << line << "\n"; + (*m_ses.m_logger) << line << "\n"; } #endif diff --git a/src/torrent_handle.cpp b/src/torrent_handle.cpp index 493c39778..001ba20a9 100755 --- a/src/torrent_handle.cpp +++ b/src/torrent_handle.cpp @@ -63,7 +63,7 @@ namespace std namespace libtorrent { - torrent_status torrent_handle::status() + torrent_status torrent_handle::status() const { if (m_ses == 0) throw invalid_handle(); @@ -83,6 +83,8 @@ namespace libtorrent torrent_status st; st.total_download = 0; st.total_upload = 0; + st.download_rate = 0.f; + st.upload_rate = 0.f; if (d == &m_chk->m_torrents.front()) st.state = torrent_status::checking_files; else @@ -96,11 +98,10 @@ namespace libtorrent } } - m_ses = 0; throw invalid_handle(); } - const torrent_info& torrent_handle::get_torrent_info() + const torrent_info& torrent_handle::get_torrent_info() const { if (m_ses == 0) throw invalid_handle(); @@ -116,11 +117,10 @@ namespace libtorrent if (d != 0) return d->torrent_ptr->torrent_file(); } - m_ses = 0; throw invalid_handle(); } - bool torrent_handle::is_valid() + bool torrent_handle::is_valid() const { if (m_ses == 0) return false; @@ -136,11 +136,44 @@ namespace libtorrent if (d != 0) return true; } - m_ses = 0; return false; } - void torrent_handle::get_peer_info(std::vector& v) + boost::filesystem::path torrent_handle::save_path() const + { + if (m_ses == 0) throw invalid_handle(); + + // copy the path into this local variable before + // unlocking and returning. Since we could get really + // unlucky and having the path removed after we + // have unlocked the data but before the return + // value has been copied into the destination + boost::filesystem::path ret; + + { + boost::mutex::scoped_lock l(m_ses->m_mutex); + torrent* t = m_ses->find_torrent(m_info_hash); + if (t != 0) + { + ret = t->save_path(); + return ret; + } + } + + { + boost::mutex::scoped_lock l(m_chk->m_mutex); + detail::piece_checker_data* d = m_chk->find_torrent(m_info_hash); + if (d != 0) + { + ret = d->save_path; + return ret; + } + } + + throw invalid_handle(); + } + + void torrent_handle::get_peer_info(std::vector& v) const { v.clear(); if (m_ses == 0) throw invalid_handle(); @@ -187,7 +220,7 @@ namespace libtorrent } } - void torrent_handle::get_download_queue(std::vector& queue) + void torrent_handle::get_download_queue(std::vector& queue) const { queue.clear();