*** empty log message ***

This commit is contained in:
Arvid Norberg 2003-12-14 05:56:12 +00:00
parent a19ec4afc2
commit 24e4c197c9
15 changed files with 512 additions and 164 deletions

View File

@ -90,17 +90,19 @@ 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> 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</li> <li>can limit the upload bandwidth usage</li>
<li>piece-wise file allocation</li> <li>piece-wise file allocation</li>
<li>upload rate limit, balanced depending on download speed and upload bandwidth</li>
</ul> </ul>
</blockquote> </blockquote>
<p>Functions that are yet to be implemented:</p> <p>Functions that are yet to be implemented:</p>
<blockquote> <blockquote>
<ul class="simple"> <ul class="simple">
<li>optimistic unchoke</li> <li>more generous optimistic unchoke</li>
<li>choke/unchoke algorithm</li> <li>better choke/unchoke algorithm</li>
<li>Snubbing</li>
<li>fast resume</li> <li>fast resume</li>
<li>number of connections limit</li>
<li>better handling of peers that send bad data</li>
<li>ip-filters</li>
<li>file-level piece priority</li> <li>file-level piece priority</li>
<li>a good upload speed cap (the one currently used don't balance loads between peers)</li>
</ul> </ul>
</blockquote> </blockquote>
<p>libtorrent is portable at least among windows, macosx, and UNIX-systems. It uses boost.thread, <p>libtorrent is portable at least among windows, macosx, and UNIX-systems. It uses boost.thread,
@ -407,6 +409,8 @@ struct torrent_handle
boost::filsystem::path save_path() const; boost::filsystem::path save_path() const;
void set_max_uploads(int max_uploads);
sha1_hash info_hash() const; sha1_hash info_hash() const;
bool operator==(const torrent_handle&amp;) const; bool operator==(const torrent_handle&amp;) const;
@ -416,10 +420,12 @@ struct torrent_handle
</pre> </pre>
<p>The default constructor will initialize the handle to an invalid state. Which means you cannot <p>The default constructor will initialize the handle to an invalid state. Which means you cannot
perform any operation on it, unless you first assign it a valid handle. If you try to perform perform any operation on it, unless you first assign it a valid handle. If you try to perform
any operation they will simply return.</p> any operation on an uninitialized handle, it will throw <tt class="literal"><span class="pre">invalid_handle</span></tt>.</p>
<p><tt class="literal"><span class="pre">save_path()</span></tt> returns the path that was given to <tt class="literal"><span class="pre">add_torrent()</span></tt> when this torrent <p><tt class="literal"><span class="pre">save_path()</span></tt> returns the path that was given to <tt class="literal"><span class="pre">add_torrent()</span></tt> when this torrent
was started.</p> was started.</p>
<p><tt class="literal"><span class="pre">info_hash()</span></tt> returns the info hash for the torrent.</p> <p><tt class="literal"><span class="pre">info_hash()</span></tt> returns the info hash for the torrent.</p>
<p><tt class="literal"><span class="pre">set_max_uploads()</span></tt> sets the maximum number of peers that's unchoked at the same time on this
torrent. If you set this to -1, there will be no limit.</p>
<div class="section" id="status"> <div class="section" id="status">
<h3><a class="toc-backref" href="#id15" name="status">status()</a></h3> <h3><a class="toc-backref" href="#id15" name="status">status()</a></h3>
<p><tt class="literal"><span class="pre">status()</span></tt> will return a structure with information about the status of this <p><tt class="literal"><span class="pre">status()</span></tt> will return a structure with information about the status of this
@ -904,6 +910,7 @@ int main(int argc, char* argv[])
<div class="section" id="aknowledgements"> <div class="section" id="aknowledgements">
<h1><a class="toc-backref" href="#id34" name="aknowledgements">Aknowledgements</a></h1> <h1><a class="toc-backref" href="#id34" name="aknowledgements">Aknowledgements</a></h1>
<p>Written by Arvid Norberg and Daniel Wallin. Copyright (c) 2003</p> <p>Written by Arvid Norberg and Daniel Wallin. Copyright (c) 2003</p>
<p>Contributions by Magnus Jonsson</p>
<p>Project is hosted by sourceforge.</p> <p>Project is hosted by sourceforge.</p>
<p><a class="reference" href="http://sourceforge.net"><img alt="sf_logo" src="http://sourceforge.net/sflogo.php?group_id=7994" /></a></p> <p><a class="reference" href="http://sourceforge.net"><img alt="sf_logo" src="http://sourceforge.net/sflogo.php?group_id=7994" /></a></p>
</div> </div>

View File

@ -41,18 +41,20 @@ The current state includes the following features:
thread-safe library interface. (i.e. There's no way for the user to cause a deadlock). thread-safe library interface. (i.e. There's no way for the user to cause a deadlock).
* can limit the upload bandwidth usage * can limit the upload bandwidth usage
* piece-wise file allocation * piece-wise file allocation
* upload rate limit, balanced depending on download speed and upload bandwidth
__ http://home.elp.rr.com/tur/multitracker-spec.txt __ http://home.elp.rr.com/tur/multitracker-spec.txt
.. _Azureus: http://azureus.sourceforge.net .. _Azureus: http://azureus.sourceforge.net
Functions that are yet to be implemented: Functions that are yet to be implemented:
* optimistic unchoke * more generous optimistic unchoke
* choke/unchoke algorithm * better choke/unchoke algorithm
* Snubbing
* fast resume * fast resume
* number of connections limit
* better handling of peers that send bad data
* ip-filters
* file-level piece priority * file-level piece priority
* a good upload speed cap (the one currently used don't balance loads between peers)
libtorrent is portable at least among windows, macosx, and UNIX-systems. It uses boost.thread, libtorrent is portable at least among windows, macosx, and UNIX-systems. It uses boost.thread,
boost.filesystem boost.date_time and various other boost libraries and zlib. boost.filesystem boost.date_time and various other boost libraries and zlib.
@ -426,6 +428,8 @@ Its declaration looks like this::
boost::filsystem::path save_path() const; boost::filsystem::path save_path() const;
void set_max_uploads(int max_uploads);
sha1_hash info_hash() const; sha1_hash info_hash() const;
bool operator==(const torrent_handle&) const; bool operator==(const torrent_handle&) const;
@ -435,13 +439,16 @@ Its declaration looks like this::
The default constructor will initialize the handle to an invalid state. Which means you cannot The default constructor will initialize the handle to an invalid state. Which means you cannot
perform any operation on it, unless you first assign it a valid handle. If you try to perform perform any operation on it, unless you first assign it a valid handle. If you try to perform
any operation they will simply return. any operation on an uninitialized handle, it will throw ``invalid_handle``.
``save_path()`` returns the path that was given to ``add_torrent()`` when this torrent ``save_path()`` returns the path that was given to ``add_torrent()`` when this torrent
was started. was started.
``info_hash()`` returns the info hash for the torrent. ``info_hash()`` returns the info hash for the torrent.
``set_max_uploads()`` sets the maximum number of peers that's unchoked at the same time on this
torrent. If you set this to -1, there will be no limit.
status() status()
~~~~~~~~ ~~~~~~~~
@ -990,6 +997,8 @@ Aknowledgements
Written by Arvid Norberg and Daniel Wallin. Copyright (c) 2003 Written by Arvid Norberg and Daniel Wallin. Copyright (c) 2003
Contributions by Magnus Jonsson
Project is hosted by sourceforge. Project is hosted by sourceforge.
|sf_logo|__ |sf_logo|__

View File

@ -36,7 +36,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include <exception> #include <exception>
#include <boost/format.hpp> #include <boost/format.hpp>
//#include <boost/date_time/posix_time/posix_time.hpp> #include <boost/date_time/posix_time/posix_time.hpp>
#include "libtorrent/entry.hpp" #include "libtorrent/entry.hpp"
#include "libtorrent/bencode.hpp" #include "libtorrent/bencode.hpp"
@ -75,7 +75,7 @@ void clear()
HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE); HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
COORD c = {0, 0}; COORD c = {0, 0};
DWORD n; DWORD n;
FillConsoleOutputCharacter(h, ' ', 80 * 80, c, &n); FillConsoleOutputCharacter(h, ' ', 120 * 80, c, &n);
} }
#else #else
@ -185,7 +185,7 @@ int main(int argc, char* argv[])
session s(6881, "E\x1"); session s(6881, "E\x1");
// limit upload rate to 100 kB/s // limit upload rate to 100 kB/s
// s.set_upload_rate_limit(100 * 1024); s.set_upload_rate_limit(100 * 1024);
s.set_http_settings(settings); s.set_http_settings(settings);
for (int i = 0; i < argc-1; ++i) for (int i = 0; i < argc-1; ++i)
@ -198,6 +198,7 @@ int main(int argc, char* argv[])
torrent_info t(e); torrent_info t(e);
t.print(std::cout); t.print(std::cout);
handles.push_back(s.add_torrent(t, "")); handles.push_back(s.add_torrent(t, ""));
handles.back().set_max_uploads(20);
} }
catch (std::exception& e) catch (std::exception& e)
{ {
@ -246,15 +247,7 @@ int main(int argc, char* argv[])
int total_down = s.total_download; int total_down = s.total_download;
int total_up = s.total_upload; int total_up = s.total_upload;
int num_peers = peers.size(); int num_peers = peers.size();
/*
std::cout << boost::format("%f%% p:%d d:(%s) %s/s u:(%s) %s/s\n")
% (s.progress*100)
% num_peers
% add_suffix(total_down)
% add_suffix(down)
% add_suffix(total_up)
% add_suffix(up);
*/
out.precision(4); out.precision(4);
out.width(5); out.width(5);
out.fill(' '); out.fill(' ');
@ -276,8 +269,7 @@ int main(int argc, char* argv[])
<< "diff: " << add_suffix(total_down - total_up) << "\n"; << "diff: " << add_suffix(total_down - total_up) << "\n";
boost::posix_time::time_duration t = s.next_announce; boost::posix_time::time_duration t = s.next_announce;
// std::cout << "next announce: " << boost::posix_time::to_simple_string(t) << "\n"; out << "next announce: " << boost::posix_time::to_simple_string(t) << "\n";
out << "next announce: " << t.hours() << ":" << t.minutes() << ":" << t.seconds() << "\n";
for (std::vector<peer_info>::iterator i = peers.begin(); for (std::vector<peer_info>::iterator i = peers.begin();
i != peers.end(); i != peers.end();
@ -288,7 +280,7 @@ int main(int argc, char* argv[])
<< "u: " << add_suffix(i->up_speed) << "/s " << "u: " << add_suffix(i->up_speed) << "/s "
<< "(" << add_suffix(i->total_upload) << ") " << "(" << add_suffix(i->total_upload) << ") "
<< "df: " << add_suffix((int)i->total_download - (int)i->total_upload) << " " << "df: " << add_suffix((int)i->total_download - (int)i->total_upload) << " "
<< "l: " << add_suffix(i->upload_ceiling) << "/s " << "l: " << add_suffix(i->upload_limit) << "/s "
<< "f: " << "f: "
<< static_cast<const char*>((i->flags & peer_info::interesting)?"I":"_") << 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::choked)?"C":"_")
@ -305,13 +297,12 @@ int main(int argc, char* argv[])
if (progress * 20 > j) out << "#"; if (progress * 20 > j) out << "#";
else out << "-"; else out << "-";
} }
out << "\n";
} }
out << "\n";
} }
out << "___________________________________\n"; out << "___________________________________\n";
/*
i->get_download_queue(queue); i->get_download_queue(queue);
for (std::vector<partial_piece_info>::iterator i = queue.begin(); for (std::vector<partial_piece_info>::iterator i = queue.begin();
i != queue.end(); i != queue.end();
@ -324,13 +315,13 @@ int main(int argc, char* argv[])
{ {
if (i->finished_blocks[j]) out << "#"; if (i->finished_blocks[j]) out << "#";
else if (i->requested_blocks[j]) out << "="; else if (i->requested_blocks[j]) out << "=";
else out << "-"; else out << ".";
} }
out << "|\n"; out << "|\n";
} }
out << "___________________________________\n"; out << "___________________________________\n";
*/
} }
clear(); clear();

