From d7f92afea367d821d70ff9996be6c79e937d4c57 Mon Sep 17 00:00:00 2001
From: Arvid Norberg
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.
-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");