diff --git a/Jamfile b/Jamfile index 02863d47c..265207753 100755 --- a/Jamfile +++ b/Jamfile @@ -12,6 +12,7 @@ SOURCES = stat.cpp storage.cpp torrent.cpp + torrent_handle.cpp torrent_info.cpp url_handler.cpp sha1.c diff --git a/docs/index.html b/docs/index.html index 245be8037..526a93185 100755 --- a/docs/index.html +++ b/docs/index.html @@ -105,7 +105,7 @@ Then the makefile should be able to do the rest.

When building (with boost 1.30.2) on linux and solaris however, I found that I had to make the following modifications to the boost.date-time library. In the file: -'boost-1.30.2/boost/date_time/gregorian_calendar.hpp' line 59. Add 'boost/date_time/' +'boost-1.30.2/boost/date_time/gregorian_calendar.hpp' line 59. Prepend 'boost/date_time/' to the include path.

@@ -215,8 +215,7 @@ refers to a character (char). So, if you want to encode entry e - -std::vector<char> buffer; +std::vector<char> buffer; bencode(std::back_insert_iterator<std::vector<char> >(buf), e); @@ -224,8 +223,7 @@ bencode(std::back_insert_iterator<std::vector<char> >(buf), e); If you want to decode a torrent file from a buffer in memory, you can do it like this:

- -std::vector<char> buffer; +std::vector<char> buffer; // ... @@ -236,8 +234,7 @@ entry e = bdecode(buf.begin(), buf.end()); Or, if you have a raw char buffer:

- -const char* buf; +const char* buf; // ... @@ -318,8 +315,7 @@ can assign the value you want it to have. The typical code to get info from a torrent file will then look like this:

- -entry torrent_file; +entry torrent_file; // ... @@ -369,6 +365,7 @@ public: entry::integer_type piece_length() const; std::size_t num_pieces() const; const sha1_hash& info_hash() const; + const std::stirng& name() const; void print(std::ostream& os) const; @@ -403,6 +400,10 @@ The print() function is there for debug purposes only. It will print th the torrent file to the given outstream.

+

+name() returns the name of the torrent. +

+

The trackers() function will return a sorted vector of announce_entry. Each announce entry contains a string, which is the tracker url, and a tier index. The @@ -544,6 +545,8 @@ struct peer_info address ip; float up_speed; float down_speed; + unsigned int total_download; + unsigned int total_upload; peer_id id; std::vector<bool> pieces; }; @@ -568,6 +571,12 @@ actual address and the port number. See address class. we have to and from this peer. These figures are updated aproximately once every second.

+

+total_download and total_upload are the total number of bytes downloaded +from and uploaded to this peer. These numbers do not include the protocol chatter, but only +the payload data. +

+

id is the peer's id as used in the bit torrent protocol. This id can be used to extract 'fingerprints' from the peer. Sometimes it can tell you which client the peer @@ -650,6 +659,42 @@ public: The iterators gives you access to individual bytes.

+

hasher

+ +

+This class creates sha1-hashes. Its declaration looks like this: +

+ +
+class hasher
+{
+public:
+	hasher();
+
+	void update(const char* data, unsigned int len);
+	sha1_hash final();
+	void reset();
+};
+
+ +

+You use it by first instantiating it, then call update() to feed it +with data. i.e. you don't have to keep the entire buffer of which you want to +create the hash in memory. You can feed the hasher parts of it at a time. When +You have fed the hasher with all the data, you call final() and it +will return the sha1-hash of the data. +

+ +

+If you want to reuse the hasher object once you have created a hash, you have to +call reset() to reinitialize it. +

+ +

+The sha1-algorithm used was implemented by Steve Reid and released as public domain. +For more info, see src/sha1.c. +

+

Credits