View File

@ -59,7 +59,17 @@ POSSIBILITY OF SUCH DAMAGE.
// TODO: maybe there should be some kind of // TODO: maybe there should be some kind of
// per-torrent free-upload counter. All free // per-torrent free-upload counter. All free
// download we get is put in there and increases // download we get is put in there and increases
// the amount of free upload we give. // the amount of free upload we give. The free upload
// could be distributed to the interest peers
// depending on amount we have downloaded from
// the peer and depending on the share ratio.
// there's no point in giving free upload to
// peers we can trade with. Maybe the free upload
// only should be given to those we are not interested
// in?
// TODO: the interested flag has to be updated when we
// get pieces.
namespace libtorrent namespace libtorrent
{ {

View File

@ -36,14 +36,11 @@ POSSIBILITY OF SUCH DAMAGE.
#include <algorithm> #include <algorithm>
#include <vector> #include <vector>
#include <boost/weak_ptr.hpp>
#include <boost/date_time/posix_time/posix_time.hpp> #include <boost/date_time/posix_time/posix_time.hpp>
#include "libtorrent/peer.hpp" #include "libtorrent/peer.hpp"
#include "libtorrent/piece_picker.hpp" #include "libtorrent/piece_picker.hpp"
// TODO: should be able to close connections with too low bandwidth to save memory
namespace libtorrent namespace libtorrent
{ {
@ -63,7 +60,7 @@ namespace libtorrent
// called when an incoming connection is accepted // called when an incoming connection is accepted
// return false if the connection closed // return false if the connection closed
bool new_connection(const boost::weak_ptr<peer_connection>& c); bool new_connection(peer_connection& c);
// this is called once for every peer we get from // this is called once for every peer we get from
// the tracker // the tracker
@ -79,7 +76,7 @@ namespace libtorrent
// the peer has got at least one interesting piece // the peer has got at least one interesting piece
void peer_is_interesting(peer_connection& c); void peer_is_interesting(peer_connection& c);
void piece_finished(peer_connection& c, int index, bool successfully_verified); void piece_finished(int index, bool successfully_verified);
void block_finished(peer_connection& c, piece_block b); void block_finished(peer_connection& c, piece_block b);
@ -95,23 +92,22 @@ namespace libtorrent
// the peer is not interested in our pieces // the peer is not interested in our pieces
void not_interested(peer_connection& c); void not_interested(peer_connection& c);
void set_max_uploads(int num_unchoked);
#ifndef NDEBUG #ifndef NDEBUG
bool has_connection(const peer_connection* p); bool has_connection(const peer_connection* p);
void check_invariant();
#endif #endif
private: private:
struct peer struct peer
{ {
peer(const peer_id& pid) peer(const peer_id& pid);
: id(pid)
, last_optimistically_unchoked(boost::posix_time::second_clock::local_time()) int total_download() const;
, connected(boost::posix_time::second_clock::local_time()) int total_upload() const;
, optimistic_unchokes(0)
, prev_amount_upload(0)
, prev_amount_download(0)
, banned(false)
{}
bool operator==(const peer_id& pid) const bool operator==(const peer_id& pid) const
{ return id == pid; } { return id == pid; }
@ -130,10 +126,6 @@ namespace libtorrent
// or disconnected if it isn't connected right now // or disconnected if it isn't connected right now
boost::posix_time::ptime connected; boost::posix_time::ptime connected;
// the number of optimistic unchokes this peer has
// been given
int optimistic_unchokes;
// this is the accumulated amount of // this is the accumulated amount of
// uploaded and downloaded data to this // uploaded and downloaded data to this
// peer. It only accounts for what was // peer. It only accounts for what was
@ -151,9 +143,13 @@ namespace libtorrent
// if the peer is connected now, this // if the peer is connected now, this
// will refer to a valid peer_connection // will refer to a valid peer_connection
boost::weak_ptr<peer_connection> connection; peer_connection* connection;
}; };
bool unchoke_one_peer();
peer* find_choke_candidate();
peer* find_unchoke_candidate();
// a functor that identifies peers that have disconnected and that // a functor that identifies peers that have disconnected and that
// are too old for still being saved. // are too old for still being saved.
struct old_disconnected_peer struct old_disconnected_peer
@ -162,7 +158,7 @@ namespace libtorrent
{ {
using namespace boost::posix_time; using namespace boost::posix_time;
return p.connection.expired() return p.connection == 0
&& second_clock::local_time() - p.connected > seconds(5*60); && second_clock::local_time() - p.connected > seconds(5*60);
} }
}; };
@ -173,6 +169,14 @@ namespace libtorrent
int m_num_peers; int m_num_peers;
torrent* m_torrent; torrent* m_torrent;
// the total number of unchoked peers at
// any given time. If set to -1, it's unlimited.
// must be 2 or higher otherwise.
int m_max_uploads;
// the number of unchoked peers
// at any given time
int m_num_unchoked;
}; };
} }

