diff --git a/docs/manual.rst b/docs/manual.rst
index 49e109d1c..e2a104283 100755
--- a/docs/manual.rst
+++ b/docs/manual.rst
@@ -33,10 +33,12 @@ The current state includes the following features:
thread-safe library interface. (i.e. There's no way for the user to cause a deadlock).
* can limit the upload bandwidth usage and the maximum number of unchoked peers
* piece-wise file allocation
- * tries to maintain a 1:1 share ratio between all peers but also shifts free
- download to peers as free upload. To maintain a global 1:1 ratio.
+ * Implements fair trade. User settable trade-ratio, must at least be 1:1,
+ but one can choose to trade 1 for 2 or any other ratio that isn't unfair to the other
+ party.
* fast resume support, a way to get rid of the costly piece check at the start
- of a resumed torrent. Saves the storage state in a separate fast-resume file.
+ of a resumed torrent. Saves the storage state, piece_picker state as well as all local
+ peers in a separate fast-resume file.
* The extension protocol `described by Nolar`__. See extensions_.
__ http://home.elp.rr.com/tur/multitracker-spec.txt
@@ -670,6 +672,7 @@ fields::
int upload_ceiling;
int load_balancing;
+ int download_queue_length;
int downloading_piece_index;
int downloading_block_index;
@@ -732,6 +735,9 @@ this member says how much *extra* free upload this peer has got. If it is a nega
number it means that this was a peer from which we have got this amount of free
download.
+``download_queue_length`` is the number of block-requests we have sent to this peer
+that hasn't been answered with a piece yet.
+
You can know which piece, and which part of that piece, that is currently being
downloaded from a specific peer by looking at the next four members.
``downloading_piece_index`` is the index of the piece that is currently being downloaded.
@@ -1364,21 +1370,37 @@ The file format is a bencoded dictionary containing the following fields:
| | tells which piece is on which slot. If piece index is -2 it |
| | means it is free, that there's no piece there. If it is -1, |
| | means the slot isn't allocated on disk yet. The pieces have |
-| | to meet the following requirements: |
+| | to meet the following requirement: |
| | |
| | * if there's a slot at the position of the piece index, |
| | the piece must be located in that slot. |
-| | |
-| | TODO: finish |
+----------------------+--------------------------------------------------------------+
-| ``peers`` | |
+| ``peers`` | list of dictionaries. Each dictionary has the following |
+| | layout: |
+| | |
+| | +----------+-----------------------------------------------+ |
+| | | ``ip`` | string, the ip address of the peer. | |
+| | +----------+-----------------------------------------------+ |
+| | | ``port`` | integer, the listen port of the peer | |
+| | +----------+-----------------------------------------------+ |
+| | |
+| | These are the local peers we were connected to when this |
+| | fast-resume data was saved. |
+----------------------+--------------------------------------------------------------+
-| ``unfinished`` | |
+| ``unfinished`` | list of dictionaries. Each dictionary represents an |
+| | piece, and has the following layout: |
+| | |
+| | +-------------+--------------------------------------------+ |
+| | | ``piece`` | integer, the index of the piece this entry | |
+| | | | refers to. | |
+| | +-------------+--------------------------------------------+ |
+| | | ``bitmask`` | string, a binary bitmask representing the | |
+| | | | blocks that have been downloaded in this | |
+| | | | piece. | |
+| | +-------------+--------------------------------------------+ |
+----------------------+--------------------------------------------------------------+
-TODO: describe the file format
-
extensions
==========
diff --git a/examples/client_test.cpp b/examples/client_test.cpp
index 7139a337b..c84ab5443 100755
--- a/examples/client_test.cpp
+++ b/examples/client_test.cpp
@@ -197,9 +197,6 @@ int main(int argc, char* argv[])
{
using namespace libtorrent;
- // TEMPORARY
-// boost::filesystem::path::default_name_check(boost::filesystem::no_check);
-
if (argc < 2)
{
std::cerr << "usage: ./client_test torrent-files ...\n"
@@ -350,13 +347,15 @@ int main(int argc, char* argv[])
<< "(" << add_suffix(i->total_download) << ") "
<< "u: " << add_suffix(i->up_speed) << "/s "
<< "(" << add_suffix(i->total_upload) << ") "
- << "df: " << ratio(i->total_download, i->total_upload) << " "
+// << "df: " << ratio(i->total_download, i->total_upload) << " "
+ << "q: " << i->download_queue_length << " "
<< "f: "
<< static_cast
((i->flags & peer_info::interesting)?"I":"_")
<< static_cast((i->flags & peer_info::choked)?"C":"_")
<< static_cast((i->flags & peer_info::remote_interested)?"i":"_")
<< static_cast((i->flags & peer_info::remote_choked)?"c":"_")
<< static_cast((i->flags & peer_info::supports_extensions)?"e":"_")
+ << static_cast((i->flags & peer_info::local_connection)?"l":"r")
<< "\n";
if (i->downloading_piece_index >= 0)
diff --git a/include/libtorrent/peer_connection.hpp b/include/libtorrent/peer_connection.hpp
index 59f8fdc50..8912ed608 100755
--- a/include/libtorrent/peer_connection.hpp
+++ b/include/libtorrent/peer_connection.hpp
@@ -226,7 +226,9 @@ namespace libtorrent
void receive_data();
// tells if this connection has data it want to send
- bool has_data() const throw();
+ bool has_data() const;
+
+ bool is_seed() const;
bool has_timed_out()
{
@@ -238,19 +240,12 @@ namespace libtorrent
// will send a keep-alive message to the peer
void keep_alive();
- const peer_id& id() const throw() { return m_peer_id; }
- bool has_piece(int i) const throw() { return m_have_piece[i]; }
+ const peer_id& id() const { return m_peer_id; }
+ bool has_piece(int i) const { return m_have_piece[i]; }
- const std::deque& download_queue() const throw()
+ const std::deque& download_queue() const
{ return m_download_queue; }
- void choke();
- void unchoke();
- void interested();
- void not_interested();
- void request_block(piece_block block);
- void cancel_block(piece_block block);
-
// returns the block currently being
// downloaded. And the progress of that
// block. If the peer isn't downloading
@@ -258,17 +253,17 @@ namespace libtorrent
// will be invalid.
boost::optional downloading_piece() const;
- bool is_interesting() const throw() { return m_interesting; }
- bool is_choked() const throw() { return m_choked; }
+ bool is_interesting() const { return m_interesting; }
+ bool is_choked() const { return m_choked; }
- bool is_peer_interested() const throw() { return m_peer_interested; }
- bool has_peer_choked() const throw() { return m_peer_choked; }
+ bool is_peer_interested() const { return m_peer_interested; }
+ bool has_peer_choked() const { return m_peer_choked; }
// returns the torrent this connection is a part of
// may be zero if the connection is an incoming connection
// and it hasn't received enough information to determine
// which torrent it should be associated with
- torrent* associated_torrent() const throw() { return m_attached_to_torrent?m_torrent:0; }
+ torrent* associated_torrent() const { return m_attached_to_torrent?m_torrent:0; }
bool verify_piece(const peer_request& p) const;
@@ -329,6 +324,11 @@ namespace libtorrent
bool support_extensions() const
{ return m_supports_extensions; }
+ const boost::posix_time::time_duration& last_piece_time() const
+ { return m_last_piece_time; }
+
+ // a connection is local if it was initiated by us.
+ // if it was an incoming connection, it is remote
bool is_local() const
{ return m_active; }
@@ -358,17 +358,26 @@ namespace libtorrent
typedef void (peer_connection::*message_handler)(int received);
- private:
-
- bool dispatch_message(int received);
- void send_buffer_updated();
-
+ // the following functions appends messages
+ // to the send buffer
+ void send_choke();
+ void send_unchoke();
+ void send_interested();
+ void send_not_interested();
+ void send_request(piece_block block);
+ void send_cancel(piece_block block);
void send_bitfield();
void send_have(int index);
void send_handshake();
void send_extensions();
void send_chat_message(const std::string& msg);
+
+ private:
+
+ bool dispatch_message(int received);
+ void send_buffer_updated();
+
// is used during handshake
enum state
{
@@ -557,7 +566,24 @@ namespace libtorrent
num_supported_extensions
};
static const char* extension_names[num_supported_extensions];
- unsigned char m_extension_messages[num_supported_extensions];
+ int m_extension_messages[num_supported_extensions];
+
+ // the number of invalid piece-requests
+ // we have got from this peer. If the request
+ // queue gets empty, and there have been
+ // invalid requests, we can assume the
+ // peer is waiting for those pieces.
+ // we can then clear its download queue
+ // by sending choke, unchoke.
+ int m_num_invalid_requests;
+
+ // the time at which we started to get the last piece
+ // message from this peer
+ boost::posix_time::ptime m_last_piece;
+
+ // the time it took for the peer to send the piece
+ // message
+ boost::posix_time::time_duration m_last_piece_time;
};
// this is called each time this peer generates some
diff --git a/include/libtorrent/piece_picker.hpp b/include/libtorrent/piece_picker.hpp
index 583721763..fef150c2a 100755
--- a/include/libtorrent/piece_picker.hpp
+++ b/include/libtorrent/piece_picker.hpp
@@ -38,6 +38,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include
#include "libtorrent/peer_id.hpp"
+#include "libtorrent/socket.hpp"
namespace libtorrent
{
@@ -73,7 +74,7 @@ namespace libtorrent
block_info(): num_downloads(0) {}
// the peer this block was requested or
// downloaded from
- peer_id peer;
+ address peer;
// the number of times this block has been downloaded
int num_downloads;
};
@@ -145,8 +146,8 @@ namespace libtorrent
bool is_finished(piece_block block) const;
// marks this piece-block as queued for downloading
- void mark_as_downloading(piece_block block, const peer_id& peer);
- void mark_as_finished(piece_block block, const peer_id& peer);
+ void mark_as_downloading(piece_block block, const address& peer);
+ void mark_as_finished(piece_block block, const address& peer);
// if a piece had a hash-failure, it must be restured and
// made available for redownloading
@@ -165,7 +166,7 @@ namespace libtorrent
// the hash-check yet
int unverified_blocks() const;
- void get_downloaders(std::vector& d, int index);
+ void get_downloaders(std::vector& d, int index);
const std::vector& get_download_queue() const
{ return m_downloads; }
diff --git a/include/libtorrent/policy.hpp b/include/libtorrent/policy.hpp
index f212f8ea6..8093837b2 100755
--- a/include/libtorrent/policy.hpp
+++ b/include/libtorrent/policy.hpp
@@ -61,7 +61,7 @@ namespace libtorrent
// called when an incoming connection is accepted
// return false if the connection closed
- bool new_connection(peer_connection& c);
+ void new_connection(peer_connection& c);
// this is called once for every peer we get from
// the tracker
@@ -103,6 +103,9 @@ namespace libtorrent
private:
+ // TODO: for the moment the peer_id is never updated
+ // when we get it from the peer. It's kindof useless
+ // in here right now.
struct peer
{
peer(const peer_id& pid, const address& a);
diff --git a/include/libtorrent/stat.hpp b/include/libtorrent/stat.hpp
index c61f1d391..6acc4da81 100755
--- a/include/libtorrent/stat.hpp
+++ b/include/libtorrent/stat.hpp
@@ -41,7 +41,7 @@ namespace libtorrent
class stat
{
- enum { history = 5 };
+ enum { history = 10 };
public:
stat()
diff --git a/include/libtorrent/storage.hpp b/include/libtorrent/storage.hpp
index 9680c3423..035fe2013 100755
--- a/include/libtorrent/storage.hpp
+++ b/include/libtorrent/storage.hpp
@@ -55,7 +55,7 @@ namespace libtorrent
{
file_allocation_failed(const char* error_msg): m_msg(error_msg) {}
virtual const char* what() const throw() { return m_msg.c_str(); }
- virtual ~file_allocation_failed() throw() {}
+ virtual ~file_allocation_failed() {}
std::string m_msg;
};
diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp
index 267e1aa35..3a589ac3d 100755
--- a/include/libtorrent/torrent.hpp
+++ b/include/libtorrent/torrent.hpp
@@ -120,7 +120,7 @@ namespace libtorrent
// returns true if it time for this torrent to make another
// tracker request
- bool should_request() const throw()
+ bool should_request() const
{
// boost::posix_time::time_duration d = m_next_request - boost::posix_time::second_clock::local_time();
// return d.is_negative();
diff --git a/include/libtorrent/torrent_handle.hpp b/include/libtorrent/torrent_handle.hpp
index a2da87c6a..90d90cdab 100755
--- a/include/libtorrent/torrent_handle.hpp
+++ b/include/libtorrent/torrent_handle.hpp
@@ -115,7 +115,7 @@ namespace libtorrent
int blocks_in_piece;
std::bitset requested_blocks;
std::bitset finished_blocks;
- peer_id peer[max_blocks_per_piece];
+ address peer[max_blocks_per_piece];
int num_downloads[max_blocks_per_piece];
};
diff --git a/src/identify_client.cpp b/src/identify_client.cpp
index 21619d9d9..987fcd56f 100755
--- a/src/identify_client.cpp
+++ b/src/identify_client.cpp
@@ -107,7 +107,7 @@ namespace libtorrent
if (!std::isdigit(*i)) return boost::optional();
ret.revision_version = *i - '0';
}
- else if (id[0] == 0)
+ else if (id[8] == 0)
{
if (*i > 127) return boost::optional();
ret.major_version = *i;
diff --git a/src/peer_connection.cpp b/src/peer_connection.cpp
index df4dd495a..7572d5690 100755
--- a/src/peer_connection.cpp
+++ b/src/peer_connection.cpp
@@ -34,6 +34,8 @@ POSSIBILITY OF SUCH DAMAGE.
#include
#include
+#include
+
#include "libtorrent/peer_connection.hpp"
#include "libtorrent/session.hpp"
#include "libtorrent/identify_client.hpp"
@@ -101,6 +103,9 @@ namespace libtorrent
, m_send_quota_left(100)
, m_send_quota_limit(100)
, m_trust_points(0)
+ , m_num_invalid_requests(0)
+ , m_last_piece(boost::gregorian::date(std::time(0)))
+ , m_last_piece_time(boost::posix_time::seconds(0))
{
assert(!m_socket->is_blocking());
assert(m_torrent != 0);
@@ -155,6 +160,9 @@ namespace libtorrent
, m_send_quota_left(100)
, m_send_quota_limit(100)
, m_trust_points(0)
+ , m_num_invalid_requests(0)
+ , m_last_piece(boost::gregorian::date(std::time(0)))
+ , m_last_piece_time(boost::posix_time::seconds(0))
{
assert(!m_socket->is_blocking());
@@ -305,7 +313,7 @@ namespace libtorrent
if (m_recv_pos < m_packet_size) return;
#ifndef NDEBUG
- (*m_logger) << m_socket->sender().as_string() << " <== CHOKE\n";
+ (*m_logger) << " <== CHOKE\n";
#endif
m_peer_choked = true;
m_torrent->get_policy().choked(*this);
@@ -336,7 +344,7 @@ namespace libtorrent
if (m_recv_pos < m_packet_size) return;
#ifndef NDEBUG
- (*m_logger) << m_socket->sender().as_string() << " <== UNCHOKE\n";
+ (*m_logger) << " <== UNCHOKE\n";
#endif
m_peer_choked = false;
m_torrent->get_policy().unchoked(*this);
@@ -375,7 +383,7 @@ namespace libtorrent
m_requests.clear();
#ifndef NDEBUG
- (*m_logger) << m_socket->sender().as_string() << " <== NOT_INTERESTED\n";
+ (*m_logger) << " <== NOT_INTERESTED\n";
#endif
m_peer_interested = false;
m_torrent->get_policy().not_interested(*this);
@@ -387,7 +395,6 @@ namespace libtorrent
void peer_connection::on_have(int received)
{
- // TODO: if we're a seed, and this peer becomes a seed, disconnect
if (m_packet_size != 5)
throw protocol_error("'have' message size != 5");
m_statistics.received_bytes(0, received);
@@ -400,7 +407,7 @@ namespace libtorrent
throw protocol_error("have message with higher index than the number of pieces");
#ifndef NDEBUG
- (*m_logger) << " <== HAVE [ piece: " << index << "]\n";
+ (*m_logger) << " <== HAVE [ piece: " << index << "]\n";
#endif
if (m_have_piece[index])
@@ -416,6 +423,11 @@ namespace libtorrent
m_torrent->peer_has(index);
if (!m_torrent->have_piece(index) && !is_interesting())
m_torrent->get_policy().peer_is_interesting(*this);
+
+ if (m_torrent->is_seed() && is_seed())
+ {
+ throw protocol_error("seed to seed connection redundant, disconnecting");
+ }
}
}
@@ -477,7 +489,7 @@ namespace libtorrent
#ifndef NDEBUG
(*m_logger) << " we're also a seed, disconnecting\n";
#endif
- throw network_error(0);
+ throw protocol_error("seed to seed connection redundant, disconnecting");
}
}
@@ -506,6 +518,7 @@ namespace libtorrent
// is not choked
if (r.piece >= 0
&& r.piece < m_torrent->torrent_file().num_pieces()
+ && m_torrent->have_piece(r.piece)
&& r.start >= 0
&& r.start < m_torrent->torrent_file().piece_size(r.piece)
&& r.length > 0
@@ -526,7 +539,7 @@ namespace libtorrent
else
{
#ifndef NDEBUG
- (*m_logger) << " <== INVALID REQUEST [ "
+ (*m_logger) << " <== INVALID_REQUEST [ "
"piece: " << r.piece << " | "
"s: " << r.start << " | "
"l: " << r.length << " | "
@@ -534,6 +547,9 @@ namespace libtorrent
"t: " << (int)m_torrent->torrent_file().piece_size(r.piece) << " | "
"n: " << m_torrent->torrent_file().num_pieces() << " ]\n";
#endif
+
+ ++m_num_invalid_requests;
+
if (m_torrent->alerts().should_post(alert::debug))
{
m_torrent->alerts().post_alert(invalid_request_alert(
@@ -551,6 +567,10 @@ namespace libtorrent
void peer_connection::on_piece(int received)
{
+ if (m_recv_pos - received <= 9)
+ {
+ m_last_piece = boost::posix_time::second_clock::local_time();
+ }
// classify the received data as protocol chatter
// or data payload for the statistics
if (m_recv_pos <= 9)
@@ -572,6 +592,9 @@ namespace libtorrent
if (m_recv_pos < m_packet_size) return;
+ m_last_piece_time = m_last_piece
+ - boost::posix_time::second_clock::local_time();
+
const char* ptr = &m_recv_buffer[1];
peer_request p;
p.piece = detail::read_int(ptr);
@@ -589,7 +612,27 @@ namespace libtorrent
}
#ifndef NDEBUG
- (*m_logger) << " <== PIECE [ piece: " << p.piece << " | "
+ for (std::deque::iterator i = m_download_queue.begin();
+ i != m_download_queue.end();
+ ++i)
+ {
+ if (i->piece_index == p.piece
+ && i->block_index == p.start / m_torrent->block_size())
+ break;
+
+ (*m_logger) << " <== SKIPPED_PIECE [ piece: " << i->piece_index << " | "
+ "b: " << i->block_index << " ]\n";
+ if (m_torrent->alerts().should_post(alert::debug))
+ {
+ std::stringstream s;
+ s << "skipped piece: " << i->piece_index << " b: " << i->block_index;
+ m_torrent->alerts().post_alert(
+ peer_error_alert(get_peer_id(), s.str()));
+
+ }
+ }
+ (*m_logger) << " <== PIECE [ piece: " << p.piece << " | "
+ "b: " << p.start / m_torrent->block_size() << " | "
"s: " << p.start << " | "
"l: " << p.length << " ]\n";
#endif
@@ -620,7 +663,7 @@ namespace libtorrent
m_torrent->filesystem().write(&m_recv_buffer[9], p.piece, p.start, p.length);
- picker.mark_as_finished(block_finished, m_peer_id);
+ picker.mark_as_finished(block_finished, m_socket->sender());
m_torrent->get_policy().block_finished(*this, block_finished);
@@ -671,7 +714,7 @@ namespace libtorrent
}
#ifndef NDEBUG
- (*m_logger) << m_socket->sender().as_string() << " <== CANCEL [ piece: " << r.piece << " | s: " << r.start << " | l: " << r.length << " ]\n";
+ (*m_logger) << " <== CANCEL [ piece: " << r.piece << " | s: " << r.start << " | l: " << r.length << " ]\n";
#endif
}
@@ -818,7 +861,7 @@ namespace libtorrent
return true;
}
- void peer_connection::cancel_block(piece_block block)
+ void peer_connection::send_cancel(piece_block block)
{
assert(block.piece_index >= 0);
assert(block.piece_index < m_torrent->torrent_file().num_pieces());
@@ -858,20 +901,19 @@ namespace libtorrent
detail::write_int(block_size, ptr);
#ifndef NDEBUG
- (*m_logger) << " ==> CANCEL [ piece: " << block.piece_index << " | s: " << block_offset << " | l: " << block_size << " | " << block.block_index << " ]\n";
+ (*m_logger) << " ==> CANCEL [ piece: " << block.piece_index << " | s: " << block_offset << " | l: " << block_size << " | " << block.block_index << " ]\n";
#endif
send_buffer_updated();
}
- // TODO: should this be renamed to 'send_request()'?
- void peer_connection::request_block(piece_block block)
+ void peer_connection::send_request(piece_block block)
{
assert(block.piece_index >= 0);
assert(block.piece_index < m_torrent->torrent_file().num_pieces());
assert(!m_torrent->picker().is_downloading(block));
- m_torrent->picker().mark_as_downloading(block, m_peer_id);
+ m_torrent->picker().mark_as_downloading(block, m_socket->sender());
m_download_queue.push_back(block);
@@ -900,7 +942,11 @@ namespace libtorrent
detail::write_int(block_size, ptr);
#ifndef NDEBUG
- (*m_logger) << " ==> REQUEST [ piece: " << block.piece_index << " | s: " << block_offset << " | l: " << block_size << " | " << block.block_index << " ]\n";
+ (*m_logger) << " ==> REQUEST [ "
+ "piece: " << block.piece_index << " | "
+ "b: " << block.block_index << " | "
+ "s: " << block_offset << " | "
+ "l: " << block_size << " ]\n";
peer_request r;
r.piece = block.piece_index;
@@ -980,7 +1026,8 @@ namespace libtorrent
send_buffer_updated();
}
- void peer_connection::choke()
+ // TODO: rename to send_choke?
+ void peer_connection::send_choke()
{
if (m_choked) return;
char msg[] = {0,0,0,1,msg_choke};
@@ -989,11 +1036,12 @@ namespace libtorrent
#ifndef NDEBUG
(*m_logger) << " ==> CHOKE\n";
#endif
+ m_num_invalid_requests = 0;
m_requests.clear();
send_buffer_updated();
}
- void peer_connection::unchoke()
+ void peer_connection::send_unchoke()
{
if (!m_choked) return;
char msg[] = {0,0,0,1,msg_unchoke};
@@ -1005,7 +1053,7 @@ namespace libtorrent
send_buffer_updated();
}
- void peer_connection::interested()
+ void peer_connection::send_interested()
{
if (m_interesting) return;
char msg[] = {0,0,0,1,msg_interested};
@@ -1017,7 +1065,7 @@ namespace libtorrent
send_buffer_updated();
}
- void peer_connection::not_interested()
+ void peer_connection::send_not_interested()
{
if (!m_interesting) return;
char msg[] = {0,0,0,1,msg_not_interested};
@@ -1041,7 +1089,7 @@ namespace libtorrent
detail::write_int(index, ptr);
m_send_buffer.insert(m_send_buffer.end(), msg, msg + packet_size);
#ifndef NDEBUG
- (*m_logger) << " ==> HAVE [ piece: " << index << " ]\n";
+ (*m_logger) << " ==> HAVE [ piece: " << index << " ]\n";
#endif
send_buffer_updated();
}
@@ -1052,10 +1100,10 @@ namespace libtorrent
// if we have an infinite ratio, just say we have downloaded
// much more than we have uploaded. And we'll keep uploading.
- if (ratio == 0.f) return 99999.f;
+ if (ratio == 0.f) return std::numeric_limits::max();
return m_free_upload
- + (m_statistics.total_payload_download() * ratio)
+ + static_cast(m_statistics.total_payload_download() * ratio)
- m_statistics.total_payload_upload();
}
@@ -1091,14 +1139,14 @@ namespace libtorrent
int bias = 0;
if (diff > -2*m_torrent->block_size())
{
- bias = (m_statistics.download_rate() * ratio) / 2;
+ bias = static_cast(m_statistics.download_rate() * ratio) / 2;
if (bias < 10*1024) bias = 10*1024;
}
else
{
- bias = -(m_statistics.download_rate() * ratio) / 2;
+ bias = -static_cast(m_statistics.download_rate() * ratio) / 2;
}
- m_send_quota_limit = m_statistics.download_rate() + bias;
+ m_send_quota_limit = static_cast(m_statistics.download_rate()) + bias;
// the maximum send_quota given our download rate from this peer
if (m_send_quota_limit < 256) m_send_quota_limit = 256;
@@ -1304,7 +1352,7 @@ namespace libtorrent
#ifndef NDEBUG
(*m_logger) << " duplicate connection, closing\n";
#endif
- throw network_error(0);
+ throw protocol_error("duplicate connection, closing");
}
m_attached_to_torrent = true;
@@ -1373,7 +1421,7 @@ namespace libtorrent
}
- bool peer_connection::has_data() const throw()
+ bool peer_connection::has_data() const
{
// if we have requests or pending data to be sent or announcements to be made
// we want to send data
@@ -1404,57 +1452,46 @@ namespace libtorrent
{
peer_request& r = m_requests.front();
- if (r.piece >= 0 && r.piece < m_have_piece.size() && m_torrent && m_torrent->have_piece(r.piece))
- {
- // make sure the request is ok
- if (r.start + r.length > m_torrent->torrent_file().piece_size(r.piece))
- {
- // NOT OK! disconnect
- throw network_error(0);
- }
+ assert(r.piece >= 0 && r.piece < m_have_piece.size() && m_torrent && m_torrent->have_piece(r.piece));
+ assert(r.start + r.length <= m_torrent->torrent_file().piece_size(r.piece));
+ assert(r.length > 0 && r.start >= 0);
- if (r.length <= 0 || r.start < 0)
- {
- // NOT OK! disconnect
- throw network_error(0);
- }
+#ifndef NDEBUG
+ assert(m_torrent->verify_piece(r.piece) && "internal error");
+#endif
+ const int send_buffer_offset = m_send_buffer.size();
+ const int packet_size = 4 + 5 + 4 + r.length;
+ m_send_buffer.resize(send_buffer_offset + packet_size);
+ char* ptr = &m_send_buffer[send_buffer_offset];
+ detail::write_int(packet_size-4, ptr);
+ *ptr = msg_piece; ++ptr;
+ detail::write_int(r.piece, ptr);
+ detail::write_int(r.start, ptr);
- #ifndef NDEBUG
- assert(m_torrent->verify_piece(r.piece) && "internal error");
- #endif
- const int send_buffer_offset = m_send_buffer.size();
- const int packet_size = 4 + 5 + 4 + r.length;
- m_send_buffer.resize(send_buffer_offset + packet_size);
- char* ptr = &m_send_buffer[send_buffer_offset];
- detail::write_int(packet_size-4, ptr);
- *ptr = msg_piece; ++ptr;
- detail::write_int(r.piece, ptr);
- detail::write_int(r.start, ptr);
+ m_torrent->filesystem().read(
+ &m_send_buffer[send_buffer_offset+13]
+ , r.piece
+ , r.start
+ , r.length);
+#ifndef NDEBUG
+ (*m_logger) << " ==> PIECE [ piece: " << r.piece << " | s: " << r.start << " | l: " << r.length << " ]\n";
+#endif
- m_torrent->filesystem().read(
- &m_send_buffer[send_buffer_offset+13]
- , r.piece
- , r.start
- , r.length);
- #ifndef NDEBUG
- (*m_logger) << " ==> PIECE [ piece: " << r.piece << " | s: " << r.start << " | l: " << r.length << " ]\n";
- #endif
- m_payloads.push_back(range(send_buffer_offset+13, r.length));
- }
- else
- {
- #ifndef NDEBUG
- (*m_logger)
- << " *** WARNING [ illegal piece request idx: " << r.piece
- << " | s: " << r.start
- << " | l: " << r.length
- << " | max_piece: " << m_have_piece.size()
- << " | torrent: " << (m_torrent != 0)
- << " | have: " << m_torrent->have_piece(r.piece)
- << " ]\n";
- #endif
- }
+ m_payloads.push_back(range(send_buffer_offset+13, r.length));
m_requests.erase(m_requests.begin());
+
+ if (m_requests.empty()
+ && m_num_invalid_requests > 0
+ && is_peer_interested()
+ && !is_seed())
+ {
+ // this will make the peer clear
+ // its download queue and re-request
+ // pieces. Hopefully it will not
+ // send invalid requests then
+ send_choke();
+ send_unchoke();
+ }
}
if (!m_announce_queue.empty())
@@ -1463,7 +1500,6 @@ namespace libtorrent
i != m_announce_queue.end();
++i)
{
- // (*m_logger) << "have piece: " << *i << " sent to: " << m_socket->sender().as_string() << "\n";
send_have(*i);
}
m_announce_queue.clear();
@@ -1484,10 +1520,6 @@ namespace libtorrent
&m_send_buffer[0]
, amount_to_send);
- #ifndef NDEBUG
- // (*m_logger) << m_socket->sender().as_string() << " ==> SENT [ length: " << sent << " ]\n";
- #endif
-
if (sent > 0)
{
if (m_send_quota_left != -1)
@@ -1580,4 +1612,11 @@ namespace libtorrent
send_buffer_updated();
}
}
+
+ // TODO: this could be implemented more efficient
+ bool peer_connection::is_seed() const
+ {
+ return std::count(m_have_piece.begin(), m_have_piece.end(), true)
+ == m_have_piece.size();
+ }
}
diff --git a/src/piece_picker.cpp b/src/piece_picker.cpp
index 9285d5931..8662a8e5f 100755
--- a/src/piece_picker.cpp
+++ b/src/piece_picker.cpp
@@ -120,8 +120,7 @@ namespace libtorrent
i != unfinished.end();
++i)
{
- peer_id peer;
- std::fill(peer.begin(), peer.end(), 0);
+ address peer;
for (int j = 0; j < m_blocks_per_piece; ++j)
{
if (i->finished_blocks[j])
@@ -538,7 +537,7 @@ namespace libtorrent
}
- void piece_picker::mark_as_downloading(piece_block block, const peer_id& peer)
+ void piece_picker::mark_as_downloading(piece_block block, const address& peer)
{
#ifndef NDEBUG
// integrity_check();
@@ -572,7 +571,7 @@ namespace libtorrent
#endif
}
- void piece_picker::mark_as_finished(piece_block block, const peer_id& peer)
+ void piece_picker::mark_as_finished(piece_block block, const address& peer)
{
#ifndef NDEBUG
// integrity_check();
@@ -633,7 +632,7 @@ namespace libtorrent
#endif
}
*/
- void piece_picker::get_downloaders(std::vector& d, int index)
+ void piece_picker::get_downloaders(std::vector& d, int index)
{
std::vector::iterator i
= std::find_if(m_downloads.begin(), m_downloads.end(), has_index(index));
diff --git a/src/policy.cpp b/src/policy.cpp
index 7921ce098..87589a959 100755
--- a/src/policy.cpp
+++ b/src/policy.cpp
@@ -51,9 +51,9 @@ namespace
{
enum
{
- // we try to maintain 4 requested blocks in the download
- // queue
- request_queue = 16,
+ // the limits of the download queue size
+ max_request_queue = 16,
+ min_request_queue = 2,
// the amount of free upload allowed before
// the peer is choked
@@ -94,9 +94,23 @@ namespace
return piece_block(-1, -1);
}
+ namespace
+ {
+ int to_seconds(const boost::posix_time::time_duration& d)
+ {
+ return d.hours() * 60 * 60 + d.minutes() * 60 + d.seconds();
+ }
+ }
+
void request_a_block(torrent& t, peer_connection& c)
{
- int num_requests = request_queue - c.download_queue().size();
+ int desired_queue_size = max_request_queue + 1 - to_seconds(c.last_piece_time()) / 4;
+ if (desired_queue_size > max_request_queue) desired_queue_size = max_request_queue;
+ if (desired_queue_size < min_request_queue) desired_queue_size = min_request_queue;
+
+ assert(desired_queue_size >= min_request_queue);
+
+ int num_requests = desired_queue_size - c.download_queue().size();
// if our request queue is already full, we
// don't have to make any new requests yet
@@ -109,11 +123,11 @@ namespace
// picks the interesting pieces from this peer
// the integer is the number of pieces that
// should be guaranteed to be available for download
- // (if this number is too big, too many pieces are
+ // (if num_requests is too big, too many pieces are
// picked and cpu-time is wasted)
p.pick_pieces(c.get_bitfield(), interesting_pieces, num_requests);
- // this vector is filled with the interestin pieces
+ // this vector is filled with the interesting pieces
// that some other peer is currently downloading
// we should then compare this peer's download speed
// with the other's, to see if we should abort another
@@ -133,7 +147,7 @@ namespace
// ok, we found a piece that's not being downloaded
// by somebody else. request it from this peer
- c.request_block(*i);
+ c.send_request(*i);
num_requests--;
if (num_requests <= 0) return;
}
@@ -174,8 +188,8 @@ namespace
// find a suitable block to take over from this peer
piece_block block = find_first_common(peer->download_queue(), busy_pieces);
- peer->cancel_block(block);
- c.request_block(block);
+ peer->send_cancel(block);
+ c.send_request(block);
// the one we interrupted may need to request a new piece
request_a_block(t, *peer);
@@ -273,6 +287,8 @@ namespace libtorrent
peer* worst_peer = 0;
int min_weight = std::numeric_limits::max();
+ // TODO: make this selection better
+
for (std::vector::iterator i = m_peers.begin();
i != m_peers.end();
++i)
@@ -288,7 +304,7 @@ namespace libtorrent
int diff = i->total_download()
- i->total_upload();
- int weight = c->statistics().download_rate() * 10
+ int weight = static_cast(c->statistics().download_rate() * 10.f)
+ diff
+ (c->has_peer_choked()?-10:10)*1024;
@@ -313,6 +329,9 @@ namespace libtorrent
peer* unchoke_peer = 0;
ptime min_time(date(9999,Jan,1));
+ float max_down_speed = 0.f;
+
+ // TODO: make this selection better
for (std::vector::iterator i = m_peers.begin();
i != m_peers.end();
@@ -324,9 +343,11 @@ namespace libtorrent
if (!c->is_peer_interested()) continue;
if (c->share_diff()
< -free_upload_amount) continue;
- if (i->last_optimistically_unchoked > min_time) continue;
+ if (c->statistics().download_rate() < max_down_speed) continue;
+// if (i->last_optimistically_unchoked > min_time) continue;
min_time = i->last_optimistically_unchoked;
+ max_down_speed = c->statistics().download_rate();
unchoke_peer = &(*i);
}
return unchoke_peer;
@@ -372,14 +393,14 @@ namespace libtorrent
if (c == 0) continue;
if (!c->is_choked()) continue;
if (!c->is_peer_interested()) continue;
-// TODO: add some more criterion here. Maybe the peers
+// TODO: add some more criterions here. Maybe the peers
// that have less should be promoted? (to allow them to trade)
p = &(*i);
}
if (p == 0) break;
- p->connection->unchoke();
+ p->connection->send_unchoke();
p->last_optimistically_unchoked = boost::posix_time::second_clock::local_time();
++m_num_unchoked;
}
@@ -392,7 +413,7 @@ namespace libtorrent
{
peer* p = find_choke_candidate();
assert(p);
- p->connection->choke();
+ p->connection->send_choke();
--m_num_unchoked;
}
@@ -402,7 +423,7 @@ namespace libtorrent
peer* p = find_choke_candidate();
if (p)
{
- p->connection->choke();
+ p->connection->send_choke();
--m_num_unchoked;
unchoke_one_peer();
}
@@ -429,14 +450,14 @@ namespace libtorrent
{
// if we have uploaded more than a piece for free, choke peer and
// wait until we catch up with our download.
- c->choke();
+ c->send_choke();
}
else if (downloaded - uploaded > -free_upload_amount
&& c->is_choked() && c->is_peer_interested())
{
// we have catched up. We have now shared the same amount
// to eachother. Unchoke this peer.
- c->unchoke();
+ c->send_unchoke();
}
}
@@ -456,7 +477,7 @@ namespace libtorrent
i->banned = true;
}
- bool policy::new_connection(peer_connection& c)
+ void policy::new_connection(peer_connection& c)
{
std::vector::iterator i
= std::find(m_peers.begin(), m_peers.end(), c.get_socket()->sender());
@@ -474,12 +495,11 @@ namespace libtorrent
else
{
assert(i->connection == 0);
- if (i->banned) return false;
+ if (i->banned) throw protocol_error("ip address banned, disconnected");
}
i->connected = boost::posix_time::second_clock::local_time();
i->connection = &c;
- return true;
}
void policy::peer_from_tracker(const address& remote, const peer_id& id)
@@ -566,7 +586,7 @@ namespace libtorrent
}
}
if (!interested)
- i->connection->not_interested();
+ i->connection->send_not_interested();
}
}
}
@@ -609,7 +629,7 @@ namespace libtorrent
peer* p = find_unchoke_candidate();
if (p == 0) return false;
- p->connection->unchoke();
+ p->connection->send_unchoke();
p->last_optimistically_unchoked = boost::posix_time::second_clock::local_time();
++m_num_unchoked;
return true;
@@ -643,7 +663,7 @@ namespace libtorrent
void policy::peer_is_interesting(peer_connection& c)
{
- c.interested();
+ c.send_interested();
if (c.has_peer_choked()) return;
request_a_block(*m_torrent, c);
}
diff --git a/src/session.cpp b/src/session.cpp
index 41f68e4cb..0cfa03217 100755
--- a/src/session.cpp
+++ b/src/session.cpp
@@ -940,22 +940,21 @@ namespace libtorrent
// the unfinished pieces
- const entry::list_type& unfinished = rd.dict()["unfinished"].list();
+ entry::list_type& unfinished = rd.dict()["unfinished"].list();
std::vector tmp_unfinished;
tmp_unfinished.reserve(unfinished.size());
- for (entry::list_type::const_iterator i = unfinished.begin();
+ for (entry::list_type::iterator i = unfinished.begin();
i != unfinished.end();
++i)
{
piece_picker::downloading_piece p;
- if (i->list().size() < 2) return;
- p.index = i->list()[0].integer();
+ p.index = i->dict()["piece"].integer();
if (p.index < 0 || p.index >= info.num_pieces())
return;
- const std::string& bitmask = i->list()[1].string();
+ const std::string& bitmask = i->dict()["bitmask"].string();
const int num_bitmask_bytes = std::max(num_blocks_per_piece / 8, 1);
if (bitmask.size() != num_bitmask_bytes) return;
diff --git a/src/torrent.cpp b/src/torrent.cpp
index 133e89946..d39647c6a 100755
--- a/src/torrent.cpp
+++ b/src/torrent.cpp
@@ -251,19 +251,20 @@ namespace libtorrent
std::random_shuffle(peer_list.begin(), peer_list.end());
- std::cout << "interval: " << m_duration << "\n";
- std::cout << "peers:\n";
+#ifndef NDEBUG
+ std::stringstream s;
+ s << "interval: " << m_duration << "\n";
+ s << "peers:\n";
for (std::vector::const_iterator i = peer_list.begin();
i != peer_list.end();
++i)
{
- std::cout << " " << std::setfill(' ') << std::setw(16) << i->ip
+ s << " " << std::setfill(' ') << std::setw(16) << i->ip
<< " " << std::setw(5) << std::dec << i->port << " "
<< i->id << " " << identify_client(i->id) << "\n";
}
- std::cout << std::setfill(' ');
-
-
+ debug_log(s.str());
+#endif
// for each of the peers we got from the tracker
for (std::vector::iterator i = peer_list.begin();
i != peer_list.end();
@@ -314,6 +315,11 @@ namespace libtorrent
, m_connections.end()
, find_peer_by_id(id, this)) <= 1);
+ // pretend that we are connected to
+ // ourself to avoid real connections
+ // to ourself
+ if (id == m_ses.m_peer_id) return true;
+
return std::find_if(
m_connections.begin()
, m_connections.end()
@@ -344,7 +350,7 @@ namespace libtorrent
s << "hash for piece " << index << " failed";
m_ses.m_alerts.post_alert(hash_failed_alert(get_handle(), index, s.str()));
}
- std::vector downloaders;
+ std::vector downloaders;
m_picker.get_downloaders(downloaders, index);
#ifndef NDEBUG
@@ -359,7 +365,7 @@ namespace libtorrent
i != m_connections.end();
++i)
{
- if (std::find(downloaders.begin(), downloaders.end(), (*i)->get_peer_id())
+ if (std::find(downloaders.begin(), downloaders.end(), (*i)->get_socket()->sender())
== downloaders.end()) continue;
(*i)->received_invalid_data();
@@ -390,7 +396,7 @@ namespace libtorrent
void torrent::announce_piece(int index)
{
- std::vector downloaders;
+ std::vector downloaders;
m_picker.get_downloaders(downloaders, index);
// increase the trust point of all peers that sent
@@ -400,7 +406,7 @@ namespace libtorrent
i != m_connections.end();
++i)
{
- if (std::find(downloaders.begin(), downloaders.end(), (*i)->get_peer_id())
+ if (std::find(downloaders.begin(), downloaders.end(), (*i)->get_socket()->sender())
!= downloaders.end())
{
(*i)->received_valid_data();
@@ -561,7 +567,7 @@ namespace libtorrent
= m_ses.m_connections.find(p->get_socket());
assert(i != m_ses.m_connections.end());
- if (!m_policy->new_connection(*i->second)) throw network_error(0);
+ m_policy->new_connection(*i->second);
}
void torrent::close_all_connections()
diff --git a/src/torrent_handle.cpp b/src/torrent_handle.cpp
index 2aad03f63..d6c79fcaa 100755
--- a/src/torrent_handle.cpp
+++ b/src/torrent_handle.cpp
@@ -216,10 +216,10 @@ namespace libtorrent
i != q.end();
++i)
{
- entry piece_struct(entry::list_t);
+ entry::dictionary_type piece_struct;
// the unfinished piece's index
- piece_struct.list().push_back(i->index);
+ piece_struct["piece"] = i->index;
std::string bitmask;
const int num_bitmask_bytes = std::max(num_blocks_per_piece / 8, 1);
@@ -230,7 +230,7 @@ namespace libtorrent
v |= i->finished_blocks[j*8+k]?(1 << k):0;
bitmask.push_back(v);
}
- piece_struct.list().push_back(bitmask);
+ piece_struct["bitmask"] = bitmask;
// TODO: add a hash to piece_struct