@@ -105,6 +108,8 @@ peers in a separate fast-resume file.
supports the no_peer_id=1 extension that will ease the load off trackers.
supports the udp-tracker protocol.
possibility to limit the number of connections.
+
delays have messages if there's no other outgoing traffic to the peer, and doesn't
+send have messages to peers that already has the piece. This saves bandwidth.
Functions that are yet to be implemented:
@@ -136,9 +141,6 @@ boost.filesystem, boost.date_time and various other boost libraries as well as z
-
Whwn building in release mode you need to define NDEBUG in order to avoid
-all asserts and invariant checks within libtorrent. Developer studio does
-this by default.
To build libtorrent you need boost and bjam installed.
Then you can use bjam to build libtorrent.
To make bjam work, you need to set the environment variable BOOST_ROOT to the
@@ -168,6 +170,46 @@ of the file:
In developer studio, you may have to set the compiler options "force conformance in for
loop scope" and "treat wchar_t as built-in type" to Yes.
TODO: more detailed build instructions.
+
+
+
If you just invoke the makefile you'll get a debug build. In debug the libtorrent vill
+have pretty expensive invariant checks and asserts built into it. If you want to disable
+such checks (you want to do that in a release build) you can see the table below for which
+defines you can use to control the build.
+
+
+
+
+
+
+macro |
+description |
+
+
+
+NDEBUG |
+If you define this macro, all asserts,
+invariant checks and general debug code will be
+removed. This option takes precedence over
+other debug settings. |
+
+TORRENT_VERBOSE_LOGGING |
+If you define this macro, every peer connection
+will log its traffic to a log file.
+This setting requires a debug build, i.e.
+if you set NDEBUG aswell, no logs will be
+written. |
+
+TORRENT_STORAGE_DEBUG |
+This will enable extra expensive invariant
+checks in the storage, including logging of
+piece sorting. |
+
+
+
+
If you experience that libtorrent uses unreasonable amounts of cpu, it will definately help to
+define NDEBUG.
+
diff --git a/docs/manual.rst b/docs/manual.rst
index b32ec2d71..40f4378c4 100755
--- a/docs/manual.rst
+++ b/docs/manual.rst
@@ -42,6 +42,8 @@ The current state includes the following features:
* supports the ``no_peer_id=1`` extension that will ease the load off trackers.
* supports the `udp-tracker protocol`__.
* possibility to limit the number of connections.
+ * delays have messages if there's no other outgoing traffic to the peer, and doesn't
+ send have messages to peers that already has the piece. This saves bandwidth.
__ http://home.elp.rr.com/tur/multitracker-spec.txt
.. _Azureus: http://azureus.sourceforge.net
@@ -77,11 +79,6 @@ libtorrent is released under the BSD-license_.
building
========
-Whwn building in release mode you need to define NDEBUG in order to avoid
-all asserts and invariant checks within libtorrent. Developer studio does
-this by default.
-
-
To build libtorrent you need boost_ and bjam installed.
Then you can use ``bjam`` to build libtorrent.
@@ -122,6 +119,37 @@ loop scope" and "treat wchar_t as built-in type" to Yes.
TODO: more detailed build instructions.
+release and debug builds
+------------------------
+
+If you just invoke the makefile you'll get a debug build. In debug the libtorrent vill
+have pretty expensive invariant checks and asserts built into it. If you want to disable
+such checks (you want to do that in a release build) you can see the table below for which
+defines you can use to control the build.
+
++-------------------------------+-------------------------------------------------+
+| macro | description |
++===============================+=================================================+
+| ``NDEBUG`` | If you define this macro, all asserts, |
+| | invariant checks and general debug code will be |
+| | removed. This option takes precedence over |
+| | other debug settings. |
++-------------------------------+-------------------------------------------------+
+| ``TORRENT_VERBOSE_LOGGING`` | If you define this macro, every peer connection |
+| | will log its traffic to a log file. |
+| | This setting requires a debug build, i.e. |
+| | if you set ``NDEBUG`` aswell, no logs will be |
+| | written. |
++-------------------------------+-------------------------------------------------+
+| ``TORRENT_STORAGE_DEBUG`` | This will enable extra expensive invariant |
+| | checks in the storage, including logging of |
+| | piece sorting. |
++-------------------------------+-------------------------------------------------+
+
+
+If you experience that libtorrent uses unreasonable amounts of cpu, it will definately help to
+define ``NDEBUG``.
+
diff --git a/examples/client_test.cpp b/examples/client_test.cpp
index 7851418e4..6da01d645 100755
--- a/examples/client_test.cpp
+++ b/examples/client_test.cpp
@@ -400,7 +400,7 @@ int main(int argc, char* argv[])
}
*/
}
-/*
+
out << "___________________________________\n";
i->get_download_queue(queue);
@@ -421,7 +421,7 @@ int main(int argc, char* argv[])
}
out << "___________________________________\n";
-*/
+
}
for (std::deque
::iterator i = events.begin();
diff --git a/include/libtorrent/alert_types.hpp b/include/libtorrent/alert_types.hpp
index bafa63820..1dd2d8d3a 100755
--- a/include/libtorrent/alert_types.hpp
+++ b/include/libtorrent/alert_types.hpp
@@ -80,6 +80,7 @@ namespace libtorrent
virtual std::auto_ptr clone() const
{ return std::auto_ptr(new peer_error_alert(*this)); }
+ // TODO: use address instead of peer_id
peer_id id;
};
@@ -98,6 +99,7 @@ namespace libtorrent
{ return std::auto_ptr(new chat_message_alert(*this)); }
torrent_handle handle;
+ // TODO: use address instead of peer_id
peer_id sender;
};
@@ -118,6 +120,7 @@ namespace libtorrent
{ return std::auto_ptr(new invalid_request_alert(*this)); }
torrent_handle handle;
+ // TODO: use address instead of peer_id
peer_id sender;
peer_request request;
};
diff --git a/include/libtorrent/hasher.hpp b/include/libtorrent/hasher.hpp
index 3b10c7370..a74f56e73 100755
--- a/include/libtorrent/hasher.hpp
+++ b/include/libtorrent/hasher.hpp
@@ -41,14 +41,34 @@ extern "C"
unsigned char buffer[64];
} SHA1_CTX;
+ /* from sha1.c */
void SHA1Init(SHA1_CTX* context);
void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int len);
void SHA1Final(unsigned char* digest, SHA1_CTX* context);
+
+ /* from zlib/adler32.c */
+ unsigned long adler32(unsigned long adler, const char* data, unsigned int len);
}
namespace libtorrent
{
+ class adler32_crc
+ {
+ public:
+ adler32_crc(): m_adler(adler32(0, 0, 0)) {}
+
+ void update(const char* data, int len)
+ { m_adler = adler32(m_adler, data, len); }
+ unsigned long final() const { return m_adler; }
+ void reset() { m_adler = adler32(0, 0, 0); }
+
+ private:
+
+ unsigned long m_adler;
+
+ };
+
class hasher
{
public:
diff --git a/include/libtorrent/peer_connection.hpp b/include/libtorrent/peer_connection.hpp
index b82e6c73c..0283e43a5 100755
--- a/include/libtorrent/peer_connection.hpp
+++ b/include/libtorrent/peer_connection.hpp
@@ -220,7 +220,6 @@ namespace libtorrent
void announce_piece(int index)
{
m_announce_queue.push_back(index);
- send_buffer_updated();
}
// called from the main loop when this connection has any
diff --git a/include/libtorrent/piece_picker.hpp b/include/libtorrent/piece_picker.hpp
index fba4e7f14..e9a079208 100755
--- a/include/libtorrent/piece_picker.hpp
+++ b/include/libtorrent/piece_picker.hpp
@@ -92,7 +92,7 @@ namespace libtorrent
// info about each block
block_info info[max_blocks_per_piece];
- // TODO: store a hash and a peer_connection reference
+ // TODO: store a crc and a peer_connection reference
// for each block. Then if the hash test fails on the
// piece, redownload one block from another peer
// then the first time, and check the hash again.
diff --git a/include/libtorrent/session.hpp b/include/libtorrent/session.hpp
index a4128758a..5658012cc 100755
--- a/include/libtorrent/session.hpp
+++ b/include/libtorrent/session.hpp
@@ -72,13 +72,15 @@ namespace libtorrent
// workaround for microsofts
// hardware exceptions that makes
// it hard to debug stuff
-#if !defined(NDEBUG) && defined(_MSC_VER)
+#if defined(_MSC_VER)
struct eh_initializer
{
eh_initializer()
{
+#ifndef NDEBUG
_clearfp();
_controlfp(_EM_INEXACT | _EM_UNDERFLOW, _MCW_EM );
+#endif
::_set_se_translator(straight_to_debugger);
}
diff --git a/include/libtorrent/storage.hpp b/include/libtorrent/storage.hpp
index 90f615ee3..2e8758159 100755
--- a/include/libtorrent/storage.hpp
+++ b/include/libtorrent/storage.hpp
@@ -34,6 +34,7 @@ POSSIBILITY OF SUCH DAMAGE.
#define TORRENT_STORAGE_HPP_INCLUDE
#include
+#include
#include
#include
@@ -103,6 +104,12 @@ namespace libtorrent
void allocate_slots(int num_slots);
void mark_failed(int index);
+ unsigned long piece_crc(
+ int slot_index
+ , int block_size
+ , const std::bitset<256>& bitmask);
+ int slot_for_piece(int piece_index) const;
+
size_type read(char* buf, int piece_index, size_type offset, size_type size);
void write(const char* buf, int piece_index, size_type offset, size_type size);
diff --git a/src/identify_client.cpp b/src/identify_client.cpp
index 773c6d749..113979c4e 100755
--- a/src/identify_client.cpp
+++ b/src/identify_client.cpp
@@ -201,6 +201,12 @@ namespace libtorrent
// non standard encodings
// ----------------------
+ if (std::equal(PID, PID + 4, "exbc"))
+ {
+ std::stringstream s;
+ s << "BitComet " << (int)PID[4] << "." << (int)PID[5]/10 << (int)PID[5]%10;
+ return s.str();
+ }
if (std::equal(PID + 5, PID + 5 + 8, "Azureus"))
{
@@ -214,7 +220,7 @@ namespace libtorrent
if (std::equal(PID, PID + 7, "btfans"))
{
- return "BitComet";
+ return "SimpleBT";
}
if (std::equal(PID, PID + 8, "turbobt"))
diff --git a/src/peer_connection.cpp b/src/peer_connection.cpp
index 0bdbfbfdb..9bd7cf7e8 100755
--- a/src/peer_connection.cpp
+++ b/src/peer_connection.cpp
@@ -1455,8 +1455,7 @@ namespace libtorrent
// if we have requests or pending data to be sent or announcements to be made
// we want to send data
return ((!m_requests.empty() && !m_choked)
- || !m_send_buffer.empty()
- || !m_announce_queue.empty())
+ || !m_send_buffer.empty())
&& m_send_quota_left != 0;
}
@@ -1634,7 +1633,11 @@ namespace libtorrent
{
boost::posix_time::time_duration d;
d = boost::posix_time::second_clock::local_time() - m_last_sent;
- if (d.seconds() > m_timeout / 2)
+ if (d.seconds() < m_timeout / 2) return;
+
+ // we must either send a keep-alive
+ // message or something else.
+ if (m_announce_queue.empty())
{
char noop[] = {0,0,0,0};
m_send_buffer.insert(m_send_buffer.end(), noop, noop+4);
@@ -1642,11 +1645,23 @@ namespace libtorrent
#ifndef NDEBUG
(*m_logger) << " ==> NOP\n";
#endif
- send_buffer_updated();
}
+ else
+ {
+ for (std::vector::iterator i = m_announce_queue.begin();
+ i != m_announce_queue.end();
+ ++i)
+ {
+ send_have(*i);
+ }
+ m_announce_queue.clear();
+ }
+ send_buffer_updated();
}
// TODO: this could be implemented more efficient
+ // by maintaining a counter of the number of pieces
+ // the peer has
bool peer_connection::is_seed() const
{
return std::count(m_have_piece.begin(), m_have_piece.end(), true)
diff --git a/src/policy.cpp b/src/policy.cpp
index 2451da781..d42f1bae7 100755
--- a/src/policy.cpp
+++ b/src/policy.cpp
@@ -280,13 +280,6 @@ namespace
namespace libtorrent
{
-/*
- TODO: implement some kind of limit of the number of sockets
- opened, to use for systems where a user has a limited number
- of open file descriptors. and for windows which has a buggy tcp-stack.
- This means also to implement a 'connecion purger', that identifies
- more or less useless connections and closes them.
-*/
policy::policy(torrent* t)
: m_num_peers(0)
@@ -624,6 +617,8 @@ namespace libtorrent
{
assert(!c.is_local());
+ // TODO: make an exception if the incoming connection
+ // is from the tracker
if(m_torrent->num_peers() >= m_max_connections)
throw protocol_error("too many connections, refusing incoming connection"); // cause a disconnect
@@ -893,6 +888,7 @@ namespace libtorrent
{
assert(max_connections > 1 || max_connections == -1);
if (max_connections == -1) max_connections = std::numeric_limits::max();
+ assert(max_connections >= 2);
m_max_connections = max_connections;
}
diff --git a/src/session.cpp b/src/session.cpp
index 934adb6c1..b1ae5b952 100755
--- a/src/session.cpp
+++ b/src/session.cpp
@@ -378,6 +378,7 @@ namespace libtorrent
{
m_connections.erase(m_disconnect_peer.back());
m_disconnect_peer.pop_back();
+ assert(m_selector.count_read_monitors() == m_connections.size() + 1);
}
}
@@ -500,12 +501,19 @@ namespace libtorrent
assert(p->second->get_socket()->is_writable());
p->second->send_data();
}
- catch(std::exception&)
+ catch(std::exception& e)
{
- // the connection wants to disconnect for some reason, remove it
- // from the connection-list
+ // the connection wants to disconnect for some reason,
+ // remove it from the connection-list
+ if (m_alerts.should_post(alert::debug))
+ {
+ m_alerts.post_alert(
+ peer_error_alert(p->second->get_peer_id(), e.what()));
+ }
+
m_selector.remove(*i);
m_connections.erase(p);
+ assert(m_selector.count_read_monitors() == m_connections.size() + 1);
}
}
}
@@ -558,10 +566,6 @@ namespace libtorrent
{
// (*m_logger) << "readable: " << p->first->sender().as_string() << "\n";
p->second->receive_data();
-
-#ifndef NDEBUG
- assert_invariant();
-#endif
}
catch(std::exception& e)
{
@@ -574,6 +578,7 @@ namespace libtorrent
// from the connection-list
m_selector.remove(*i);
m_connections.erase(p);
+ assert(m_selector.count_read_monitors() == m_connections.size() + 1);
}
}
}
@@ -593,10 +598,21 @@ namespace libtorrent
++i)
{
connection_map::iterator p = m_connections.find(*i);
+ if (m_alerts.should_post(alert::debug))
+ {
+ m_alerts.post_alert(
+ peer_error_alert(
+ p->second->get_peer_id()
+ , "socket received an exception"));
+ }
m_selector.remove(*i);
// the connection may have been disconnected in the receive or send phase
- if (p != m_connections.end()) m_connections.erase(p);
+ if (p != m_connections.end())
+ {
+ m_connections.erase(p);
+ assert(m_selector.count_read_monitors() == m_connections.size() + 1);
+ }
}
#ifndef NDEBUG
@@ -631,6 +647,7 @@ namespace libtorrent
{
m_selector.remove(j->first);
m_connections.erase(j);
+ assert(m_selector.count_read_monitors() == m_connections.size() + 1);
continue;
}
@@ -1018,6 +1035,34 @@ namespace libtorrent
p.finished_blocks[bit] = true;
}
}
+
+ if (p.finished_blocks.count() == 0) continue;
+
+ std::vector::iterator slot_iter
+ = std::find(tmp_pieces.begin(), tmp_pieces.end(), p.index);
+ if (slot_iter == tmp_pieces.end())
+ {
+ // this piece is marked as unfinished
+ // but doesn't have any storage
+ return;
+ }
+
+ assert(*slot_iter == p.index);
+ int slot_index = slot_iter - tmp_pieces.begin();
+ unsigned long adler
+ = torrent_ptr->filesystem().piece_crc(
+ slot_index
+ , torrent_ptr->block_size()
+ , p.finished_blocks);
+
+ entry::dictionary_type::iterator ad = i->dict().find("adler32");
+ if (ad != i->dict().end())
+ {
+ // crc's didn't match, don't use the resume data
+ if (ad->second.integer() != adler)
+ return;
+ }
+
tmp_unfinished.push_back(p);
}
diff --git a/src/storage.cpp b/src/storage.cpp
index 790a5757f..006ae7477 100755
--- a/src/storage.cpp
+++ b/src/storage.cpp
@@ -427,6 +427,13 @@ namespace libtorrent
void allocate_slots(int num_slots);
void mark_failed(int index);
+ unsigned long piece_crc(
+ int slot_index
+ , int block_size
+ , const std::bitset<256>& bitmask);
+
+ int slot_for_piece(int piece_index) const;
+
size_type read(char* buf, int piece_index, size_type offset, size_type size);
void write(const char* buf, int piece_index, size_type offset, size_type size);
@@ -438,17 +445,15 @@ namespace libtorrent
private:
// returns the slot currently associated with the given
// piece or assigns the given piece_index to a free slot
- int slot_for_piece(int piece_index);
+ int allocate_slot_for_piece(int piece_index);
#ifndef NDEBUG
void check_invariant() const;
+#ifdef TORRENT_STORAGE_DEBUG
void debug_log() const;
+#endif
#endif
storage m_storage;
- // total number of bytes left to be downloaded
- // TODO: this member shouldn't be necessaty any more
- size_type m_bytes_left;
-
// a bitmask representing the pieces we have
std::vector m_have_piece;
@@ -571,6 +576,57 @@ namespace libtorrent
m_pimpl->mark_failed(index);
}
+ int piece_manager::slot_for_piece(int piece_index) const
+ {
+ return m_pimpl->slot_for_piece(piece_index);
+ }
+
+ int piece_manager::impl::slot_for_piece(int piece_index) const
+ {
+ assert(piece_index >= 0 && piece_index < m_info.num_pieces());
+ return m_piece_to_slot[piece_index];
+ }
+
+ unsigned long piece_manager::piece_crc(
+ int index
+ , int block_size
+ , const std::bitset<256>& bitmask)
+ {
+ return m_pimpl->piece_crc(index, block_size, bitmask);
+ }
+
+ unsigned long piece_manager::impl::piece_crc(
+ int slot_index
+ , int block_size
+ , const std::bitset<256>& bitmask)
+ {
+ adler32_crc crc;
+ std::vector buf(block_size);
+ int num_blocks = m_info.piece_size(slot_index) / block_size;
+ int last_block_size = m_info.piece_size(slot_index) % block_size;
+ if (last_block_size == 0) last_block_size = block_size;
+
+ for (int i = 0; i < num_blocks-1; ++i)
+ {
+ if (!bitmask[i]) continue;
+ m_storage.read(
+ &buf[0]
+ , slot_index
+ , i * block_size
+ , block_size);
+ crc.update(&buf[0], block_size);
+ }
+ if (bitmask[num_blocks - 1])
+ {
+ m_storage.read(
+ &buf[0]
+ , slot_index
+ , block_size * (num_blocks - 1)
+ , last_block_size);
+ crc.update(&buf[0], last_block_size);
+ }
+ return crc.final();
+ }
size_type piece_manager::impl::read(
char* buf
@@ -598,7 +654,7 @@ namespace libtorrent
, size_type offset
, size_type size)
{
- int slot = slot_for_piece(piece_index);
+ int slot = allocate_slot_for_piece(piece_index);
m_storage.write(buf, slot, offset, size);
}
@@ -626,8 +682,6 @@ namespace libtorrent
m_piece_to_slot.resize(m_info.num_pieces(), has_no_slot);
m_slot_to_piece.resize(m_info.num_pieces(), unallocated);
- m_bytes_left = m_info.total_size();
-
const std::size_t piece_size = m_info.piece_length();
const std::size_t last_piece_size = m_info.piece_size(
m_info.num_pieces() - 1);
@@ -655,7 +709,6 @@ namespace libtorrent
, piece_picker::has_index(found_piece))
== data.unfinished_pieces.end())
{
- m_bytes_left -= m_info.piece_size(found_piece);
pieces[found_piece] = true;
}
}
@@ -832,10 +885,6 @@ namespace libtorrent
m_slot_to_piece[m_piece_to_slot[found_piece]] = unassigned;
m_free_slots.push_back(m_piece_to_slot[found_piece]);
}
- else
- {
- m_bytes_left -= m_info.piece_size(found_piece);
- }
m_piece_to_slot[found_piece] = current_slot;
m_slot_to_piece[current_slot] = found_piece;
@@ -892,7 +941,7 @@ namespace libtorrent
m_pimpl->check_pieces(mutex, data, pieces);
}
- int piece_manager::impl::slot_for_piece(int piece_index)
+ int piece_manager::impl::allocate_slot_for_piece(int piece_index)
{
// synchronization ------------------------------------------------------
boost::recursive_mutex::scoped_lock lock(m_mutex);
@@ -979,7 +1028,9 @@ namespace libtorrent
assert(m_piece_to_slot[piece_at_our_slot] == piece_index);
#ifndef NDEBUG
print_to_log(s.str());
+#ifdef TORRENT_STORAGE_DEBUG
debug_log();
+#endif
#endif
std::swap(
m_slot_to_piece[piece_index]
@@ -997,7 +1048,7 @@ namespace libtorrent
assert(m_piece_to_slot[piece_index] == piece_index);
slot_index = piece_index;
-#ifndef NDEBUG
+#if !defined(NDEBUG) && defined(TORRENT_STORAGE_DEBUG)
debug_log();
#endif
}
@@ -1098,7 +1149,8 @@ namespace libtorrent
{
assert(m_slot_to_piece[i]= 2);
if (m_ses == 0) throw invalid_handle();
{
@@ -240,13 +239,17 @@ namespace libtorrent
i != q.end();
++i)
{
+ if (i->finished_blocks.count() == 0) continue;
+
entry::dictionary_type piece_struct;
// the unfinished piece's index
piece_struct["piece"] = i->index;
std::string bitmask;
- const int num_bitmask_bytes = std::max(num_blocks_per_piece / 8, 1);
+ const int num_bitmask_bytes
+ = std::max(num_blocks_per_piece / 8, 1);
+
for (int j = 0; j < num_bitmask_bytes; ++j)
{
unsigned char v = 0;
@@ -256,7 +259,14 @@ namespace libtorrent
}
piece_struct["bitmask"] = bitmask;
- // TODO: add a hash to piece_struct
+ assert(t->filesystem().slot_for_piece(i->index) >= 0);
+ unsigned long adler
+ = t->filesystem().piece_crc(
+ t->filesystem().slot_for_piece(i->index)
+ , t->block_size()
+ , i->finished_blocks);
+
+ piece_struct["adler32"] = adler;
// push the struct onto the unfinished-piece list
up.push_back(piece_struct);
diff --git a/src/torrent_info.cpp b/src/torrent_info.cpp
index 3a9b711cf..147b06728 100755
--- a/src/torrent_info.cpp
+++ b/src/torrent_info.cpp
@@ -131,6 +131,20 @@ namespace libtorrent
m_urls.push_back(e);
}
+ // extract creation date
+ i = dict.find("creation date");
+ if (i != dict.end() && i->second.type() == entry::int_t)
+ {
+ m_creation_date = m_creation_date + boost::posix_time::seconds(i->second.integer());
+ }
+
+ // extract comment
+ i = dict.find("comment");
+ if (i != dict.end() && i->second.type() == entry::string_t)
+ {
+ m_comment = i->second.string();
+ }
+
i = dict.find("info");
if (i == dict.end()) throw invalid_torrent_file();
entry info = i->second;
@@ -170,20 +184,6 @@ namespace libtorrent
extract_files(i->second.list(), m_files, m_name);
}
- // extract creation date
- i = info.dict().find("creation date");
- if (i != info.dict().end() && i->second.type() == entry::int_t)
- {
- m_creation_date = m_creation_date + boost::posix_time::seconds(i->second.integer());
- }
-
- // extract comment
- i = info.dict().find("comment");
- if (i != info.dict().end() && i->second.type() == entry::string_t)
- {
- m_comment = i->second.string();
- }
-
// calculate total size of all pieces
m_total_size = 0;
for (std::vector::iterator i = m_files.begin(); i != m_files.end(); ++i)