diff --git a/examples/client_test.cpp b/examples/client_test.cpp index 8ea130474..0b85251db 100755 --- a/examples/client_test.cpp +++ b/examples/client_test.cpp @@ -40,6 +40,21 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/session.hpp" #include "libtorrent/http_settings.hpp" +#ifdef WIN32 +#include + +bool sleep_and_input(char* c) +{ + Sleep(500); + if (kbhit()) + { + *c = getch(); + return true; + } + return false; +}; + +#endif int main(int argc, char* argv[]) { @@ -62,7 +77,7 @@ int main(int argc, char* argv[]) try { std::vector handles; - session s(6881, "ex-01"); + session s(6881, "E\x1"); s.set_http_settings(settings); for (int i = 0; i < argc-1; ++i) @@ -82,19 +97,82 @@ int main(int argc, char* argv[]) } } - while (!handles.empty()) - { - int a; - std::cin >> a; - handles.back().abort(); - handles.pop_back(); - } + std::pair prev_status + = std::make_pair(torrent_handle::invalid_handle, 0.f); + + std::vector peers; + + for (;;) + { + char c; + if (sleep_and_input(&c)) + { + if (c == 'q') break; + } + + // just print info from the first torrent + torrent_handle h = handles.front(); + + std::pair s + = h.status(); + + if (s.first == prev_status.first + && s.second == prev_status.second) + continue; + + switch(s.first) + { + case torrent_handle::checking_files: + std::cout << "checking files: "; + break; + case torrent_handle::downloading: + std::cout << "downloading: "; + break; + case torrent_handle::seeding: + std::cout << "seeding: "; + break; + }; + + std::cout.width(3); + std::cout.precision(3); + std::cout.fill('0'); + std::cout << s.second*100 << "% "; + + // calculate download and upload speeds + h.get_peer_info(peers); + float down = 0.f; + float up = 0.f; + unsigned int total_down = 0; + unsigned int total_up = 0; + int num_peers = peers.size(); + + for (std::vector::iterator i = peers.begin(); + i != peers.end(); + ++i) + { + down += i->down_speed; + up += i->up_speed; + total_down += i->total_download; + total_up += i->total_upload; + } + + std::cout.width(2); + std::cout.precision(2); + + std::cout << "p:" << num_peers; + + std::cout.width(6); + std::cout.precision(6); + + std::cout << " d:(" + << total_down/1024.f << " kB) " << down/1024.f << " kB/s up:(" + << total_up/1024.f << " kB) " << up/1024.f << " kB/s \r"; + } } catch (std::exception& e) { std::cout << e.what() << "\n"; } - return 0; } diff --git a/include/libtorrent/hasher.hpp b/include/libtorrent/hasher.hpp index e38a3d83e..3b10c7370 100755 --- a/include/libtorrent/hasher.hpp +++ b/include/libtorrent/hasher.hpp @@ -57,7 +57,14 @@ namespace libtorrent void update(const char* data, unsigned int len) { SHA1Update(&m_context, reinterpret_cast(const_cast(data)), len); } - void final(sha1_hash& digest) { SHA1Final(digest.begin(), &m_context); } + sha1_hash final() + { + sha1_hash digest; + SHA1Final(digest.begin(), &m_context); + return digest; + } + + void reset() { SHA1Init(&m_context); } private: diff --git a/include/libtorrent/peer_info.hpp b/include/libtorrent/peer_info.hpp index 690f6e6b9..14233dcec 100755 --- a/include/libtorrent/peer_info.hpp +++ b/include/libtorrent/peer_info.hpp @@ -52,6 +52,8 @@ namespace libtorrent address ip; float up_speed; float down_speed; + unsigned int total_download; + unsigned int total_upload; peer_id id; std::vector pieces; }; diff --git a/include/libtorrent/piece_picker.hpp b/include/libtorrent/piece_picker.hpp index 294014072..7828c1bde 100755 --- a/include/libtorrent/piece_picker.hpp +++ b/include/libtorrent/piece_picker.hpp @@ -113,6 +113,8 @@ namespace libtorrent bool is_piece_finished(int index) const; + int blocks_in_piece(int index) const; + int unverified_blocks() const; #ifndef NDEBUG // used in debug mode @@ -205,6 +207,14 @@ namespace libtorrent }; + inline int piece_picker::blocks_in_piece(int index) const + { + if (index+1 == m_piece_map.size()) + return m_blocks_in_last_piece; + else + return m_blocks_per_piece; + } + } #endif // TORRENT_PIECE_PICKER_HPP_INCLUDED diff --git a/include/libtorrent/session.hpp b/include/libtorrent/session.hpp index d5dfc0544..d0ddfda41 100755 --- a/include/libtorrent/session.hpp +++ b/include/libtorrent/session.hpp @@ -82,8 +82,12 @@ namespace libtorrent namespace detail { + // this data is shared between the main thread and the + // thread that initialize pieces struct piece_checker_data { + piece_checker_data(): abort(false) {} + boost::shared_ptr torrent_ptr; std::string save_path; @@ -97,7 +101,15 @@ namespace libtorrent // below in this struct boost::mutex mutex; + // is filled in by storage::initialize_pieces() + // and represents the progress. It should be a + // value in the range [0, 1] float progress; + + // abort defaults to false and is typically + // filled in by torrent_handle when the user + // aborts the torrent + bool abort; }; struct piece_check_thread @@ -128,7 +140,9 @@ namespace libtorrent // a list of all torrents that are currently checking // their files (in separate threads) - std::map > m_checkers; + std::map + > m_checkers; boost::thread_group m_checker_threads; // the peer id that is generated at the start of each torrent @@ -146,7 +160,9 @@ namespace libtorrent void run(int listen_port); - torrent* find_torrent(const sha1_hash& info_hash); + torrent* find_active_torrent(const sha1_hash& info_hash); + detail::piece_checker_data* find_checking_torrent(const sha1_hash& info_hash); + const peer_id& get_peer_id() const { return m_peer_id; } #if defined(TORRENT_VERBOSE_LOGGING) @@ -215,8 +231,8 @@ namespace libtorrent // data shared between the threads detail::session_impl m_impl; + // the main working thread boost::thread m_thread; - }; } diff --git a/include/libtorrent/socket.hpp b/include/libtorrent/socket.hpp index a8bf52e92..56d2039bf 100755 --- a/include/libtorrent/socket.hpp +++ b/include/libtorrent/socket.hpp @@ -249,6 +249,8 @@ namespace libtorrent , std::vector >& writable , std::vector >& error); + int count_read_monitors() const { return m_readable.size(); } + private: std::vector > m_readable; diff --git a/include/libtorrent/stat.hpp b/include/libtorrent/stat.hpp index bff5bc21e..0d1d6544c 100755 --- a/include/libtorrent/stat.hpp +++ b/include/libtorrent/stat.hpp @@ -73,7 +73,7 @@ namespace libtorrent float up_peak() const { return m_peak_uploaded_per_second; } unsigned int total_upload() const { return m_total_upload; } - unsigned int total_dowload() const { return m_total_download; } + unsigned int total_download() const { return m_total_download; } private: diff --git a/include/libtorrent/storage.hpp b/include/libtorrent/storage.hpp index 32dae91be..7a75ea829 100755 --- a/include/libtorrent/storage.hpp +++ b/include/libtorrent/storage.hpp @@ -60,7 +60,10 @@ POSSIBILITY OF SUCH DAMAGE. namespace libtorrent { - + namespace detail + { + class piece_checker_data; + } class session; struct file_allocation_failed: std::exception @@ -140,7 +143,9 @@ namespace libtorrent friend class piece_file; public: - void initialize_pieces(torrent* t, const boost::filesystem::path& path); + void initialize_pieces(torrent* t, + const boost::filesystem::path& path, + boost::shared_ptr data); int bytes_left() const { return m_bytes_left; } diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index 7d74f771b..16fb09df8 100755 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -100,9 +100,10 @@ namespace libtorrent void print(std::ostream& os) const; - void allocate_files(const std::string& save_path) + void allocate_files(boost::shared_ptr data, + const std::string& save_path) { - m_storage.initialize_pieces(this, save_path); + m_storage.initialize_pieces(this, save_path, data); m_picker.files_checked(m_storage.pieces()); #ifndef NDEBUG m_picker.integrity_check(this); @@ -214,6 +215,10 @@ namespace libtorrent logger* spawn_logger(const char* title); #endif + // the number of blocks downloaded + // that hasn't been verified yet + int m_unverified_blocks; + private: void try_next_tracker(); diff --git a/include/libtorrent/torrent_handle.hpp b/include/libtorrent/torrent_handle.hpp index 30ba3600f..216d5212e 100755 --- a/include/libtorrent/torrent_handle.hpp +++ b/include/libtorrent/torrent_handle.hpp @@ -64,6 +64,9 @@ namespace libtorrent }; std::pair status() const; + // TODO: add a 'time to next announce' query. + + private: torrent_handle(detail::session_impl* s, const sha1_hash& h) diff --git a/src/peer_connection.cpp b/src/peer_connection.cpp index 53cba8912..3d568820c 100755 --- a/src/peer_connection.cpp +++ b/src/peer_connection.cpp @@ -385,10 +385,13 @@ bool libtorrent::peer_connection::dispatch_message() // pop the request that just finished // from the download queue m_download_queue.erase(m_download_queue.begin()); + m_torrent->m_unverified_blocks++; // did we just finish the piece? if (picker.is_piece_finished(index)) { + m_torrent->m_unverified_blocks -= picker.blocks_in_piece(index); + bool verified = m_torrent->filesystem()->verify_piece(m_receiving_piece); if (verified) { @@ -563,199 +566,205 @@ void libtorrent::peer_connection::send_have(int index) // throws exception when the client should be disconnected void libtorrent::peer_connection::receive_data() { - int received = m_socket->receive(&m_recv_buffer[m_recv_pos], m_packet_size - m_recv_pos); - -// (*m_logger) << "<== RECV [ size: " << received << " ]\n"; - - if (received == 0) throw network_error(0); - if (received < 0) + for(;;) { - if (m_socket->last_error() == socket::would_block) return; - // the connection was closed - throw network_error(0); - } + int received = m_socket->receive(&m_recv_buffer[m_recv_pos], m_packet_size - m_recv_pos); - if (received > 0) - { - m_statistics.received_bytes(received); - m_last_receive = boost::posix_time::second_clock::local_time(); + // connection closed + if (received == 0) throw network_error(0); - m_recv_pos += received; - - if (m_recv_pos == m_packet_size) + // an error + if (received < 0) { - switch(m_state) + // would block means that no data was ready to be received + if (m_socket->last_error() == socket::would_block) return; + + // the connection was closed + throw network_error(0); + } + + if (received > 0) + { + m_statistics.received_bytes(received); + m_last_receive = boost::posix_time::second_clock::local_time(); + + m_recv_pos += received; + + if (m_recv_pos == m_packet_size) { - case read_protocol_length: - m_packet_size = reinterpret_cast(m_recv_buffer[0]); -#if defined(TORRENT_VERBOSE_LOGGING) - (*m_logger) << m_socket->sender().as_string() << " protocol length: " << m_packet_size << "\n"; -#endif - m_state = read_protocol_version; - if (m_packet_size != 19) - throw network_error(0); - m_recv_buffer.resize(m_packet_size); - m_recv_pos = 0; - break; - - - case read_protocol_version: + switch(m_state) { - const char* protocol_version = "BitTorrent protocol"; -#if defined(TORRENT_VERBOSE_LOGGING) - (*m_logger) << m_socket->sender().as_string() << " protocol name: " << std::string(m_recv_buffer.begin(), m_recv_buffer.end()) << "\n"; -#endif - if (!std::equal(m_recv_buffer.begin(), m_recv_buffer.end(), protocol_version)) - { - // unknown protocol, close connection + case read_protocol_length: + m_packet_size = reinterpret_cast(m_recv_buffer[0]); + #if defined(TORRENT_VERBOSE_LOGGING) + (*m_logger) << m_socket->sender().as_string() << " protocol length: " << m_packet_size << "\n"; + #endif + m_state = read_protocol_version; + if (m_packet_size != 19) throw network_error(0); - } - m_state = read_info_hash; - m_packet_size = 28; + m_recv_buffer.resize(m_packet_size); m_recv_pos = 0; - m_recv_buffer.resize(28); - } - break; + break; - case read_info_hash: - { - // ok, now we have got enough of the handshake. Is this connection - // attached to a torrent? + case read_protocol_version: + { + const char* protocol_version = "BitTorrent protocol"; + #if defined(TORRENT_VERBOSE_LOGGING) + (*m_logger) << m_socket->sender().as_string() << " protocol name: " << std::string(m_recv_buffer.begin(), m_recv_buffer.end()) << "\n"; + #endif + if (!std::equal(m_recv_buffer.begin(), m_recv_buffer.end(), protocol_version)) + { + // unknown protocol, close connection + throw network_error(0); + } + m_state = read_info_hash; + m_packet_size = 28; + m_recv_pos = 0; + m_recv_buffer.resize(28); + } + break; - if (m_torrent == 0) + + case read_info_hash: { - // no, we have to see if there's a torrent with the - // info_hash we got from the peer - 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); + // ok, now we have got enough of the handshake. Is this connection + // attached to a torrent? + if (m_torrent == 0) { - // we couldn't find the torrent! -#if defined(TORRENT_VERBOSE_LOGGING) - (*m_logger) << m_socket->sender().as_string() << " couldn't find a torrent with the given info_hash\n"; -#endif - throw network_error(0); + // no, we have to see if there's a torrent with the + // info_hash we got from the peer + 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_active_torrent(info_hash); + if (m_torrent == 0) + { + // we couldn't find the torrent! + #if defined(TORRENT_VERBOSE_LOGGING) + (*m_logger) << m_socket->sender().as_string() << " couldn't find a torrent with the given info_hash\n"; + #endif + throw network_error(0); + } + m_torrent->attach_peer(this); + + // assume the other end has no pieces + m_have_piece.resize(m_torrent->torrent_file().num_pieces()); + std::fill(m_have_piece.begin(), m_have_piece.end(), false); + + // yes, we found the torrent + // reply with our handshake + std::copy(m_recv_buffer.begin()+28, m_recv_buffer.begin() + 48, (char*)m_peer_id.begin()); + send_handshake(); + send_bitfield(); } - m_torrent->attach_peer(this); - - // assume the other end has no pieces - m_have_piece.resize(m_torrent->torrent_file().num_pieces()); - std::fill(m_have_piece.begin(), m_have_piece.end(), false); - - // yes, we found the torrent - // reply with our handshake - std::copy(m_recv_buffer.begin()+28, m_recv_buffer.begin() + 48, (char*)m_peer_id.begin()); - send_handshake(); - send_bitfield(); - } - else - { - // verify info hash - if (!std::equal(m_recv_buffer.begin()+8, m_recv_buffer.begin() + 28, (const char*)m_torrent->torrent_file().info_hash().begin())) + else { -#if defined(TORRENT_VERBOSE_LOGGING) - (*m_logger) << m_socket->sender().as_string() << " received invalid info_hash\n"; -#endif - throw network_error(0); + // verify info hash + if (!std::equal(m_recv_buffer.begin()+8, m_recv_buffer.begin() + 28, (const char*)m_torrent->torrent_file().info_hash().begin())) + { + #if defined(TORRENT_VERBOSE_LOGGING) + (*m_logger) << m_socket->sender().as_string() << " received invalid info_hash\n"; + #endif + throw network_error(0); + } } + + m_state = read_peer_id; + m_packet_size = 20; + m_recv_pos = 0; + m_recv_buffer.resize(20); + #if defined(TORRENT_VERBOSE_LOGGING) + (*m_logger) << m_socket->sender().as_string() << " info_hash received\n"; + #endif + break; } - m_state = read_peer_id; - m_packet_size = 20; - m_recv_pos = 0; - m_recv_buffer.resize(20); -#if defined(TORRENT_VERBOSE_LOGGING) - (*m_logger) << m_socket->sender().as_string() << " info_hash received\n"; -#endif - break; - } - - case read_peer_id: - { - if (m_active) + case read_peer_id: { - // verify peer_id - // TODO: It seems like the original client ignores to check the peer id - // can this be correct? - if (!std::equal(m_recv_buffer.begin(), m_recv_buffer.begin() + 20, (const char*)m_peer_id.begin())) + if (m_active) { -#if defined(TORRENT_VERBOSE_LOGGING) - (*m_logger) << m_socket->sender().as_string() << " invalid peer_id (it doesn't equal the one from the tracker)\n"; -#endif - throw network_error(0); + // verify peer_id + // TODO: It seems like the original client ignores to check the peer id + // can this be correct? + if (!std::equal(m_recv_buffer.begin(), m_recv_buffer.begin() + 20, (const char*)m_peer_id.begin())) + { + #if defined(TORRENT_VERBOSE_LOGGING) + (*m_logger) << m_socket->sender().as_string() << " invalid peer_id (it doesn't equal the one from the tracker)\n"; + #endif + throw network_error(0); + } } - } - else - { - // check to make sure we don't have another connection with the same - // info_hash and peer_id. If we do. close this connection. - std::copy(m_recv_buffer.begin(), m_recv_buffer.begin() + 20, (char*)m_peer_id.begin()); - if (m_torrent->num_connections(m_peer_id) > 1) + else { -#if defined(TORRENT_VERBOSE_LOGGING) - (*m_logger) << m_socket->sender().as_string() << " duplicate connection, closing\n"; -#endif - throw network_error(0); + // check to make sure we don't have another connection with the same + // info_hash and peer_id. If we do. close this connection. + std::copy(m_recv_buffer.begin(), m_recv_buffer.begin() + 20, (char*)m_peer_id.begin()); + if (m_torrent->num_connections(m_peer_id) > 1) + { + #if defined(TORRENT_VERBOSE_LOGGING) + (*m_logger) << m_socket->sender().as_string() << " duplicate connection, closing\n"; + #endif + throw network_error(0); + } } - } - m_state = read_packet_size; - m_packet_size = 4; - m_recv_pos = 0; - m_recv_buffer.resize(4); -#if defined(TORRENT_VERBOSE_LOGGING) - (*m_logger) << m_socket->sender().as_string() << " received peer_id\n"; -#endif - break; - } - - - case read_packet_size: - // 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 - if (m_packet_size > 1024*1024 || m_packet_size < 0) - { -#if defined(TORRENT_VERBOSE_LOGGING) - (*m_logger) << m_socket->sender().as_string() << " packet too large (packet_size > 1 Megabyte), abort\n"; -#endif - // packet too large - throw network_error(0); - } - - if (m_packet_size == 0) - { - // keepalive message m_state = read_packet_size; m_packet_size = 4; - } - else - { - m_state = read_packet; - m_recv_buffer.resize(m_packet_size); - } - m_recv_pos = 0; - break; - - case read_packet: - if (!dispatch_message()) - { -#if defined(TORRENT_VERBOSE_LOGGING) - (*m_logger) << m_socket->sender().as_string() << " received invalid packet\n"; -#endif - // invalid message - throw network_error(0); + m_recv_pos = 0; + m_recv_buffer.resize(4); + #if defined(TORRENT_VERBOSE_LOGGING) + (*m_logger) << m_socket->sender().as_string() << " received peer_id\n"; + #endif + break; } - m_state = read_packet_size; - m_packet_size = 4; - m_recv_buffer.resize(4); - m_recv_pos = 0; - break; + + case read_packet_size: + // 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 + if (m_packet_size > 1024*1024 || m_packet_size < 0) + { + #if defined(TORRENT_VERBOSE_LOGGING) + (*m_logger) << m_socket->sender().as_string() << " packet too large (packet_size > 1 Megabyte), abort\n"; + #endif + // packet too large + throw network_error(0); + } + + if (m_packet_size == 0) + { + // keepalive message + m_state = read_packet_size; + m_packet_size = 4; + } + else + { + m_state = read_packet; + m_recv_buffer.resize(m_packet_size); + } + m_recv_pos = 0; + break; + + case read_packet: + if (!dispatch_message()) + { + #if defined(TORRENT_VERBOSE_LOGGING) + (*m_logger) << m_socket->sender().as_string() << " received invalid packet\n"; + #endif + // invalid message + throw network_error(0); + } + + m_state = read_packet_size; + m_packet_size = 4; + m_recv_buffer.resize(4); + m_recv_pos = 0; + break; + } } } } diff --git a/src/piece_picker.cpp b/src/piece_picker.cpp index 9b226714f..4570b34b1 100755 --- a/src/piece_picker.cpp +++ b/src/piece_picker.cpp @@ -452,8 +452,7 @@ namespace libtorrent = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(index)); assert(i != m_downloads.end()); assert(i->finished_blocks.count() <= m_blocks_per_piece); - int max_blocks = m_blocks_per_piece; - if (index+1 == m_piece_map.size()) max_blocks = m_blocks_in_last_piece; + int max_blocks = blocks_in_piece(index); if (i->finished_blocks.count() != max_blocks) return false; assert(i->requested_blocks.count() == max_blocks); @@ -561,4 +560,16 @@ namespace libtorrent #endif } + int piece_picker::unverified_blocks() const + { + int counter = 0; + for (std::vector::const_iterator i = m_downloads.begin(); + i != m_downloads.end(); + ++i) + { + counter += i->finished_blocks.count(); + } + return counter; + } + } diff --git a/src/session.cpp b/src/session.cpp index 6d8c7d523..5d8832a77 100755 --- a/src/session.cpp +++ b/src/session.cpp @@ -172,7 +172,9 @@ namespace libtorrent boost::mutex::scoped_lock l(m_mutex); - std::cout << "peers: " << m_connections.size() << "\n"; + // +1 for the listen socket + assert(m_selector.count_read_monitors() == m_connections.size() + 1); + if (m_abort) { m_tracker_manager.abort_all_requests(); @@ -407,16 +409,21 @@ namespace libtorrent // the return value from this function is valid only as long as the // session is locked! - torrent* session_impl::find_torrent(const sha1_hash& info_hash) + torrent* session_impl::find_active_torrent(const sha1_hash& info_hash) { std::map >::iterator i = m_torrents.find(info_hash); if (i != m_torrents.end()) return boost::get_pointer(i->second); + return 0; + } - std::map >::iterator j + piece_checker_data* session_impl::find_checking_torrent(const sha1_hash& info_hash) + { + std::map >::iterator i = m_checkers.find(info_hash); - if (j != m_checkers.end()) - return boost::get_pointer(j->second->torrent_ptr); + + if (i != m_checkers.end()) + return boost::get_pointer(i->second); return 0; } @@ -431,7 +438,7 @@ namespace libtorrent // new allocation model) try { - m_data->torrent_ptr->allocate_files(m_data->save_path); + m_data->torrent_ptr->allocate_files(m_data, m_data->save_path); } catch(...) { @@ -442,6 +449,10 @@ namespace libtorrent session_impl* ses = m_data->ses; boost::mutex::scoped_lock l(ses->m_mutex); +#ifndef NDEBUG + std::cout << "adding torrent to session!\n"; +#endif + ses->m_torrents.insert( std::make_pair(m_data->info_hash, m_data->torrent_ptr)).first; @@ -453,7 +464,8 @@ namespace libtorrent } - torrent_handle session::add_torrent(const torrent_info& ti, const std::string& save_path) + torrent_handle session::add_torrent(const torrent_info& ti, + const std::string& save_path) { // lock the session boost::mutex::scoped_lock l(m_impl.m_mutex); @@ -470,6 +482,8 @@ namespace libtorrent // create the torrent and the data associated with // the checker thread and store it before starting // the thread + // TODO: have a queue of checking torrents instead of + // having them all run at the same time boost::shared_ptr torrent_ptr(new torrent(&m_impl, ti)); boost::shared_ptr d(new detail::piece_checker_data); @@ -498,63 +512,8 @@ namespace libtorrent } m_thread.join(); - } - std::pair torrent_handle::status() const - { - if (m_ses == 0) return std::make_pair(invalid_handle, 0.f); - - boost::mutex::scoped_lock l(m_ses->m_mutex); - - std::map >::iterator i = m_ses->m_torrents.find(m_info_hash); - if (i == m_ses->m_torrents.end()) return std::make_pair(invalid_handle, 0.f); - return i->second->status(); - } - - void torrent_handle::get_peer_info(std::vector& v) - { - v.clear(); - if (m_ses == 0) return; - boost::mutex::scoped_lock l(m_ses->m_mutex); - - std::map >::iterator i = m_ses->m_torrents.find(m_info_hash); - if (i == m_ses->m_torrents.end()) return; - - const torrent* t = boost::get_pointer(i->second); - - for (std::vector::const_iterator i = t->begin(); - i != t->end(); - ++i) - { - peer_connection* peer = *i; - v.push_back(peer_info()); - peer_info& p = v.back(); - - const stat& statistics = peer->statistics(); - p.down_speed = statistics.download_rate(); - p.up_speed = statistics.upload_rate(); - p.id = peer->get_peer_id(); - p.ip = peer->get_socket()->sender(); - - p.flags = 0; - if (peer->is_interesting()) p.flags |= peer_info::interesting; - if (peer->has_choked()) p.flags |= peer_info::choked; - if (peer->is_peer_interested()) p.flags |= peer_info::remote_interested; - if (peer->has_peer_choked()) p.flags |= peer_info::remote_choked; - - p.pieces = peer->get_bitfield(); - } - } - - void torrent_handle::abort() - { - if (m_ses == 0) return; - boost::mutex::scoped_lock l(m_ses->m_mutex); - - std::map >::iterator i = m_ses->m_torrents.find(m_info_hash); - if (i == m_ses->m_torrents.end()) return; - i->second->abort(); - m_ses = 0; + // TODO: join the checking threads! } // TODO: document diff --git a/src/storage.cpp b/src/storage.cpp index 52a0fa5d0..1503ed9ac 100755 --- a/src/storage.cpp +++ b/src/storage.cpp @@ -41,10 +41,12 @@ POSSIBILITY OF SUCH DAMAGE. #include #include +#include #include "libtorrent/storage.hpp" #include "libtorrent/torrent.hpp" #include "libtorrent/hasher.hpp" +#include "libtorrent/session.hpp" #if defined(_MSC_VER) && _MSV_CER < 1300 #define for if (false) {} else for @@ -105,7 +107,7 @@ void libtorrent::piece_file::open(storage* s, int index, open_mode o, int seek_o m_file_mode = m; m_file.open(p.native_file_string().c_str(), m_file_mode); - std::cout << "opening file: '" << p.native_file_string() << "'\n"; +// std::cout << "opening file: '" << p.native_file_string() << "'\n"; if (m_file.fail()) { // TODO: try to recover! create a new file? @@ -168,7 +170,7 @@ int libtorrent::piece_file::read(char* buf, int size) m_file.close(); m_file.clear(); m_file.open(path.native_file_string().c_str(), m_file_mode); - std::cout << "opening file: '" << path.native_file_string() << "'\n"; +// std::cout << "opening file: '" << path.native_file_string() << "'\n"; if (m_file.fail()) { // TODO: try to recover! create a new file? @@ -221,7 +223,7 @@ void libtorrent::piece_file::write(const char* buf, int size) m_file_offset = 0; m_file.close(); m_file.open(path.native_file_string().c_str(), m_file_mode); - std::cout << "opening file: '" << path.native_file_string() << "'\n"; +// std::cout << "opening file: '" << path.native_file_string() << "'\n"; if (m_file.fail()) { // TODO: try to recover! create a new file? @@ -294,10 +296,9 @@ bool libtorrent::storage::verify_piece(piece_file& file) assert(read == m_torrent_file->piece_size(index)); // calculate hash for piece - sha1_hash digest; hasher h; h.update(&buffer[0], read); - h.final(digest); + sha1_hash digest = h.final(); if (std::equal(digest.begin(), digest.end(), m_torrent_file->hash_for_piece(index).begin())) { @@ -325,7 +326,13 @@ bool libtorrent::storage::verify_piece(piece_file& file) // allocate files will create all files that are missing // if there are some files that already exists, it checks // that they have the correct filesize -void libtorrent::storage::initialize_pieces(torrent* t, const boost::filesystem::path& path) +// data is the structure that is shared between the +// thread where this function is run in and the +// main thread. It is used to communicate progress +// and abortion information. +void libtorrent::storage::initialize_pieces(torrent* t, + const boost::filesystem::path& path, + boost::shared_ptr data) { m_save_path = path; m_torrent_file = &t->torrent_file(); @@ -366,13 +373,17 @@ void libtorrent::storage::initialize_pieces(torrent* t, const boost::filesystem: // have bool resume = false; + unsigned int total_bytes = m_torrent_file->total_size(); + unsigned int progress = 0; + // the buffersize of the file writes const int chunksize = 8192; char zeros[chunksize]; std::fill(zeros, zeros+chunksize, 0); - +#ifndef NDEBUG std::cout << "allocating files\n"; +#endif // remember which directories we have created, so // we don't have to ask the filesystem all the time @@ -413,7 +424,7 @@ void libtorrent::storage::initialize_pieces(torrent* t, const boost::filesystem: msg += "\" is in the way."; throw file_allocation_failed(msg.c_str()); } - std::cout << "creating file: '" << path.native_file_string() << "'\n"; +// std::cout << "creating file: '" << path.native_file_string() << "'\n"; std::ifstream f(path.native_file_string().c_str(), std::ios_base::binary); f.seekg(0, std::ios_base::end); int filesize = f.tellg(); @@ -438,9 +449,19 @@ void libtorrent::storage::initialize_pieces(torrent* t, const boost::filesystem: f.write(zeros, chunksize); // TODO: Check if disk is full left_to_write -= chunksize; + progress += chunksize; + + boost::mutex::scoped_lock l(data->mutex); + data->progress = static_cast(progress) / total_bytes; + if (data->abort) return; } // TODO: Check if disk is full if (left_to_write > 0) f.write(zeros, left_to_write); + progress += left_to_write; + + boost::mutex::scoped_lock l(data->mutex); + data->progress = static_cast(progress) / total_bytes; + if (data->abort) return; } } @@ -448,21 +469,31 @@ void libtorrent::storage::initialize_pieces(torrent* t, const boost::filesystem: if (resume) { int missing = 0; - std::cout << "checking existing files\n"; +// std::cout << "checking existing files\n"; int num_pieces = m_torrent_file->num_pieces(); + progress = 0; piece_file f; for (unsigned int i = 0; i < num_pieces; ++i) { f.open(this, i, piece_file::in); if (!verify_piece(f)) missing++; - std::cout << i+1 << " / " << m_torrent_file->num_pieces() << " missing: " << missing << "\r"; +// std::cout << i+1 << " / " << m_torrent_file->num_pieces() << " missing: " << missing << "\r"; + + progress += m_torrent_file->piece_size(i); + boost::mutex::scoped_lock l(data->mutex); + data->progress = static_cast(progress) / total_bytes; + if (data->abort) return; } - std::cout << "\n"; +// std::cout << "\n"; } +#ifndef NDEBUG + std::cout << "allocation/checking DONE!\n"; +#endif + } /* // reads the piece with the given index from disk diff --git a/src/torrent.cpp b/src/torrent.cpp index b31a7d70c..93fa3beba 100755 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -37,6 +37,7 @@ POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #include #include @@ -142,6 +143,7 @@ namespace libtorrent , m_bytes_uploaded(0) , m_bytes_downloaded(0) , m_torrent_file(torrent_file) + , m_unverified_blocks(0) , m_next_request(boost::posix_time::second_clock::local_time()) , m_duration(1800) , m_policy(new policy(this)) @@ -401,12 +403,26 @@ namespace libtorrent } } - // TODO: temporary implementation. Should count the actually - // verified pieces and should support the different states - // a torrent can be in. std::pair torrent::status() const { - return std::make_pair(torrent_handle::downloading, 0.f); + // TODO: report progress on block-level. + // make sure to handle the case where a piece + // fails the hash-check + const std::vector& p = m_storage.pieces(); + int num_pieces = std::accumulate(p.begin(), p.end(), 0); + if (num_pieces == p.size()) + return std::make_pair(torrent_handle::seeding, 1.f); + + 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; + + assert(m_unverified_blocks == m_picker.unverified_blocks()); + + return std::make_pair(torrent_handle::downloading, + (num_pieces * blocks_per_piece + m_unverified_blocks) + / static_cast(total_blocks)); } } diff --git a/src/torrent_handle.cpp b/src/torrent_handle.cpp new file mode 100755 index 000000000..9a010c414 --- /dev/null +++ b/src/torrent_handle.cpp @@ -0,0 +1,153 @@ +/* + +Copyright (c) 2003, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "libtorrent/peer_id.hpp" +#include "libtorrent/torrent_info.hpp" +#include "libtorrent/url_handler.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/hasher.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/session.hpp" + +#if defined(_MSC_VER) && _MSC_VER < 1300 +namespace std +{ + using ::srand; + using ::isprint; +}; +#endif + + +namespace libtorrent +{ + + std::pair torrent_handle::status() const + { + if (m_ses == 0) return std::make_pair(invalid_handle, 0.f); + + boost::mutex::scoped_lock l(m_ses->m_mutex); + + torrent* t = m_ses->find_active_torrent(m_info_hash); + if (t != 0) return t->status(); + + detail::piece_checker_data* d = m_ses->find_checking_torrent(m_info_hash); + + if (d != 0) + { + boost::mutex::scoped_lock l(d->mutex); + return std::make_pair(checking_files, d->progress); + } + + return std::make_pair(invalid_handle, 0.f); + } + + void torrent_handle::get_peer_info(std::vector& v) + { + v.clear(); + if (m_ses == 0) return; + boost::mutex::scoped_lock l(m_ses->m_mutex); + + std::map >::iterator i = m_ses->m_torrents.find(m_info_hash); + if (i == m_ses->m_torrents.end()) return; + + const torrent* t = boost::get_pointer(i->second); + + for (std::vector::const_iterator i = t->begin(); + i != t->end(); + ++i) + { + peer_connection* peer = *i; + v.push_back(peer_info()); + peer_info& p = v.back(); + + const stat& statistics = peer->statistics(); + p.down_speed = statistics.download_rate(); + p.up_speed = statistics.upload_rate(); + p.id = peer->get_peer_id(); + p.ip = peer->get_socket()->sender(); + + p.total_download = statistics.total_download(); + p.total_upload = statistics.total_upload(); + + p.flags = 0; + if (peer->is_interesting()) p.flags |= peer_info::interesting; + if (peer->has_choked()) p.flags |= peer_info::choked; + if (peer->is_peer_interested()) p.flags |= peer_info::remote_interested; + if (peer->has_peer_choked()) p.flags |= peer_info::remote_choked; + + p.pieces = peer->get_bitfield(); + } + } + + void torrent_handle::abort() + { + if (m_ses == 0) return; + boost::mutex::scoped_lock l(m_ses->m_mutex); + + torrent* t = m_ses->find_active_torrent(m_info_hash); + if (t != 0) + { + t->abort(); + m_ses = 0; + return; + } + + detail::piece_checker_data* d = m_ses->find_checking_torrent(m_info_hash); + + if (d != 0) + { + boost::mutex::scoped_lock l(d->mutex); + d->abort = true; + // remove the checker. It will abort itself and + // close the thread now. + m_ses->m_checkers.erase(m_ses->m_checkers.find(m_info_hash)); + m_ses = 0; + return; + } + + m_ses = 0; + } + +} diff --git a/src/torrent_info.cpp b/src/torrent_info.cpp index deba6b622..44fa97780 100755 --- a/src/torrent_info.cpp +++ b/src/torrent_info.cpp @@ -139,7 +139,7 @@ namespace libtorrent bencode(std::back_insert_iterator >(buf), info); hasher h; h.update(&buf[0], buf.size()); - h.final(m_info_hash); + m_info_hash = h.final(); // extract piece length i = info.dict().find("piece length");