View File

@ -35,6 +35,7 @@ POSSIBILITY OF SUCH DAMAGE.
// TODO: remove the dependency of // TODO: remove the dependency of
// platform specific headers here. // platform specific headers here.
// sockaddr_in is hard to get rid of in a nice way
#if defined(_WIN32) #if defined(_WIN32)
#include <winsock2.h> #include <winsock2.h>

View File

@ -75,10 +75,6 @@ namespace libtorrent
m_total_upload_protocol += s.m_uploaded_protocol; m_total_upload_protocol += s.m_uploaded_protocol;
} }
// TODO: these function should take two arguments
// to be able to count both total data sent and also
// count only the actual payload (not counting the
// protocol chatter)
void received_bytes(int bytes_payload, int bytes_protocol) void received_bytes(int bytes_payload, int bytes_protocol)
{ {
m_downloaded += bytes_payload; m_downloaded += bytes_payload;

View File

@ -63,18 +63,6 @@ namespace libtorrent
struct session_impl; struct session_impl;
} }
// TODO: each torrent should have a status value that
// reflects what's happening to it
// TODO: There should be a maximum number of peers that
// is maintained (if someone disconnects, try to connect to
// anotherone). There should also be a candidate slot where a
// new peer is tried for one minute, and if it has better ownload
// speed than one of the peers currently connected, it will be
// replaced to maximize bandwidth usage. It wil also have to
// depend on how many and which pieces the peers have.
// TODO: In debug mode all pieces that are sent should be checked.
// a torrent is a class that holds information // a torrent is a class that holds information
// for a specific download. It updates itself against // for a specific download. It updates itself against
// the tracker // the tracker
@ -88,7 +76,8 @@ namespace libtorrent
detail::session_impl& ses detail::session_impl& ses
, const torrent_info& torrent_file , const torrent_info& torrent_file
, const boost::filesystem::path& save_path); , const boost::filesystem::path& save_path);
~torrent() {}
~torrent();
void abort() { m_abort = true; m_event = event_stopped; } void abort() { m_abort = true; m_event = event_stopped; }
bool is_aborted() const { return m_abort; } bool is_aborted() const { return m_abort; }
@ -115,7 +104,7 @@ namespace libtorrent
torrent_status status() const; torrent_status status() const;
boost::weak_ptr<peer_connection> connect_to_peer( peer_connection& connect_to_peer(
const address& a const address& a
, const peer_id& id); , const peer_id& id);
@ -248,6 +237,7 @@ namespace libtorrent
#ifndef NDEBUG #ifndef NDEBUG
virtual void debug_log(const std::string& line); virtual void debug_log(const std::string& line);
void check_invariant();
#endif #endif
private: private:

View File

@ -115,8 +115,14 @@ namespace libtorrent
// to finish all pieces currently in the pipeline, and then // to finish all pieces currently in the pipeline, and then
// abort the torrent. // abort the torrent.
// TODO: add finish_file_allocation, which will force the
// torrent to allocate storage for all pieces.
boost::filesystem::path save_path() const; boost::filesystem::path save_path() const;
// -1 means unlimited unchokes
void set_max_uploads(int max_uploads);
const sha1_hash& info_hash() const const sha1_hash& info_hash() const
{ return m_info_hash; } { return m_info_hash; }

View File

@ -174,8 +174,7 @@ libtorrent::peer_connection::~peer_connection()
void libtorrent::peer_connection::set_send_quota(int num_bytes) void libtorrent::peer_connection::set_send_quota(int num_bytes)
{ {
assert(num_bytes <= m_send_quota_limit); assert(num_bytes <= m_send_quota_limit || m_send_quota_limit == -1);
assert(num_bytes >= 0);
if (num_bytes > m_send_quota_limit) num_bytes = m_send_quota_limit; if (num_bytes > m_send_quota_limit) num_bytes = m_send_quota_limit;
m_send_quota = num_bytes; m_send_quota = num_bytes;
@ -438,12 +437,17 @@ bool libtorrent::peer_connection::dispatch_message(int received)
r.piece = read_int(&m_recv_buffer[1]); r.piece = read_int(&m_recv_buffer[1]);
r.start = read_int(&m_recv_buffer[5]); r.start = read_int(&m_recv_buffer[5]);
r.length = read_int(&m_recv_buffer[9]); r.length = read_int(&m_recv_buffer[9]);
m_requests.push_back(r);
if (!m_choked) if (!m_choked)
{ {
m_requests.push_back(r);
send_buffer_updated(); send_buffer_updated();
} }
else
{
// ignoring request since we have
// choked this peer
}
#ifndef NDEBUG #ifndef NDEBUG
(*m_logger) << m_socket->sender().as_string() << " <== REQUEST [ piece: " << r.piece << " | s: " << r.start << " | l: " << r.length << " ]\n"; (*m_logger) << m_socket->sender().as_string() << " <== REQUEST [ piece: " << r.piece << " | s: " << r.start << " | l: " << r.length << " ]\n";
@ -575,7 +579,7 @@ bool libtorrent::peer_connection::dispatch_message(int received)
{ {
m_torrent->piece_failed(index); m_torrent->piece_failed(index);
} }
m_torrent->get_policy().piece_finished(*this, index, verified); m_torrent->get_policy().piece_finished(index, verified);
} }
break; break;
} }
@ -688,9 +692,6 @@ void libtorrent::peer_connection::request_block(piece_block block)
std::size_t start_offset = m_send_buffer.size(); std::size_t start_offset = m_send_buffer.size();
m_send_buffer.resize(start_offset + 17); m_send_buffer.resize(start_offset + 17);
// TODO: add a timeout to disconnect peer if we don't get any piece messages when
// we have requested.
std::copy(buf, buf + 5, m_send_buffer.begin()+start_offset); std::copy(buf, buf + 5, m_send_buffer.begin()+start_offset);
start_offset +=5; start_offset +=5;
@ -741,6 +742,7 @@ void libtorrent::peer_connection::choke()
#ifndef NDEBUG #ifndef NDEBUG
(*m_logger) << m_socket->sender().as_string() << " ==> CHOKE\n"; (*m_logger) << m_socket->sender().as_string() << " ==> CHOKE\n";
#endif #endif
m_requests.clear();
send_buffer_updated(); send_buffer_updated();
} }
@ -804,19 +806,15 @@ void libtorrent::peer_connection::second_tick()
// client has sent us. This is the mean to // client has sent us. This is the mean to
// maintain a 1:1 share ratio with all peers. // maintain a 1:1 share ratio with all peers.
// TODO: make sure the rate is able to rise if
// both peers uses this technique! It could be
// enough to just have a constant positive bias
// of the send_quota_limit
int diff = static_cast<int>(m_statistics.total_download()) int diff = static_cast<int>(m_statistics.total_download())
- static_cast<int>(m_statistics.total_upload()); - static_cast<int>(m_statistics.total_upload());
if (diff > m_torrent->torrent_file().piece_length()) if (diff > 2*m_torrent->block_size())
{ {
// if we have downloaded more than one piece more // if we have downloaded more than one piece more
// than we have uploaded, have an unlimited // than we have uploaded, have an unlimited
// upload rate // upload rate
m_send_quota = -1; m_send_quota_limit = -1;
} }
else else
{ {
@ -824,18 +822,20 @@ void libtorrent::peer_connection::second_tick()
// upload rate of 10 kB/s more than we dowlload // upload rate of 10 kB/s more than we dowlload
// if we have uploaded too much, send with a rate of // if we have uploaded too much, send with a rate of
// 10 kB/s less than we receive // 10 kB/s less than we receive
if (diff > -32*1024) int bias = 0;
if (diff > -2*m_torrent->block_size())
{ {
m_send_quota_limit = m_statistics.download_rate() * 1.5; bias = m_statistics.download_rate() * .5;
if (bias < 10*1024) bias = 10*1024;
} }
else else
{ {
m_send_quota_limit = m_statistics.download_rate() * .5; bias = -m_statistics.download_rate() * .5;
} }
m_send_quota_limit = m_statistics.download_rate() + bias;
// the maximum send_quota given our download rate from this peer // the maximum send_quota given our download rate from this peer
if (m_send_quota_limit < 500) m_send_quota_limit = 500; if (m_send_quota_limit < 256) m_send_quota_limit = 256;
} }
assert(m_send_quota_limit >= 500 || m_send_quota_limit == -1);
} }
// -------------------------- // --------------------------

