*** empty log message ***

This commit is contained in:
Arvid Norberg 2004-01-12 20:31:27 +00:00
parent 9e979efb8b
commit b6c826c6af
17 changed files with 337 additions and 183 deletions

View File

@ -94,10 +94,12 @@ The current state includes the following features:</p>
thread-safe library interface. (i.e. There's no way for the user to cause a deadlock).</li>
<li>can limit the upload bandwidth usage and the maximum number of unchoked peers</li>
<li>piece-wise file allocation</li>
<li>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.</li>
<li>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.</li>
<li>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.</li>
of a resumed torrent. Saves the storage state, piece_picker state as well as all local
peers in a separate fast-resume file.</li>
<li>The extension protocol <a class="reference" href="http://nolar.com/azureus/extended.htm">described by Nolar</a>. See <a class="reference" href="#extensions">extensions</a>.</li>
</ul>
</blockquote>
@ -657,6 +659,7 @@ struct peer_info
int upload_ceiling;
int load_balancing;
int download_queue_length;
int downloading_piece_index;
int downloading_block_index;
@ -720,6 +723,8 @@ and free upload that we give. Every peer gets a certain amount of free upload, b
this member says how much <em>extra</em> free upload this peer has got. If it is a negative
number it means that this was a peer from which we have got this amount of free
download.</p>
<p><tt class="literal"><span class="pre">download_queue_length</span></tt> is the number of block-requests we have sent to this peer
that hasn't been answered with a piece yet.</p>
<p>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.
<tt class="literal"><span class="pre">downloading_piece_index</span></tt> is the index of the piece that is currently being downloaded.
@ -1299,23 +1304,58 @@ each piece is 16 * 1024 bytes in size.</td>
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:</p>
<ul class="simple">
to meet the following requirement:</p>
<ul class="last simple">
<li>if there's a slot at the position of the piece index,
the piece must be located in that slot.</li>
</ul>
<p class="last">TODO: finish</p>
</td>
</tr>
<tr><td><tt class="literal"><span class="pre">peers</span></tt></td>
<td>&nbsp;</td>
<td><p class="first">list of dictionaries. Each dictionary has the following
layout:</p>
<table border class="table">
<colgroup>
<col width="18%" />
<col width="82%" />
</colgroup>
<tbody valign="top">
<tr><td><tt class="literal"><span class="pre">ip</span></tt></td>
<td>string, the ip address of the peer.</td>
</tr>
<tr><td><tt class="literal"><span class="pre">unfinished</span></tt></td>
<td>&nbsp;</td>
<tr><td><tt class="literal"><span class="pre">port</span></tt></td>
<td>integer, the listen port of the peer</td>
</tr>
</tbody>
</table>
<p class="last">These are the local peers we were connected to when this
fast-resume data was saved.</p>
</td>
</tr>
<tr><td><tt class="literal"><span class="pre">unfinished</span></tt></td>
<td><p class="first">list of dictionaries. Each dictionary represents an
piece, and has the following layout:</p>
<table border class="last table">
<colgroup>
<col width="23%" />
<col width="77%" />
</colgroup>
<tbody valign="top">
<tr><td><tt class="literal"><span class="pre">piece</span></tt></td>
<td>integer, the index of the piece this entry
refers to.</td>
</tr>
<tr><td><tt class="literal"><span class="pre">bitmask</span></tt></td>
<td>string, a binary bitmask representing the
blocks that have been downloaded in this
piece.</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<p>TODO: describe the file format</p>
</div>
<div class="section" id="extensions">
<h1><a class="toc-backref" href="#id45" name="extensions">extensions</a></h1>

View File

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

View File

@ -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<const char*>((i->flags & peer_info::interesting)?"I":"_")
<< static_cast<const char*>((i->flags & peer_info::choked)?"C":"_")
<< static_cast<const char*>((i->flags & peer_info::remote_interested)?"i":"_")
<< static_cast<const char*>((i->flags & peer_info::remote_choked)?"c":"_")
<< static_cast<const char*>((i->flags & peer_info::supports_extensions)?"e":"_")
<< static_cast<const char*>((i->flags & peer_info::local_connection)?"l":"r")
<< "\n";
if (i->downloading_piece_index >= 0)

View File

@ -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<piece_block>& download_queue() const throw()
const std::deque<piece_block>& 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<piece_block_progress> 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

View File

@ -38,6 +38,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include <cassert>
#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<peer_id>& d, int index);
void get_downloaders(std::vector<address>& d, int index);
const std::vector<downloading_piece>& get_download_queue() const
{ return m_downloads; }

View File

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

View File

@ -41,7 +41,7 @@ namespace libtorrent
class stat
{
enum { history = 5 };
enum { history = 10 };
public:
stat()

View File

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

View File

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

View File

@ -115,7 +115,7 @@ namespace libtorrent
int blocks_in_piece;
std::bitset<max_blocks_per_piece> requested_blocks;
std::bitset<max_blocks_per_piece> finished_blocks;
peer_id peer[max_blocks_per_piece];
address peer[max_blocks_per_piece];
int num_downloads[max_blocks_per_piece];
};

View File

@ -107,7 +107,7 @@ namespace libtorrent
if (!std::isdigit(*i)) return boost::optional<fingerprint>();
ret.revision_version = *i - '0';
}
else if (id[0] == 0)
else if (id[8] == 0)
{
if (*i > 127) return boost::optional<fingerprint>();
ret.major_version = *i;

View File

@ -34,6 +34,8 @@ POSSIBILITY OF SUCH DAMAGE.
#include <iomanip>
#include <vector>
#include <limits>
#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<piece_block>::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<int>::max();
return m_free_upload
+ (m_statistics.total_payload_download() * ratio)
+ static_cast<int>(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<int>(m_statistics.download_rate() * ratio) / 2;
if (bias < 10*1024) bias = 10*1024;
}
else
{
bias = -(m_statistics.download_rate() * ratio) / 2;
bias = -static_cast<int>(m_statistics.download_rate() * ratio) / 2;
}
m_send_quota_limit = m_statistics.download_rate() + bias;
m_send_quota_limit = static_cast<int>(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();
}
}

View File

@ -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<peer_id>& d, int index)
void piece_picker::get_downloaders(std::vector<address>& d, int index)
{
std::vector<downloading_piece>::iterator i
= std::find_if(m_downloads.begin(), m_downloads.end(), has_index(index));

View File

@ -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<int>::max();
// TODO: make this selection better
for (std::vector<peer>::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<int>(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<peer>::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<peer>::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);
}

View File

@ -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<piece_picker::downloading_piece> 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;

View File

@ -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<peer>::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<peer>::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<peer_id> downloaders;
std::vector<address> 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<peer_id> downloaders;
std::vector<address> 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()

View File

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