*** empty log message ***

This commit is contained in:
Arvid Norberg 2003-10-29 23:28:09 +00:00
parent 1bd0a8234a
commit d7f92afea3
19 changed files with 624 additions and 271 deletions

View File

@ -12,6 +12,7 @@ SOURCES =
stat.cpp
storage.cpp
torrent.cpp
torrent_handle.cpp
torrent_info.cpp
url_handler.cpp
sha1.c

View File

@ -105,7 +105,7 @@ Then the makefile should be able to do the rest.
<p>
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.
</p>
@ -215,8 +215,7 @@ refers to a character (<tt>char</tt>). So, if you want to encode entry <tt>e</tt
into a buffer in memory, you can do it like this:
</p>
<code>
std::vector&lt;char&gt; buffer;
<code>std::vector&lt;char&gt; buffer;
bencode(std::back_insert_iterator&lt;std::vector&lt;char&gt; &gt;(buf), e);
</code>
@ -224,8 +223,7 @@ bencode(std::back_insert_iterator&lt;std::vector&lt;char&gt; &gt;(buf), e);
If you want to decode a torrent file from a buffer in memory, you can do it like this:
</p>
<code>
std::vector&lt;char&gt; buffer;
<code>std::vector&lt;char&gt; buffer;
// ...
@ -236,8 +234,7 @@ entry e = bdecode(buf.begin(), buf.end());
Or, if you have a raw char buffer:
</p>
<code>
const char* buf;
<code>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:
</p>
<code>
entry torrent_file;
<code>entry torrent_file;
// ...
@ -369,6 +365,7 @@ public:
entry::integer_type piece_length() const;
std::size_t num_pieces() const;
const sha1_hash&amp; info_hash() const;
const std::stirng&amp; name() const;
void print(std::ostream&amp; os) const;
@ -403,6 +400,10 @@ The <tt>print()</tt> function is there for debug purposes only. It will print th
the torrent file to the given outstream.
</p>
<p>
<tt>name()</tt> returns the name of the torrent.
</p>
<p>
The <tt>trackers()</tt> function will return a sorted vector of <tt>announce_entry</tt>.
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&lt;bool&gt; pieces;
};
@ -568,6 +571,12 @@ actual address and the port number. See <a href"#address">address</a> class.
we have to and from this peer. These figures are updated aproximately once every second.
</p>
<p>
<tt>total_download</tt> and <tt>total_upload</tt> 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.
</p>
<p>
<tt>id</tt> 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.
</p>
<h2>hasher</h2>
<p>
This class creates sha1-hashes. Its declaration looks like this:
</p>
<pre>
class hasher
{
public:
hasher();
void update(const char* data, unsigned int len);
sha1_hash final();
void reset();
};
</pre>
<p>
You use it by first instantiating it, then call <tt>update()</tt> 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 <tt>final()</tt> and it
will return the sha1-hash of the data.
</p>
<p>
If you want to reuse the hasher object once you have created a hash, you have to
call <tt>reset()</tt> to reinitialize it.
</p>
<p>
The sha1-algorithm used was implemented by Steve Reid and released as public domain.
For more info, see <tt>src/sha1.c</tt>.
</p>
<h1>Credits</h1>
<p>

View File

@ -40,6 +40,21 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/session.hpp"
#include "libtorrent/http_settings.hpp"
#ifdef WIN32
#include <conio.h>
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<torrent_handle> 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<torrent_handle::state_t, float> prev_status
= std::make_pair(torrent_handle::invalid_handle, 0.f);
std::vector<peer_info> 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<torrent_handle::state_t, float> 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<peer_info>::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;
}

View File

@ -57,7 +57,14 @@ namespace libtorrent
void update(const char* data, unsigned int len)
{ SHA1Update(&m_context, reinterpret_cast<unsigned char*>(const_cast<char*>(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:

View File

@ -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<bool> pieces;
};

View File

@ -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

View File

@ -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> 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<sha1_hash, boost::shared_ptr<piece_checker_data> > m_checkers;
std::map<sha1_hash,
boost::shared_ptr<detail::piece_checker_data>
> 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;
};
}

View File

@ -249,6 +249,8 @@ namespace libtorrent
, std::vector<boost::shared_ptr<socket> >& writable
, std::vector<boost::shared_ptr<socket> >& error);
int count_read_monitors() const { return m_readable.size(); }
private:
std::vector<boost::shared_ptr<socket> > m_readable;

View File

@ -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:

View File

@ -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<detail::piece_checker_data> data);
int bytes_left() const { return m_bytes_left; }

View File

@ -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<detail::piece_checker_data> 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();

View File

@ -64,6 +64,9 @@ namespace libtorrent
};
std::pair<state_t, float> status() const;
// TODO: add a 'time to next announce' query.
private:
torrent_handle(detail::session_impl* s, const sha1_hash& h)

View File

@ -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<unsigned char&>(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<unsigned char&>(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;
}
}
}
}

View File

@ -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<downloading_piece>::const_iterator i = m_downloads.begin();
i != m_downloads.end();
++i)
{
counter += i->finished_blocks.count();
}
return counter;
}
}

View File

@ -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<sha1_hash, boost::shared_ptr<torrent> >::iterator i
= m_torrents.find(info_hash);
if (i != m_torrents.end()) return boost::get_pointer(i->second);
return 0;
}
std::map<sha1_hash, boost::shared_ptr<detail::piece_checker_data> >::iterator j
piece_checker_data* session_impl::find_checking_torrent(const sha1_hash& info_hash)
{
std::map<sha1_hash, boost::shared_ptr<detail::piece_checker_data> >::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> torrent_ptr(new torrent(&m_impl, ti));
boost::shared_ptr<detail::piece_checker_data> d(new detail::piece_checker_data);
@ -498,63 +512,8 @@ namespace libtorrent
}
m_thread.join();
}
std::pair<torrent_handle::state_t, float> 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<sha1_hash, boost::shared_ptr<torrent> >::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<peer_info>& v)
{
v.clear();
if (m_ses == 0) return;
boost::mutex::scoped_lock l(m_ses->m_mutex);
std::map<sha1_hash, boost::shared_ptr<torrent> >::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<peer_connection*>::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<sha1_hash, boost::shared_ptr<torrent> >::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

View File

@ -41,10 +41,12 @@ POSSIBILITY OF SUCH DAMAGE.
#include <boost/lexical_cast.hpp>
#include <boost/filesystem/convenience.hpp>
#include <boost/thread/mutex.hpp>
#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<detail::piece_checker_data> 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<float>(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<float>(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<float>(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

View File

@ -37,6 +37,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include <algorithm>
#include <set>
#include <cctype>
#include <numeric>
#include <boost/lexical_cast.hpp>
#include <boost/filesystem/convenience.hpp>
@ -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_handle::state_t, float> 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<bool>& 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<float>(total_blocks));
}
}

153
src/torrent_handle.cpp Executable file
View File

@ -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 <ctime>
#include <iostream>
#include <fstream>
#include <iomanip>
#include <iterator>
#include <algorithm>
#include <set>
#include <cctype>
#include <algorithm>
#include <boost/lexical_cast.hpp>
#include <boost/filesystem/convenience.hpp>
#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::state_t, float> 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<peer_info>& v)
{
v.clear();
if (m_ses == 0) return;
boost::mutex::scoped_lock l(m_ses->m_mutex);
std::map<sha1_hash, boost::shared_ptr<torrent> >::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<peer_connection*>::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;
}
}

View File

@ -139,7 +139,7 @@ namespace libtorrent
bencode(std::back_insert_iterator<std::vector<char> >(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");