View File

@ -49,7 +49,11 @@ namespace
{ {
// we try to maintain 4 requested blocks in the download // we try to maintain 4 requested blocks in the download
// queue // queue
request_queue = 16 request_queue = 16,
// the amount of free upload allowed before
// the peer is choked
free_upload_amount = 4 * 16 * 1024
}; };
@ -197,18 +201,83 @@ namespace libtorrent
void peer_connection::request_piece(int index); void peer_connection::request_piece(int index);
const std::vector<int>& peer_connection::download_queue(); const std::vector<int>& peer_connection::download_queue();
TODO: implement a limit of the number of unchoked peers.
TODO: implement some kind of limit of the number of sockets TODO: implement some kind of limit of the number of sockets
opened, to use for systems where a user has a limited number opened, to use for systems where a user has a limited number
of open file descriptors of open file descriptors. and for windows which has a buggy tcp-stack.
*/ */
policy::policy(torrent* t) policy::policy(torrent* t)
: m_num_peers(0) : m_num_peers(0)
, m_torrent(t) , m_torrent(t)
, m_max_uploads(-1)
, m_num_unchoked(0)
{} {}
// finds the peer that has the worst download rate
// and returns it. May return 0 if all peers are
// choked.
policy::peer* policy::find_choke_candidate()
{
peer* worst_peer = 0;
int min_weight = std::numeric_limits<int>::max();
for (std::vector<peer>::iterator i = m_peers.begin();
i != m_peers.end();
++i)
{
peer_connection* c = i->connection;
if (c == 0) continue;
if (c->is_choked()) continue;
// if the peer isn't interested, just choke it
if (!c->is_peer_interested())
return &(*i);
int diff = i->total_download()
- i->total_upload();
int weight = c->statistics().download_rate() * 10
+ diff
+ (c->has_peer_choked()?-10:10)*1024;
if (weight > min_weight) continue;
min_weight = weight;
worst_peer = &(*i);
continue;
}
return worst_peer;
}
policy::peer* policy::find_unchoke_candidate()
{
// if all of our peers are unchoked, there's
// no left to unchoke
if (m_num_unchoked == m_torrent->num_peers())
return 0;
using namespace boost::posix_time;
using namespace boost::gregorian;
peer* unchoke_peer = 0;
ptime min_time(date(9999,Jan,1));
for (std::vector<peer>::iterator i = m_peers.begin();
i != m_peers.end();
++i)
{
peer_connection* c = i->connection;
if (c == 0) continue;
if (!c->is_choked()) continue;
if (!c->is_peer_interested()) continue;
if (i->total_download() - i->total_upload()
< -free_upload_amount) continue;
if (i->last_optimistically_unchoked > min_time) continue;
min_time = i->last_optimistically_unchoked;
unchoke_peer = &(*i);
}
return unchoke_peer;
}
void policy::pulse() void policy::pulse()
{ {
@ -221,34 +290,67 @@ namespace libtorrent
, old_disconnected_peer()) , old_disconnected_peer())
, m_peers.end()); , m_peers.end());
// choke peers that have leeched too much without giving anything back if (m_max_uploads != -1)
for (std::vector<peer>::iterator i = m_peers.begin(); i != m_peers.end(); ++i)
{ {
boost::shared_ptr<peer_connection> c = i->connection.lock(); // make sure we don't have too many
if (c.get() == 0) continue; // unchoked peers
while (m_num_unchoked > m_max_uploads)
int downloaded = i->prev_amount_download + c->statistics().total_download();
int uploaded = i->prev_amount_upload + c->statistics().total_upload();
if (uploaded - downloaded > m_torrent->torrent_file().piece_length()
&& !c->is_choked())
{ {
// if we have uploaded more than a piece for free, choke peer and peer* p = find_choke_candidate();
// wait until we catch up with our download. assert(p);
c->choke(); p->connection->choke();
--m_num_unchoked;
} }
else if (uploaded - downloaded <= m_torrent->block_size()
&& c->is_choked() && c->is_interesting())
{
// TODO: if we're not interested in this peer
// we should only unchoke it if it' its turn
// to be optimistically unchoked.
// we have catched up. We have now shared the same amount // optimistic unchoke. trade the 'worst'
// to eachother. Unchoke this peer. // unchoked peer with one of the choked
c->unchoke(); assert(m_num_unchoked <= m_torrent->num_peers());
peer* p = find_choke_candidate();
if (p)
{
p->connection->choke();
--m_num_unchoked;
unchoke_one_peer();
} }
// make sure we have enough
// unchoked peers
while (m_num_unchoked < m_max_uploads && unchoke_one_peer());
} }
else
{
// choke peers that have leeched too much without giving anything back
for (std::vector<peer>::iterator i = m_peers.begin();
i != m_peers.end();
++i)
{
peer_connection* c = i->connection;
if (c == 0) continue;
int downloaded = i->total_download();
int uploaded = i->total_upload();
if (downloaded - uploaded < -free_upload_amount
&& !c->is_choked())
{
// if we have uploaded more than a piece for free, choke peer and
// wait until we catch up with our download.
c->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();
}
}
}
#ifndef NDEBUG
check_invariant();
#endif
} }
void policy::ban_peer(const peer_connection& c) void policy::ban_peer(const peer_connection& c)
@ -259,30 +361,29 @@ namespace libtorrent
i->banned = true; i->banned = true;
} }
bool policy::new_connection(const boost::weak_ptr<peer_connection>& c) bool policy::new_connection(peer_connection& c)
{ {
boost::shared_ptr<peer_connection> con = c.lock();
assert(con.get() != 0);
if (con.get() == 0) return false;
std::vector<peer>::iterator i std::vector<peer>::iterator i
= std::find(m_peers.begin(), m_peers.end(), con->get_peer_id()); = std::find(m_peers.begin(), m_peers.end(), c.get_peer_id());
if (i == m_peers.end()) if (i == m_peers.end())
{ {
using namespace boost::posix_time;
using namespace boost::gregorian;
// we don't have ny info about this peer. // we don't have ny info about this peer.
// add a new entry // add a new entry
peer p(con->get_peer_id()); peer p(c.get_peer_id());
m_peers.push_back(p); m_peers.push_back(p);
i = m_peers.end()-1; i = m_peers.end()-1;
} }
else else
{ {
assert(i->connection.expired()); assert(i->connection == 0);
if (i->banned) return false; if (i->banned) return false;
} }
i->connected = boost::posix_time::second_clock::local_time(); i->connected = boost::posix_time::second_clock::local_time();
i->connection = c; i->connection = &c;
return true; return true;
} }
@ -293,13 +394,16 @@ namespace libtorrent
std::vector<peer>::iterator i = std::find(m_peers.begin(), m_peers.end(), id); std::vector<peer>::iterator i = std::find(m_peers.begin(), m_peers.end(), id);
if (i == m_peers.end()) if (i == m_peers.end())
{ {
using namespace boost::posix_time;
using namespace boost::gregorian;
// we don't have ny info about this peer. // we don't have ny info about this peer.
// add a new entry // add a new entry
peer p(id); peer p(id);
m_peers.push_back(p); m_peers.push_back(p);
i = m_peers.end()-1; i = m_peers.end()-1;
} }
else if (!i->connection.expired()) else if (!i->connection == 0)
{ {
// this means we're already connected // this means we're already connected
// to this peer. don't connect to // to this peer. don't connect to
@ -310,7 +414,7 @@ namespace libtorrent
if (i->banned) return; if (i->banned) return;
i->connected = boost::posix_time::second_clock::local_time(); i->connected = boost::posix_time::second_clock::local_time();
i->connection = m_torrent->connect_to_peer(remote, id); i->connection = &m_torrent->connect_to_peer(remote, id);
} }
catch(network_error&) {} catch(network_error&) {}
@ -324,8 +428,7 @@ namespace libtorrent
{ {
} }
// TODO: the peer_connection argument here should be removed. void policy::piece_finished(int index, bool successfully_verified)
void policy::piece_finished(peer_connection& c, int index, bool successfully_verified)
{ {
// TODO: if verification failed, mark the peers that were involved // TODO: if verification failed, mark the peers that were involved
// in some way // in some way
@ -348,15 +451,33 @@ namespace libtorrent
} }
} }
// called when a peer is interested in us
void policy::interested(peer_connection& c) void policy::interested(peer_connection& c)
{ {
// if we're interested in the peer, we unchoke it // if we're interested in the peer, we unchoke it
// and hopes it will unchoke us too // and hopes it will unchoke us too
if (c.is_interesting()) c.unchoke(); /* if (c.is_interesting())
} {
c.unchoke();
++m_num_unchoked;
}
*/ }
// called when a peer is no longer interested in us
void policy::not_interested(peer_connection& c) void policy::not_interested(peer_connection& c)
{ {
c.not_interested();
}
bool policy::unchoke_one_peer()
{
peer* p = find_unchoke_candidate();
if (p == 0) return false;
p->connection->unchoke();
p->last_optimistically_unchoked = boost::posix_time::second_clock::local_time();
++m_num_unchoked;
return true;
} }
// this is called whenever a peer connection is closed // this is called whenever a peer connection is closed
@ -370,6 +491,18 @@ namespace libtorrent
i->connected = boost::posix_time::second_clock::local_time(); i->connected = boost::posix_time::second_clock::local_time();
i->prev_amount_download += c.statistics().total_download(); i->prev_amount_download += c.statistics().total_download();
i->prev_amount_upload += c.statistics().total_upload(); i->prev_amount_upload += c.statistics().total_upload();
if (!i->connection->is_choked() && !m_torrent->is_aborted())
{
--m_num_unchoked;
unchoke_one_peer();
}
i->connection = 0;
}
void policy::set_max_uploads(int max_uploads)
{
assert(max_uploads > 1 || max_uploads == -1);
m_max_uploads = max_uploads;
} }
void policy::peer_is_interesting(peer_connection& c) void policy::peer_is_interesting(peer_connection& c)
@ -384,5 +517,47 @@ namespace libtorrent
{ {
return std::find(m_peers.begin(), m_peers.end(), p->get_peer_id()) != m_peers.end(); return std::find(m_peers.begin(), m_peers.end(), p->get_peer_id()) != m_peers.end();
} }
void policy::check_invariant()
{
assert(m_max_uploads >= 2 || m_max_uploads == -1);
int actual_unchoked = 0;
for (std::vector<peer>::iterator i = m_peers.begin();
i != m_peers.end();
++i)
{
if (!i->connection) continue;
if (!i->connection->is_choked()) actual_unchoked++;
}
assert(actual_unchoked <= m_max_uploads || m_max_uploads == -1);
}
#endif #endif
policy::peer::peer(const peer_id& pid)
: id(pid)
, last_optimistically_unchoked(
boost::gregorian::date(1970,boost::gregorian::Jan,1))
, connected(boost::posix_time::second_clock::local_time())
, prev_amount_upload(0)
, prev_amount_download(0)
, banned(false)
{}
int policy::peer::total_download() const
{
if (connection != 0)
return connection->statistics().total_download()
+ prev_amount_download;
else
return prev_amount_download;
}
int policy::peer::total_upload() const
{
if (connection != 0)
return connection->statistics().total_upload()
+ prev_amount_upload;
else
return prev_amount_upload;
}
} }

View File

@ -1,6 +1,6 @@
/* /*
Copyright (c) 2003, Arvid Norberg Copyright (c) 2003, Arvid Norberg, Magnus Jonsson
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
@ -63,6 +63,41 @@ namespace std
namespace namespace
{ {
// This struct is used by control_upload_rates() below. It keeps
// track how much bandwidth has been allocated to each connection
// and other relevant information to assist in the allocation process.
struct connection_info
{
libtorrent::peer_connection* p; // which peer_connection this info refers to
int allocated_quota; // bandwidth allocated to this peer connection
int quota_limit; // bandwidth limit
int estimated_upload_capacity; // estimated channel bandwidth
bool operator < (const connection_info &other) const
{
return estimated_upload_capacity < other.estimated_upload_capacity;
}
int give(int amount)
{
// if amount > 0, try to add amount to the allocated quota.
// if amount < 0, try to subtract abs(amount) from the allocated quota
//
// Quota will not go above quota_limit or below 0. This means that
// not all the amount given or taken may be accepted.
//
// return value: how much quota was actually added (or subtracted if negative).
int old_quota=allocated_quota;
allocated_quota+=amount;
allocated_quota=std::min(allocated_quota,quota_limit);
allocated_quota=std::max(0,allocated_quota);
return allocated_quota-old_quota;
}
};
// adjusts the upload rates of every peer connection // adjusts the upload rates of every peer connection
// to make sure the sum of all send quotas equals // to make sure the sum of all send quotas equals
// the given upload_limit. An upload limit of -1 means // the given upload_limit. An upload limit of -1 means
@ -92,21 +127,105 @@ namespace
} }
return; return;
} }
else
{
// There's an upload limit, so we need to distribute the available
// upload bandwidth among the peer_connections fairly, but not
// wastefully.
// TODO: upload limit support is currently broken // For each peer_connection, keep some local data about their
assert(false); // quota limit and estimated upload capacity, and how much quota
// has been allocated to them.
std::vector<connection_info> peer_info;
for (detail::session_impl::connection_map::iterator i = connections.begin();
i != connections.end();
++i)
{
peer_connection& p = *i->second;
connection_info pi;
pi.p=&p;
pi.allocated_quota=0; // we haven't given it any bandwith yet
pi.quota_limit=p.send_quota_limit();
pi.estimated_upload_capacity=
p.has_data() ? std::max(10,(int)p.statistics().upload_rate()*11/10)
// If there's no data to send, upload capacity is practically 0.
// Here we set it to 1 though, because otherwise it will not be able
// to accept any quota at all, which may upset quota_limit balances.
: 1;
peer_info.push_back(pi);
}
// Sum all peer_connections' quota limit to get the total quota limit.
int sum_total_of_quota_limits=0;
for(int i=0;i<peer_info.size();i++)
sum_total_of_quota_limits+=peer_info[i].quota_limit;
// This is how much total bandwidth that can be distributed.
int quota_left_to_distribute=std::min(upload_limit,sum_total_of_quota_limits);
// Sort w.r.t. channel capacitiy, lowest channel capacity first.
// Makes it easy to traverse the list in sorted order.
std::sort(peer_info.begin(),peer_info.end());
// Distribute quota until there's nothing more to distribute
while(quota_left_to_distribute!=0)
{
assert(quota_left_to_distribute>0);
for(int i=0;i<peer_info.size();i++)
{
// Traverse the peer list from slowest connection to fastest.
// In each step, share bandwidth equally between this peer_connection
// and the following faster peer_connections.
//
// Rounds upwards to avoid trying to give 0 bandwidth to someone (may get caught in an endless loop otherwise)
int num_peers_left_to_share_quota=peer_info.size()-i;
int try_to_give_to_this_peer=(quota_left_to_distribute + num_peers_left_to_share_quota-1)/num_peers_left_to_share_quota;
// But do not allocate more than the estimated upload capacity.
try_to_give_to_this_peer=std::min(
peer_info[i].estimated_upload_capacity,
try_to_give_to_this_peer);
// Also, when the peer is given quota, it will not accept more than it's quota_limit.
int quota_actually_given_to_peer=peer_info[i].give(try_to_give_to_this_peer);
quota_left_to_distribute-=quota_actually_given_to_peer;
}
}
// Finally, inform the peers of how much quota they get.
for(int i=0;i<peer_info.size();i++)
peer_info[i].p->set_send_quota(peer_info[i].allocated_quota);
}
#ifndef NDEBUG #ifndef NDEBUG
int sum = 0; {
int sum_quota = 0;
int sum_quota_limit = 0;
for (detail::session_impl::connection_map::iterator i = connections.begin(); for (detail::session_impl::connection_map::iterator i = connections.begin();
i != connections.end(); i != connections.end();
++i) ++i)
{ {
peer_connection& p = *i->second; peer_connection& p = *i->second;
sum += p.send_quota(); sum_quota += p.send_quota();
sum_quota_limit += p.send_quota_limit();
}
assert(abs(sum_quota - std::min(upload_limit,sum_quota_limit)) < 10);
} }
assert(sum == upload_limit);
#endif #endif
} }
} }
@ -324,9 +443,6 @@ namespace libtorrent
#ifndef NDEBUG #ifndef NDEBUG
(*m_logger) << s->sender().as_string() << " <== INCOMING CONNECTION\n"; (*m_logger) << s->sender().as_string() << " <== INCOMING CONNECTION\n";
#endif #endif
// TODO: the send buffer size should be controllable from the outside
// s->set_send_bufsize(2048);
// TODO: filter ip:s // TODO: filter ip:s
boost::shared_ptr<peer_connection> c( boost::shared_ptr<peer_connection> c(
@ -485,14 +601,6 @@ namespace libtorrent
++i; ++i;
} }
// distribute the maximum upload rate among the peers // distribute the maximum upload rate among the peers
// TODO: implement an intelligent algorithm that
// will shift bandwidth from the peers that can't
// utilize all their assigned bandwidth to the peers
// that actually can maintain the upload rate.
// This should probably be done by accumulating the
// left-over bandwidth to next second. Since the
// the sockets consumes its data in rather big chunks.
control_upload_rates(m_upload_rate, m_connections); control_upload_rates(m_upload_rate, m_connections);

View File

@ -105,6 +105,10 @@ namespace {
} }
// TODO: implement fast resume. i.e. the possibility to
// supply additional information about which pieces are
// assigned to which slots.
namespace libtorrent { namespace libtorrent {
struct thread_safe_storage struct thread_safe_storage
@ -608,8 +612,7 @@ namespace libtorrent {
for (int i = current_slot; i < m_info.num_pieces(); ++i) for (int i = current_slot; i < m_info.num_pieces(); ++i)
{ {
if (pieces[i]) if (pieces[i] && i != current_slot) continue;
continue;
const sha1_hash& hash = digest[ const sha1_hash& hash = digest[
i == m_info.num_pieces() - 1]->get(); i == m_info.num_pieces() - 1]->get();
@ -620,11 +623,20 @@ namespace libtorrent {
if (found_piece != -1) if (found_piece != -1)
{ {
if (pieces[found_piece])
{
assert(m_piece_to_slot[found_piece] != -1);
m_slot_to_piece[m_piece_to_slot[found_piece]] = -2;
m_free_slots.push_back(m_piece_to_slot[found_piece]);
}
else
{
m_bytes_left -= m_info.piece_size(found_piece); m_bytes_left -= m_info.piece_size(found_piece);
}
m_piece_to_slot[found_piece] = current_slot; m_piece_to_slot[found_piece] = current_slot;
m_slot_to_piece[current_slot] = found_piece; m_slot_to_piece[current_slot] = found_piece;
pieces[found_piece] = true; pieces[found_piece] = true;
} }
else else
{ {

View File

@ -168,6 +168,11 @@ namespace libtorrent
m_have_pieces.resize(torrent_file.num_pieces(), false); m_have_pieces.resize(torrent_file.num_pieces(), false);
} }
torrent::~torrent()
{
if (m_ses.m_abort) m_abort = true;
}
void torrent::tracker_response(const entry& e) void torrent::tracker_response(const entry& e)
{ {
std::vector<peer> peer_list; std::vector<peer> peer_list;
@ -279,15 +284,14 @@ namespace libtorrent
++i) ++i)
{ {
if (std::find(downloaders.begin(), downloaders.end(), (*i)->get_peer_id()) if (std::find(downloaders.begin(), downloaders.end(), (*i)->get_peer_id())
!= downloaders.end()) == downloaders.end()) continue;
(*i)->received_invalid_data();
if ((*i)->trust_points() <= -5)
{ {
(*i)->received_invalid_data(); // we don't trust this peer anymore
if ((*i)->trust_points() <= -5) // ban it.
{ m_policy->ban_peer(*(*i));
// we don't trust this peer anymore
// ban it.
m_policy->ban_peer(*(*i));
}
} }
} }
@ -422,11 +426,9 @@ namespace libtorrent
#endif #endif
} }
boost::weak_ptr<peer_connection> torrent::connect_to_peer(const address& a, const peer_id& id) peer_connection& torrent::connect_to_peer(const address& a, const peer_id& id)
{ {
boost::shared_ptr<socket> s(new socket(socket::tcp, false)); boost::shared_ptr<socket> s(new socket(socket::tcp, false));
// TODO: the send buffer size should be controllable from the outside
// s->set_send_bufsize(2048);
s->connect(a); s->connect(a);
boost::shared_ptr<peer_connection> c(new peer_connection( boost::shared_ptr<peer_connection> c(new peer_connection(
m_ses m_ses
@ -449,7 +451,7 @@ namespace libtorrent
m_ses.m_selector.monitor_readability(s); m_ses.m_selector.monitor_readability(s);
m_ses.m_selector.monitor_errors(s); m_ses.m_selector.monitor_errors(s);
// std::cout << "connecting to: " << a.as_string() << ":" << a.port() << "\n"; // std::cout << "connecting to: " << a.as_string() << ":" << a.port() << "\n";
return c; return *c;
} }
void torrent::attach_peer(peer_connection* p) void torrent::attach_peer(peer_connection* p)
@ -460,7 +462,7 @@ namespace libtorrent
= m_ses.m_connections.find(p->get_socket()); = m_ses.m_connections.find(p->get_socket());
assert(i != m_ses.m_connections.end()); assert(i != m_ses.m_connections.end());
if (!m_policy->new_connection(i->second)) throw network_error(0); if (!m_policy->new_connection(*i->second)) throw network_error(0);
} }
void torrent::close_all_connections() void torrent::close_all_connections()
@ -523,6 +525,14 @@ namespace libtorrent
#endif #endif
} }
#ifndef NDEBUG
void torrent::check_invariant()
{
assert(m_num_pieces
== std::count(m_have_pieces.begin(), m_have_pieces.end(), true));
}
#endif
void torrent::second_tick() void torrent::second_tick()
{ {
m_time_scaler++; m_time_scaler++;

View File

@ -63,6 +63,35 @@ namespace std
namespace libtorrent namespace libtorrent
{ {
void torrent_handle::set_max_uploads(int max_uploads)
{
if (m_ses == 0) throw invalid_handle();
assert(m_chk != 0);
{
boost::mutex::scoped_lock l(m_ses->m_mutex);
torrent* t = m_ses->find_torrent(m_info_hash);
if (t != 0)
{
t->get_policy().set_max_uploads(max_uploads);
return;
}
}
{
boost::mutex::scoped_lock l(m_chk->m_mutex);
detail::piece_checker_data* d = m_chk->find_torrent(m_info_hash);
if (d != 0)
{
d->torrent_ptr->get_policy().set_max_uploads(max_uploads);
return;
}
}
throw invalid_handle();
}
torrent_status torrent_handle::status() const torrent_status torrent_handle::status() const
{ {