diff --git a/Jamfile b/Jamfile index 1e5f98394..d3506ec90 100755 --- a/Jamfile +++ b/Jamfile @@ -14,9 +14,8 @@ lib winsock : : wsock32.lib ; project torrent : requirements - : usage-requirements - ./include + ./zlib $(BOOST_ROOT) release:NDEBUG BOOST_ALL_NO_LIB @@ -38,6 +37,13 @@ project torrent darwin:-Wno-unused-variable + : usage-requirements + + ./include + $(BOOST_ROOT) + release:NDEBUG + BOOST_ALL_NO_LIB + ; @@ -65,6 +71,21 @@ SOURCES = sha1.c ; +ZLIB_SOURCES = + adler32.c + compress.c + crc32.c + deflate.c + gzio.c + infback.c + inffast.c + inflate.c + inftrees.c + trees.c + uncompr.c + zutil.c + ; + # some windows specific settings @@ -81,8 +102,8 @@ SOURCES = lib torrent : - zlib//zlib src/$(SOURCES) + zlib/$(ZLIB_SOURCES) $(LIBS) /boost/thread//boost_thread /boost/filesystem//boost_filesystem/static diff --git a/docs/manual.html b/docs/manual.html index 30e6bfc35..42c5d4908 100755 --- a/docs/manual.html +++ b/docs/manual.html @@ -100,18 +100,18 @@ The current state includes the following features:

  • queues torrents for file check, instead of checking all of them in parallel.
  • uses separate threads for checking files and for main downloader, with a fool-proof 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
  • +
  • can limit the upload and download bandwidth usage and the maximum number of unchoked peers
  • +
  • piece-wise, unordered, file allocation
  • 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.
  • +party. (i.e. real tit for tat)
  • fast resume support, a way to get rid of the costly piece check at the start of a resumed torrent. Saves the storage state, piece_picker state as well as all local peers in a separate fast-resume file.
  • supports the extension protocol described by Nolar. See extensions.
  • supports files > 2 gigabytes (currently only on windows).
  • supports the no_peer_id=1 extension that will ease the load off trackers.
  • -
  • supports the udp-tracker protocol.
  • +
  • supports the udp-tracker protocol by Olaf van der Spek.
  • possibility to limit the number of connections.
  • delays have messages if there's no other outgoing traffic to the peer, and doesn't send have messages to peers that already has the piece. This saves bandwidth.
  • @@ -178,7 +178,7 @@ not support files larger than 2 Gigabytes.

    cygwin and msvc

    Note that if you're building on windows using the msvc toolset, you cannot run it -from a cygwin terminal, you'll have to run it from a cmd terminal. The same goes for +from a cygwin terminal, you'll have to run it from a cmd terminal. The same goes for cygwin, if you're building with gcc (mingw) you'll have to run it from a cygwin terminal. Also, make sure the paths are correct in the different environments. In cygwin, the paths (BOOST_BUILD_PATH and BOOST_ROOT) should be in the typical unix-format (e.g. @@ -281,6 +281,7 @@ class session: public boost::noncopyable void set_http_settings(const http_settings& settings); void set_upload_rate_limit(int bytes_per_second); + void set_download_rate_limit(int bytes_per_second); bool is_listening() const; unsigned short listen_port() const; @@ -298,7 +299,7 @@ class session: public boost::noncopyable The main thread will be idle as long it doesn't have any torrents to participate in. You add torrents through the add_torrent()-function where you give an object representing the information found in the torrent file and the path where you -want to save the files. The save_path will be prepended to the directory- +want to save the files. The save_path will be prepended to the directory structure in the torrent-file. add_torrent will throw duplicate_torrent exception if the torrent already exists in the session.

    The optional last parameter, resume_data can be given if up to date fast-resume data @@ -316,7 +317,9 @@ the peer-id to identify the client and the client's version. For more details se fingerprint class.

    set_upload_rate_limit() set the maximum number of bytes allowed to be sent to peers per second. This bandwidth is distributed among all the peers. If -you don't want to limit upload rate, you can set this to -1 (the default).

    +you don't want to limit upload rate, you can set this to -1 (the default). +set_download_rate_limit() works the same way but for download rate instead +of upload rate.

    is_listening() will tell you wether or not the session has successfully opened a listening port. If it hasn't, this function will return false, and then you can use listen_on() to make another try.

    @@ -400,10 +403,10 @@ class entry { public: - typedef std::map<std::string, entry> dictionary_type; + typedef std::list<std::pair<std::string, entry> > dictionary_type; typedef std::string string_type; - typedef std::vector<entry> list_type; - typedef implementation-defined integer_type; + typedef std::list<entry> list_type; + typedef size_type integer_type; enum data_type { @@ -424,7 +427,6 @@ public: entry(); entry(data_type t); entry(const entry& e); - ~entry(); void operator=(const entry& e); @@ -433,7 +435,7 @@ public: void operator=(const list_type&); void operator=(const integer_type&); - integer_type& integer() + integer_type& integer(); const integer_type& integer() const; string_type& string(); const string_type& string() const; @@ -442,6 +444,15 @@ public: dictionary_type& dict(); const dictionary_type& dict() const; + // these functions requires that the entry + // is a dictionary, otherwise they will throw + entry& operator[](char const* key); + entry& operator[](std::string const& key); + const entry& operator[](char const* key) const; + const entry& operator[](std::string const& key) const; + entry* find_key(char const* key); + entry const* find_key(char const* key) const; + void print(std::ostream& os, int indent = 0) const; }; @@ -587,11 +598,13 @@ struct torrent_handle void force_reannounce(); void connect_peer(const address& adr) const; - void set_ratio(float ratio); void set_tracker_login(std::string const& username, std::string const& password); + + void set_ratio(float ratio); void set_max_uploads(int max_uploads); void set_max_connections(int max_connections); void set_upload_limit(int limit); + void set_download_limit(int limit); void use_interface(const char* net_interface); void pause(); @@ -628,7 +641,11 @@ attempt to upload in return for each download. e.g. if set to 2, the client will 2 bytes for every byte received. The default setting for this is 0, which will make it work as a standard client.

    set_upload_limit will limit the upload bandwidth used by this particular torrent to the -limit you set. It is given as the number of bytes per second the torrent is allowed to upload.

    +limit you set. It is given as the number of bytes per second the torrent is allowed to upload. +set_download_limit works the same way but for download bandwidth instead of upload bandwidth. +Note that setting i higher limit on a torrent then the global limit (session::set_upload_rate_limit) +will not override the global rate limit. The torrent can never upload more than the global rate +limit.

    pause(), and resume() will disconnect all peers and reconnect all peers respectively. When a torrent is paused, it will however remember all share ratios to all peers and remember all potential (not connected) peers. You can use is_paused() to determine if a torrent @@ -1235,7 +1252,7 @@ to the torrent that this peer was a member of.

     struct peer_ban_alert: alert
     {
    -        peer_error_alert(
    +        peer_ban_alert(
                     address const& pip
                     , torrent_handle h
                     , const std::string& msg);
    @@ -1255,10 +1272,15 @@ is generated as severity level debug
     struct peer_error_alert: alert
     {
    -        peer_error_alert(const address& pid, const std::string& msg);
    +        peer_error_alert(
    +                address const& pip
    +                , peer_id const& pid
    +                , const std::string& msg);
    +
             virtual std::auto_ptr<alert> clone() const;
     
             address ip;
    +        peer_id id;
     };
     
    @@ -1272,16 +1294,18 @@ is a handle to the torrent the peer is a member of.

    The peer_request contains the values the client sent in its request message. piece is diff --git a/docs/manual.rst b/docs/manual.rst index fb7f55603..09950437b 100755 --- a/docs/manual.rst +++ b/docs/manual.rst @@ -29,18 +29,18 @@ The current state includes the following features: * queues torrents for file check, instead of checking all of them in parallel. * uses separate threads for checking files and for main downloader, with a fool-proof 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 + * can limit the upload and download bandwidth usage and the maximum number of unchoked peers + * piece-wise, unordered, file allocation * 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. + party. (i.e. real tit for tat) * fast resume support, a way to get rid of the costly piece check at the start of a resumed torrent. Saves the storage state, piece_picker state as well as all local peers in a separate fast-resume file. * supports the extension protocol `described by Nolar`__. See extensions_. * supports files > 2 gigabytes (currently only on windows). * supports the ``no_peer_id=1`` extension that will ease the load off trackers. - * supports the `udp-tracker protocol`__. + * supports the `udp-tracker protocol`__ by Olaf van der Spek. * possibility to limit the number of connections. * delays have messages if there's no other outgoing traffic to the peer, and doesn't send have messages to peers that already has the piece. This saves bandwidth. @@ -129,7 +129,7 @@ cygwin and msvc --------------- Note that if you're building on windows using the ``msvc`` toolset, you cannot run it -from a cygwin terminal, you'll have to run it from a cmd terminal. The same goes for +from a cygwin terminal, you'll have to run it from a ``cmd`` terminal. The same goes for cygwin, if you're building with gcc (mingw) you'll have to run it from a cygwin terminal. Also, make sure the paths are correct in the different environments. In cygwin, the paths (``BOOST_BUILD_PATH`` and ``BOOST_ROOT``) should be in the typical unix-format (e.g. @@ -225,6 +225,7 @@ The ``session`` class has the following synopsis:: void set_http_settings(const http_settings& settings); void set_upload_rate_limit(int bytes_per_second); + void set_download_rate_limit(int bytes_per_second); bool is_listening() const; unsigned short listen_port() const; @@ -242,7 +243,7 @@ Once it's created, it will spawn the main thread that will do all the work. The main thread will be idle as long it doesn't have any torrents to participate in. You add torrents through the ``add_torrent()``-function where you give an object representing the information found in the torrent file and the path where you -want to save the files. The ``save_path`` will be prepended to the directory- +want to save the files. The ``save_path`` will be prepended to the directory structure in the torrent-file. ``add_torrent`` will throw duplicate_torrent_ exception if the torrent already exists in the session. @@ -266,6 +267,8 @@ fingerprint class. ``set_upload_rate_limit()`` set the maximum number of bytes allowed to be sent to peers per second. This bandwidth is distributed among all the peers. If you don't want to limit upload rate, you can set this to -1 (the default). +``set_download_rate_limit()`` works the same way but for download rate instead +of upload rate. ``is_listening()`` will tell you wether or not the session has successfully opened a listening port. If it hasn't, this function will return false, and @@ -374,10 +377,10 @@ or a string. This is its synopsis:: { public: - typedef std::map dictionary_type; + typedef std::list > dictionary_type; typedef std::string string_type; - typedef std::vector list_type; - typedef implementation-defined integer_type; + typedef std::list list_type; + typedef size_type integer_type; enum data_type { @@ -398,7 +401,6 @@ or a string. This is its synopsis:: entry(); entry(data_type t); entry(const entry& e); - ~entry(); void operator=(const entry& e); @@ -407,7 +409,7 @@ or a string. This is its synopsis:: void operator=(const list_type&); void operator=(const integer_type&); - integer_type& integer() + integer_type& integer(); const integer_type& integer() const; string_type& string(); const string_type& string() const; @@ -416,6 +418,15 @@ or a string. This is its synopsis:: dictionary_type& dict(); const dictionary_type& dict() const; + // these functions requires that the entry + // is a dictionary, otherwise they will throw + entry& operator[](char const* key); + entry& operator[](std::string const& key); + const entry& operator[](char const* key) const; + const entry& operator[](std::string const& key) const; + entry* find_key(char const* key); + entry const* find_key(char const* key) const; + void print(std::ostream& os, int indent = 0) const; }; @@ -586,11 +597,13 @@ Its declaration looks like this:: void force_reannounce(); void connect_peer(const address& adr) const; - void set_ratio(float ratio); void set_tracker_login(std::string const& username, std::string const& password); + + void set_ratio(float ratio); void set_max_uploads(int max_uploads); void set_max_connections(int max_connections); void set_upload_limit(int limit); + void set_download_limit(int limit); void use_interface(const char* net_interface); void pause(); @@ -634,6 +647,10 @@ as a standard client. ``set_upload_limit`` will limit the upload bandwidth used by this particular torrent to the limit you set. It is given as the number of bytes per second the torrent is allowed to upload. +``set_download_limit`` works the same way but for download bandwidth instead of upload bandwidth. +Note that setting i higher limit on a torrent then the global limit (``session::set_upload_rate_limit``) +will not override the global rate limit. The torrent can never upload more than the global rate +limit. ``pause()``, and ``resume()`` will disconnect all peers and reconnect all peers respectively. When a torrent is paused, it will however remember all share ratios to all peers and remember @@ -1300,7 +1317,7 @@ to the torrent that this peer was a member of. struct peer_ban_alert: alert { - peer_error_alert( + peer_ban_alert( address const& pip , torrent_handle h , const std::string& msg); @@ -1323,10 +1340,15 @@ is generated as severity level ``debug``. struct peer_error_alert: alert { - peer_error_alert(const address& pid, const std::string& msg); + peer_error_alert( + address const& pip + , peer_id const& pid + , const std::string& msg); + virtual std::auto_ptr clone() const; address ip; + peer_id id; }; @@ -1343,16 +1365,18 @@ is a handle to the torrent the peer is a member of. `` struct invalid_request_alert: alert { invalid_request_alert( - const peer_request& r - , const torrent_handle& h - , const address& send - , const std::string& msg); + peer_request const& r + , torrent_handle const& h + , address const& send + , peer_id const& pid + , std::string const& msg); virtual std::auto_ptr clone() const; torrent_handle handle; address ip; peer_request request; + peer_id id; }; @@ -1361,7 +1385,7 @@ is a handle to the torrent the peer is a member of. `` int piece; int start; int length; - bool operator==(const peer_request& r); + bool operator==(peer_request const& r) const; }; diff --git a/examples/client_test.cpp b/examples/client_test.cpp index 211c3e021..d76285d03 100755 --- a/examples/client_test.cpp +++ b/examples/client_test.cpp @@ -245,7 +245,7 @@ int main(int argc, char* argv[]) std::make_pair(6881, 6889) , fingerprint("LT", 0, 1, 0, 0)); - ses.set_upload_rate_limit(20 * 1024); + ses.set_upload_rate_limit(100000); ses.set_http_settings(settings); ses.set_severity_level(alert::debug); @@ -345,16 +345,29 @@ int main(int argc, char* argv[]) a = ses.pop_alert(); while (a.get()) { - torrent_finished_alert* p = dynamic_cast(a.get()); - if (p) + if (torrent_finished_alert* p = dynamic_cast(a.get())) { // limit the bandwidth for all seeding torrents p->handle.set_max_connections(10); p->handle.set_max_uploads(5); - p->handle.set_upload_limit(30000); + p->handle.set_upload_limit(10000); + events.push_back( + p->handle.get_torrent_info().name() + ": " + a->msg()); } + else if (peer_error_alert* p = dynamic_cast(a.get())) + { + events.push_back(identify_client(p->id) + ": " + a->msg()); + } + else if (invalid_request_alert* p = dynamic_cast(a.get())) + { + events.push_back(identify_client(p->id) + ": " + a->msg()); + } + else + { + events.push_back(a->msg()); + } + if (events.size() >= 10) events.pop_front(); - events.push_back(a->msg()); a = ses.pop_alert(); } diff --git a/include/libtorrent/alert_types.hpp b/include/libtorrent/alert_types.hpp index 5c6d87408..ce22f8056 100755 --- a/include/libtorrent/alert_types.hpp +++ b/include/libtorrent/alert_types.hpp @@ -90,15 +90,17 @@ namespace libtorrent struct peer_error_alert: alert { - peer_error_alert(const address& pip, const std::string& msg) + peer_error_alert(address const& pip, peer_id const& pid, const std::string& msg) : alert(alert::debug, msg) , ip(pip) + , id(pid) {} virtual std::auto_ptr clone() const { return std::auto_ptr(new peer_error_alert(*this)); } address ip; + peer_id id; }; struct chat_message_alert: alert @@ -122,14 +124,16 @@ namespace libtorrent struct invalid_request_alert: alert { invalid_request_alert( - const peer_request& r - , const torrent_handle& h - , const address& sender - , const std::string& msg) + peer_request const& r + , torrent_handle const& h + , address const& sender + , peer_id const& pid + , std::string const& msg) : alert(alert::debug, msg) , handle(h) , ip(sender) , request(r) + , id(pid) {} virtual std::auto_ptr clone() const @@ -138,6 +142,7 @@ namespace libtorrent torrent_handle handle; address ip; peer_request request; + peer_id id; }; struct torrent_finished_alert: alert diff --git a/include/libtorrent/entry.hpp b/include/libtorrent/entry.hpp index 4fd2fa957..4e9419192 100755 --- a/include/libtorrent/entry.hpp +++ b/include/libtorrent/entry.hpp @@ -107,10 +107,10 @@ namespace libtorrent { public: - // we need a vector here to maintain the order - // of elements. Since the info-hash is reconstructed - // from an entry, it's important that the order is - // preserved. + // we need a list here instead of a map, to maintain + // the order of elements. Since the info-hash is + // reconstructed from an entry, it's important that + // the order is preserved. typedef std::list > dictionary_type; typedef std::string string_type; typedef std::list list_type; @@ -125,76 +125,32 @@ namespace libtorrent undefined_t }; - data_type type() const { return m_type; } + data_type type() const; entry(const dictionary_type&); entry(const string_type&); entry(const list_type&); entry(const integer_type&); - entry(): m_type(undefined_t) {} - entry(data_type t): m_type(t) { construct(t); } - entry(const entry& e) { copy(e); } - ~entry() { destruct(); } - - void operator=(const entry& e) - { - destruct(); - copy(e); - } + entry(); + entry(data_type t); + entry(const entry& e); + ~entry(); + void operator=(const entry& e); void operator=(const dictionary_type&); void operator=(const string_type&); void operator=(const list_type&); void operator=(const integer_type&); - integer_type& integer() - { - if (m_type != int_t) throw type_error("invalid type requested from entry"); - return *reinterpret_cast(data); - } - - const integer_type& integer() const - { - if (m_type != int_t) throw type_error("invalid type requested from entry"); - return *reinterpret_cast(data); - } - - string_type& string() - { - if (m_type != string_t) throw type_error("invalid type requested from entry"); - return *reinterpret_cast(data); - } - - const string_type& string() const - { - if (m_type != string_t) throw type_error("invalid type requested from entry"); - return *reinterpret_cast(data); - } - - list_type& list() - { - if (m_type != list_t) throw type_error("invalid type requested from entry"); - return *reinterpret_cast(data); - } - - const list_type& list() const - { - if (m_type != list_t) throw type_error("invalid type requested from entry"); - return *reinterpret_cast(data); - } - - dictionary_type& dict() - { - if (m_type != dictionary_t) throw type_error("invalid type requested from entry"); - return *reinterpret_cast(data); - } - - const dictionary_type& dict() const - { - if (m_type != dictionary_t) throw type_error("invalid type requested from entry"); - return *reinterpret_cast(data); - } + integer_type& integer(); + const integer_type& integer() const; + string_type& string(); + const string_type& string() const; + list_type& list(); + const list_type& list() const; + dictionary_type& dict(); + const dictionary_type& dict() const; // these functions requires that the entry // is a dictionary, otherwise they will throw @@ -246,6 +202,67 @@ namespace libtorrent return os; } + inline entry::data_type entry::type() const { return m_type; } + + inline entry::entry(): m_type(undefined_t) {} + inline entry::entry(data_type t): m_type(t) { construct(t); } + inline entry::entry(const entry& e) { copy(e); } + inline entry::~entry() { destruct(); } + + inline void entry::operator=(const entry& e) + { + destruct(); + copy(e); + } + + inline entry::integer_type& entry::integer() + { + if (m_type != int_t) throw type_error("invalid type requested from entry"); + return *reinterpret_cast(data); + } + + inline entry::integer_type const& entry::integer() const + { + if (m_type != int_t) throw type_error("invalid type requested from entry"); + return *reinterpret_cast(data); + } + + inline entry::string_type& entry::string() + { + if (m_type != string_t) throw type_error("invalid type requested from entry"); + return *reinterpret_cast(data); + } + + inline entry::string_type const& entry::string() const + { + if (m_type != string_t) throw type_error("invalid type requested from entry"); + return *reinterpret_cast(data); + } + + inline entry::list_type& entry::list() + { + if (m_type != list_t) throw type_error("invalid type requested from entry"); + return *reinterpret_cast(data); + } + + inline entry::list_type const& entry::list() const + { + if (m_type != list_t) throw type_error("invalid type requested from entry"); + return *reinterpret_cast(data); + } + + inline entry::dictionary_type& entry::dict() + { + if (m_type != dictionary_t) throw type_error("invalid type requested from entry"); + return *reinterpret_cast(data); + } + + inline entry::dictionary_type const& entry::dict() const + { + if (m_type != dictionary_t) throw type_error("invalid type requested from entry"); + return *reinterpret_cast(data); + } + } #endif // TORRENT_ENTRY_HPP_INCLUDED diff --git a/include/libtorrent/peer_connection.hpp b/include/libtorrent/peer_connection.hpp index 940195bf7..a844f071c 100755 --- a/include/libtorrent/peer_connection.hpp +++ b/include/libtorrent/peer_connection.hpp @@ -117,7 +117,9 @@ namespace libtorrent void receive_data(); // tells if this connection has data it want to send - bool has_data() const; + // and has enough upload bandwidth quota left to send it. + bool can_write() const; + bool can_read() const; bool is_seed() const; @@ -171,13 +173,6 @@ namespace libtorrent void disconnect(); bool is_disconnecting() const { return m_disconnecting; } - // returns the send quota this peer has - // left until will stop sending. - // if the send_quota is -1, it means the - // quota is unlimited. - int send_quota_left() const; - resource_request* upload_bandwidth_quota(); - // This is called for every peer right after the upload // bandwidth has been distributed among them // It will reset the used bandwidth to 0 and @@ -248,7 +243,9 @@ namespace libtorrent // how much bandwidth we're using, how much we want, // and how much we are allowed to use. - resource_request m_upload_bandwidth_quota; + resource_request m_ul_bandwidth_quota; + resource_request m_dl_bandwidth_quota; + resource_request m_unchoked_quota; private: @@ -340,8 +337,8 @@ namespace libtorrent boost::shared_ptr m_socket; // upload bandwidth used this second. - // Must not exceed m_upload_bandwidth_quota.given. -// int m_upload_bandwidth_quota_used; + // Must not exceed m_ul_bandwidth_quota.given. +// int m_ul_bandwidth_quota_used; // this is the torrent this connection is // associated with. If the connection is an @@ -372,7 +369,8 @@ namespace libtorrent // sent to this peer, we check this and // if it's not added to the selector we // add it. (this is done in send_buffer_updated()) - bool m_added_to_selector; + bool m_writability_monitored; + bool m_readability_monitored; // remote peer's id peer_id m_peer_id; diff --git a/include/libtorrent/peer_request.hpp b/include/libtorrent/peer_request.hpp index 2af894f62..6d12548c2 100644 --- a/include/libtorrent/peer_request.hpp +++ b/include/libtorrent/peer_request.hpp @@ -40,7 +40,7 @@ namespace libtorrent int piece; int start; int length; - bool operator==(const peer_request& r) + bool operator==(peer_request const& r) const { return piece == r.piece && start == r.start && length == r.length; } }; } diff --git a/include/libtorrent/resource_request.hpp b/include/libtorrent/resource_request.hpp index 5823441b5..f831aa4db 100755 --- a/include/libtorrent/resource_request.hpp +++ b/include/libtorrent/resource_request.hpp @@ -44,6 +44,13 @@ namespace libtorrent , given(0) {} + int left() const + { + assert(given >= used); + return given - used; + } + + // I'm right now actively using: int used; diff --git a/include/libtorrent/session.hpp b/include/libtorrent/session.hpp index d3f827109..23504f578 100755 --- a/include/libtorrent/session.hpp +++ b/include/libtorrent/session.hpp @@ -225,6 +225,7 @@ namespace libtorrent // bytes per second. -1 means // unlimited int m_upload_rate; + int m_download_rate; // handles delayed alerts alert_manager m_alerts; @@ -292,6 +293,7 @@ namespace libtorrent void set_http_settings(const http_settings& s); void set_upload_rate_limit(int bytes_per_second); + void set_download_rate_limit(int bytes_per_second); // TODO: add a session_status that contain // some indication of if the listen-port works diff --git a/include/libtorrent/socket.hpp b/include/libtorrent/socket.hpp index 9632e5623..bb4ca92c1 100755 --- a/include/libtorrent/socket.hpp +++ b/include/libtorrent/socket.hpp @@ -240,12 +240,21 @@ namespace libtorrent void remove_writable(boost::shared_ptr s) { m_writable.erase(std::find(m_writable.begin(), m_writable.end(), s)); } + void remove_readable(boost::shared_ptr s) + { m_readable.erase(std::find(m_readable.begin(), m_readable.end(), s)); } + bool is_writability_monitored(boost::shared_ptr s) { return std::find(m_writable.begin(), m_writable.end(), s) != m_writable.end(); } + bool is_readability_monitored(boost::shared_ptr s) + { + return std::find(m_readable.begin(), m_readable.end(), s) + != m_readable.end(); + } + void wait(int timeout , std::vector >& readable , std::vector >& writable diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index e8983663c..af82dd9bd 100755 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -92,27 +92,20 @@ namespace libtorrent ~torrent(); + // this will flag the torrent as aborted. The main + // loop in session_impl will check for this state + // on all torrents once every second, and take + // the necessary actions then. void abort() { m_abort = true; m_event = tracker_request::stopped; } bool is_aborted() const { return m_abort; } - // is called every second by session. + // is called every second by session. This will + // caclulate the upload/download and number + // of connections this torrent needs. And prepare + // it for being used by allocate_resources. void second_tick(); - // returns true if it time for this torrent to make another - // tracker request - bool should_request() const - { - namespace time = boost::posix_time; - return !m_paused && - m_next_request < time::second_clock::local_time(); - } - - void force_tracker_request() - { - namespace time = boost::posix_time; - m_next_request = time::second_clock::local_time(); - } - + // debug purpose only void print(std::ostream& os) const; void check_files( @@ -132,13 +125,6 @@ namespace libtorrent void use_interface(const char* net_interface); peer_connection& connect_to_peer(const address& a); - const torrent_info& torrent_file() const - { return m_torrent_file; } - - policy& get_policy() { return *m_policy; } - - piece_manager& filesystem() { return m_storage; } - void set_ratio(float ratio) { assert(ratio >= 0.0f); m_ratio = ratio; } @@ -169,10 +155,6 @@ namespace libtorrent // the number of peers that belong to this torrent int num_peers() const { return (int)m_connections.size(); } - // returns true if this torrent has a connection - // to a peer with the given peer_id -// bool has_peer(const peer_id& id) const; - typedef std::map::iterator peer_iterator; typedef std::map::const_iterator const_peer_iterator; @@ -186,8 +168,10 @@ namespace libtorrent // -------------------------------------------- // TRACKER MANAGEMENT - // this is a callback called by the tracker_connection class + // these are callbacks called by the tracker_connection instance + // (either http_tracker_connection or udp_tracker_connection) // when this torrent got a response from its tracker request + // or when a failure occured virtual void tracker_response(std::vector& e, int interval); virtual void tracker_request_timed_out(); virtual void tracker_request_error(int response_code, const std::string& str); @@ -195,10 +179,32 @@ namespace libtorrent // generates a request string for sending // to the tracker tracker_request generate_tracker_request(); - std::string tracker_password() const; - boost::posix_time::ptime next_announce() const - { return m_next_request; } + // if no password and username is set + // this will return an empty string, otherwise + // it will concatenate the login and password + // ready to be sent over http (but without + // base64 encoding). + std::string tracker_login() const; + + // returns the absolute time when the next tracker + // announce will take place. + boost::posix_time::ptime next_announce() const; + + // returns true if it is time for this torrent to make another + // tracker request + bool should_request() const; + + // forcefully sets next_announce to the current time + void force_tracker_request(); + + // sets the username and password that will be sent to + // the tracker + void set_tracker_login(std::string const& name, std::string const& pw); + + // the address of the tracker that we managed to + // announce ourself at the last time we tried to announce + const address& current_tracker() const; // -------------------------------------------- // PIECE MANAGEMENT @@ -242,9 +248,6 @@ namespace libtorrent // all seeds and let the tracker know we're finished. void completed(); - piece_picker& picker() { return m_picker; } - - bool verify_piece(int piece_index); // this is called from the peer_connection @@ -266,18 +269,14 @@ namespace libtorrent boost::filesystem::path save_path() const { return m_storage.save_path(); } - alert_manager& alerts() const; + piece_picker& picker() { return m_picker; } + policy& get_policy() { return *m_policy; } + piece_manager& filesystem() { return m_storage; } + torrent_info const& torrent_file() const { return m_torrent_file; } + torrent_handle get_handle() const; - void set_tracker_login(std::string const& name, std::string const& pw) - { - m_username = name; - m_password = pw; - } - - const address& current_tracker() const; - // DEBUG #ifndef NDEBUG logger* spawn_logger(const char* title); @@ -286,18 +285,20 @@ namespace libtorrent void check_invariant(); #endif +// -------------------------------------------- + // RESOURCE MANAGEMENT + // this will distribute the given upload/download - // quotas among the peers + // quotas and number of connections, among the peers void distribute_resources(); - resource_request m_upload_bandwidth_quota; + resource_request m_ul_bandwidth_quota; + resource_request m_dl_bandwidth_quota; + resource_request m_unchoked_quota; + resource_request m_connections_quota; - void set_upload_limit(int limit) - { - assert(limit >= -1); - if (limit == -1) limit = std::numeric_limits::max(); - m_upload_bandwidth_limit = limit; - } + void set_upload_limit(int limit); + void set_download_limit(int limit); private: @@ -391,8 +392,51 @@ namespace libtorrent // the max number of bytes this torrent // can upload per second int m_upload_bandwidth_limit; + int m_download_bandwidth_limit; }; + inline boost::posix_time::ptime torrent::next_announce() const + { return m_next_request; } + + // returns true if it is time for this torrent to make another + // tracker request + inline bool torrent::should_request() const + { + namespace time = boost::posix_time; + return !m_paused && + m_next_request < time::second_clock::local_time(); + } + + inline void torrent::force_tracker_request() + { + namespace time = boost::posix_time; + m_next_request = time::second_clock::local_time(); + } + + inline void torrent::set_tracker_login( + std::string const& name + , std::string const& pw) + { + m_username = name; + m_password = pw; + } + + inline void torrent::set_upload_limit(int limit) + { + assert(limit >= -1); + if (limit == -1) limit = std::numeric_limits::max(); + if (limit < num_peers() * 10) limit = num_peers() * 10; + m_upload_bandwidth_limit = limit; + } + + inline void torrent::set_download_limit(int limit) + { + assert(limit >= -1); + if (limit == -1) limit = std::numeric_limits::max(); + if (limit < num_peers() * 10) limit = num_peers() * 10; + m_download_bandwidth_limit = limit; + } + } #endif // TORRENT_TORRENT_HPP_INCLUDED diff --git a/src/http_tracker_connection.cpp b/src/http_tracker_connection.cpp index b554a98bc..e79435d69 100755 --- a/src/http_tracker_connection.cpp +++ b/src/http_tracker_connection.cpp @@ -380,14 +380,6 @@ namespace libtorrent } return true; } - #ifndef NDEBUG - if (requester()) - { - requester()->debug_log( - std::string("m_content_length = ") - + boost::lexical_cast(m_content_length)); - } - #endif } else if (line.substr(0, 18) == "Content-Encoding: ") { diff --git a/src/peer_connection.cpp b/src/peer_connection.cpp index 23793d0ce..1e3d2e4c5 100755 --- a/src/peer_connection.cpp +++ b/src/peer_connection.cpp @@ -92,7 +92,8 @@ namespace libtorrent , m_attached_to_torrent(true) , m_ses(ses) , m_active(true) - , m_added_to_selector(false) + , m_writability_monitored(false) + , m_readability_monitored(true) , m_peer_interested(false) , m_peer_choked(true) , m_interesting(false) @@ -110,8 +111,11 @@ namespace libtorrent { INVARIANT_CHECK; - m_upload_bandwidth_quota.min = 10; - m_upload_bandwidth_quota.max = 10; + m_ul_bandwidth_quota.min = 10; + m_ul_bandwidth_quota.max = 10; + m_dl_bandwidth_quota.min = 10; + m_dl_bandwidth_quota.max = std::numeric_limits::max(); + m_dl_bandwidth_quota.given = 400; assert(!m_socket->is_blocking()); assert(m_torrent != 0); @@ -156,7 +160,8 @@ namespace libtorrent , m_attached_to_torrent(0) , m_ses(ses) , m_active(false) - , m_added_to_selector(false) + , m_writability_monitored(false) + , m_readability_monitored(true) , m_peer_id() , m_peer_interested(false) , m_peer_choked(true) @@ -172,7 +177,6 @@ namespace libtorrent , m_disconnecting(false) , m_became_uninterested(boost::posix_time::second_clock::local_time()) , m_became_uninteresting(boost::posix_time::second_clock::local_time()) -// , m_upload_bandwidth_quota_used(0) { INVARIANT_CHECK; @@ -180,9 +184,13 @@ namespace libtorrent // that are part of a torrent. Since this is an incoming // connection, we have to give it some initial bandwidth // to send the handshake - m_upload_bandwidth_quota.min = 10; - m_upload_bandwidth_quota.max = 400; - m_upload_bandwidth_quota.given = 400; + m_ul_bandwidth_quota.min = 10; + m_ul_bandwidth_quota.max = 400; + m_ul_bandwidth_quota.given = 400; + + m_dl_bandwidth_quota.min = 10; + m_dl_bandwidth_quota.max = std::numeric_limits::max(); + m_dl_bandwidth_quota.given = 400; assert(!m_socket->is_blocking()); @@ -280,22 +288,19 @@ namespace libtorrent m_free_upload += free_upload; } - int peer_connection::send_quota_left() const - { - return m_upload_bandwidth_quota.given - m_upload_bandwidth_quota.used; - } - void peer_connection::reset_upload_quota() { - m_upload_bandwidth_quota.used = 0; + m_ul_bandwidth_quota.used = 0; + m_dl_bandwidth_quota.used = 0; + if (!m_readability_monitored) + { + assert(!m_selector.is_readability_monitored(m_socket)); + m_selector.monitor_readability(m_socket); + m_readability_monitored = true; + } send_buffer_updated(); } - resource_request* peer_connection::upload_bandwidth_quota() - { - return &m_upload_bandwidth_quota; - } - void peer_connection::send_handshake() { INVARIANT_CHECK; @@ -687,6 +692,7 @@ namespace libtorrent r , m_torrent->get_handle() , m_socket->sender() + , m_peer_id , "peer sent an illegal request, ignoring")); } } @@ -797,8 +803,9 @@ namespace libtorrent { m_torrent->alerts().post_alert( peer_error_alert( - m_socket->sender() - , "got a block that was not requested")); + m_socket->sender() + , m_peer_id + , "got a block that was not requested")); } #ifndef NDEBUG (*m_logger) << " *** The block we just got was not requested ***\n"; @@ -864,9 +871,9 @@ namespace libtorrent m_requests.erase(i); } - if (!has_data() && m_added_to_selector) + if (!can_write() && m_writability_monitored) { - m_added_to_selector = false; + m_writability_monitored = false; m_selector.remove_writable(m_socket); } @@ -1312,7 +1319,9 @@ namespace libtorrent INVARIANT_CHECK; m_statistics.second_tick(); - m_upload_bandwidth_quota.used = (int)ceil(statistics().upload_rate()); + m_ul_bandwidth_quota.used = std::min( + (int)ceil(statistics().upload_rate()) + , m_ul_bandwidth_quota.given); send_buffer_updated(); @@ -1329,9 +1338,9 @@ namespace libtorrent // than we have uploaded OR if we are a seed // have an unlimited upload rate if(!m_send_buffer.empty() || (!m_requests.empty() && !is_choked())) - m_upload_bandwidth_quota.max = std::numeric_limits::max(); + m_ul_bandwidth_quota.max = std::numeric_limits::max(); else - m_upload_bandwidth_quota.max = m_upload_bandwidth_quota.min; + m_ul_bandwidth_quota.max = m_ul_bandwidth_quota.min; } else { @@ -1355,8 +1364,8 @@ namespace libtorrent upload_speed_limit = std::min(upload_speed_limit, (double)std::numeric_limits::max()); - m_upload_bandwidth_quota.max - = std::max((int)upload_speed_limit, m_upload_bandwidth_quota.min); + m_ul_bandwidth_quota.max + = std::max((int)upload_speed_limit, m_ul_bandwidth_quota.min); } /* @@ -1371,7 +1380,7 @@ namespace libtorrent // if we have downloaded more than one piece more // than we have uploaded OR if we are a seed // have an unlimited upload rate - m_upload_bandwidth_quota.wanted = std::numeric_limits::max(); + m_ul_bandwidth_quota.wanted = std::numeric_limits::max(); } else { @@ -1390,10 +1399,10 @@ namespace libtorrent { bias = -static_cast(m_statistics.download_rate() * ratio) / 2; } - m_upload_bandwidth_quota.wanted = static_cast(m_statistics.download_rate()) + bias; + m_ul_bandwidth_quota.wanted = static_cast(m_statistics.download_rate()) + bias; // the maximum send_quota given our download rate from this peer - if (m_upload_bandwidth_quota.wanted < 256) m_upload_bandwidth_quota.wanted = 256; + if (m_ul_bandwidth_quota.wanted < 256) m_ul_bandwidth_quota.wanted = 256; } */ } @@ -1410,10 +1419,16 @@ namespace libtorrent assert(!m_socket->is_blocking()); assert(m_packet_size > 0); assert(m_socket->is_readable()); + assert(can_read()); + assert(m_selector.is_readability_monitored(m_socket)); + for(;;) { assert(m_packet_size > 0); - int received = m_socket->receive(&m_recv_buffer[m_recv_pos], m_packet_size - m_recv_pos); + int max_receive = std::min( + m_dl_bandwidth_quota.left() + , m_packet_size - m_recv_pos); + int received = m_socket->receive(&m_recv_buffer[m_recv_pos], max_receive); // connection closed if (received == 0) @@ -1438,6 +1453,14 @@ namespace libtorrent m_last_receive = boost::posix_time::second_clock::local_time(); m_recv_pos += received; + m_dl_bandwidth_quota.used += received; + if (!can_read()) + { + assert(m_readability_monitored); + assert(m_selector.is_readability_monitored(m_socket)); + m_selector.remove_readable(m_socket); + m_readability_monitored = false; + } switch(m_state) { @@ -1675,19 +1698,28 @@ namespace libtorrent } break; } + + // if we have used all our download quota, + // break the receive loop + if (!can_read()) break; } } assert(m_packet_size > 0); } - bool peer_connection::has_data() const + bool peer_connection::can_write() const { // if we have requests or pending data to be sent or announcements to be made // we want to send data return ((!m_requests.empty() && !m_choked) || !m_send_buffer.empty()) - && send_quota_left() > 0; + && m_ul_bandwidth_quota.left() > 0; + } + + bool peer_connection::can_read() const + { + return m_dl_bandwidth_quota.left() > 0; } // -------------------------- @@ -1700,7 +1732,7 @@ namespace libtorrent INVARIANT_CHECK; assert(m_socket->is_writable()); - assert(has_data()); + assert(can_write()); // only add new piece-chunks if the send buffer is small enough // otherwise there will be no end to how large it will be! @@ -1766,15 +1798,15 @@ namespace libtorrent m_announce_queue.clear(); } - assert(m_upload_bandwidth_quota.used <= m_upload_bandwidth_quota.given); + assert(m_ul_bandwidth_quota.used <= m_ul_bandwidth_quota.given); // send the actual buffer if (!m_send_buffer.empty()) { + int amount_to_send + = std::min(m_ul_bandwidth_quota.left(), (int)m_send_buffer.size()); - int amount_to_send = (int)m_send_buffer.size(); - amount_to_send = std::min(send_quota_left(), amount_to_send); - assert(amount_to_send>0); + assert(amount_to_send > 0); // we have data that's scheduled for sending int sent = m_socket->send( @@ -1783,7 +1815,7 @@ namespace libtorrent if (sent > 0) { - m_upload_bandwidth_quota.used += sent; + m_ul_bandwidth_quota.used += sent; // manage the payload markers int amount_payload = 0; @@ -1840,14 +1872,14 @@ namespace libtorrent m_last_sent = boost::posix_time::second_clock::local_time(); } - assert(m_added_to_selector); + assert(m_writability_monitored); send_buffer_updated(); } #ifndef NDEBUG void peer_connection::check_invariant() const { - assert(has_data() == m_selector.is_writability_monitored(m_socket)); + assert(can_write() == m_selector.is_writability_monitored(m_socket)); /* assert(m_num_pieces == std::count( m_have_piece.begin() @@ -1923,25 +1955,25 @@ namespace libtorrent void peer_connection::send_buffer_updated() { - if (!has_data()) + if (!can_write()) { - if (m_added_to_selector) + if (m_writability_monitored) { m_selector.remove_writable(m_socket); - m_added_to_selector = false; + m_writability_monitored = false; } assert(!m_selector.is_writability_monitored(m_socket)); return; } - assert(send_quota_left() > 0); - assert(has_data()); - if (!m_added_to_selector) + assert(m_ul_bandwidth_quota.left() > 0); + assert(can_write()); + if (!m_writability_monitored) { m_selector.monitor_writability(m_socket); - m_added_to_selector = true; + m_writability_monitored = true; } - assert(m_added_to_selector); + assert(m_writability_monitored); assert(m_selector.is_writability_monitored(m_socket)); } diff --git a/src/policy.cpp b/src/policy.cpp index fde900a14..042d8f769 100755 --- a/src/policy.cpp +++ b/src/policy.cpp @@ -748,6 +748,8 @@ namespace libtorrent // it's probably just a NAT-check. Ignore the // num connections constraint then. // TODO: mske sure this works + // TODO: only allow _one_ connection to use this + // override at a time if (m_torrent->num_peers() >= m_max_connections && c.get_socket()->sender().ip() != m_torrent->current_tracker().ip()) { @@ -853,7 +855,7 @@ namespace libtorrent if (m_torrent->alerts().should_post(alert::debug)) { m_torrent->alerts().post_alert( - peer_error_alert(remote, e.what())); + peer_error_alert(remote, id, e.what())); } } catch(protocol_error& e) @@ -861,7 +863,7 @@ namespace libtorrent if (m_torrent->alerts().should_post(alert::debug)) { m_torrent->alerts().post_alert( - peer_error_alert(remote, e.what())); + peer_error_alert(remote, id, e.what())); } } } diff --git a/src/session.cpp b/src/session.cpp index 313aa5c76..973bd3303 100755 --- a/src/session.cpp +++ b/src/session.cpp @@ -271,6 +271,7 @@ namespace libtorrent { namespace detail , m_listen_interface(listen_interface, listen_port_range.first) , m_abort(false) , m_upload_rate(-1) + , m_download_rate(-1) , m_incoming_connection(false) { assert(listen_port_range.first > 0); @@ -309,7 +310,6 @@ namespace libtorrent { namespace detail { m_connections.erase(m_disconnect_peer.back()); m_disconnect_peer.pop_back(); - assert(m_selector.count_read_monitors() == (int)m_connections.size() + (bool)m_listen_socket); } } @@ -420,8 +420,6 @@ namespace libtorrent { namespace detail #endif boost::mutex::scoped_lock l(m_mutex); - assert(m_selector.count_read_monitors() == (int)m_connections.size() + (bool)m_listen_socket); - if (m_abort) { m_tracker_manager.abort_all_requests(); @@ -467,7 +465,7 @@ namespace libtorrent { namespace detail try { assert(m_selector.is_writability_monitored(p->first)); - assert(p->second->has_data()); + assert(p->second->can_write()); assert(p->second->get_socket()->is_writable()); p->second->send_data(); } @@ -486,7 +484,6 @@ namespace libtorrent { namespace detail // pause the torrent t->pause(); - assert(m_selector.count_read_monitors() == (int)m_connections.size() + (bool)m_listen_socket); } catch (std::exception& e) { @@ -495,13 +492,15 @@ namespace libtorrent { namespace detail if (m_alerts.should_post(alert::debug)) { m_alerts.post_alert( - peer_error_alert(p->first->sender(), e.what())); + peer_error_alert( + p->first->sender() + , p->second->id() + , e.what())); } p->second->set_failed(); m_selector.remove(*i); m_connections.erase(p); - assert(m_selector.count_read_monitors() == (int)m_connections.size() + (bool)m_listen_socket); } } } @@ -573,7 +572,6 @@ namespace libtorrent { namespace detail , e.what())); } - assert(m_selector.count_read_monitors() == (int)m_connections.size() + (bool)m_listen_socket); t->pause(); } catch (std::exception& e) @@ -581,14 +579,16 @@ namespace libtorrent { namespace detail if (m_alerts.should_post(alert::debug)) { m_alerts.post_alert( - peer_error_alert(p->first->sender(), e.what())); + peer_error_alert( + p->first->sender() + , p->second->id() + , e.what())); } // the connection wants to disconnect for some reason, remove it // from the connection-list p->second->set_failed(); m_selector.remove(*i); m_connections.erase(p); - assert(m_selector.count_read_monitors() == (int)m_connections.size() + (bool)m_listen_socket); } } } @@ -613,7 +613,8 @@ namespace libtorrent { namespace detail m_alerts.post_alert( peer_error_alert( p->first->sender() - , "socket received an exception")); + , p->second->id() + , "connection closed")); } m_selector.remove(*i); @@ -622,7 +623,6 @@ namespace libtorrent { namespace detail { p->second->set_failed(); m_connections.erase(p); - assert(m_selector.count_read_monitors() == (int)m_connections.size() + (bool)m_listen_socket); } } @@ -659,12 +659,14 @@ namespace libtorrent { namespace detail if (m_alerts.should_post(alert::debug)) { m_alerts.post_alert( - peer_error_alert(j->first->sender(), "connection timed out")); + peer_error_alert( + j->first->sender() + , j->second->id() + , "connection timed out")); } j->second->set_failed(); m_selector.remove(j->first); m_connections.erase(j); - assert(m_selector.count_read_monitors() == (int)m_connections.size() + (bool)m_listen_socket); continue; } @@ -713,7 +715,13 @@ namespace libtorrent { namespace detail ? std::numeric_limits::max() : m_upload_rate , m_torrents - , &torrent::m_upload_bandwidth_quota); + , &torrent::m_ul_bandwidth_quota); + + allocate_resources(m_download_rate == -1 + ? std::numeric_limits::max() + : m_download_rate + , m_torrents + , &torrent::m_dl_bandwidth_quota); for (std::map >::iterator i = m_torrents.begin(); i != m_torrents.end(); ++i) @@ -795,15 +803,18 @@ namespace libtorrent { namespace detail i != m_connections.end(); ++i) { - if (i->second->has_data() != m_selector.is_writability_monitored(i->first)) + if (i->second->can_write() != m_selector.is_writability_monitored(i->first) + || i->second->can_read() != m_selector.is_readability_monitored(i->first)) { std::ofstream error_log("error.log", std::ios_base::app); boost::shared_ptr p = i->second; - error_log << "session_imple::check_invariant()\n" - "peer_connection::has_data() != is_writability_monitored()\n"; - error_log << "peer_connection::has_data() " << p->has_data() << "\n"; - error_log << "peer_connection::send_quota_left " << p->send_quota_left() << "\n"; - error_log << "peer_connection::upload_bandwidth_quota()->given " << p->upload_bandwidth_quota()->given << "\n"; + error_log << "selector::is_writability_monitored() " << m_selector.is_writability_monitored(i->first) << "\n"; + error_log << "selector::is_readability_monitored() " << m_selector.is_readability_monitored(i->first) << "\n"; + error_log << "peer_connection::can_write() " << p->can_write() << "\n"; + error_log << "peer_connection::can_read() " << p->can_read() << "\n"; + error_log << "peer_connection::ul_quota_left " << p->m_ul_bandwidth_quota.left() << "\n"; + error_log << "peer_connection::dl_quota_left " << p->m_dl_bandwidth_quota.left() << "\n"; + error_log << "peer_connection::m_ul_bandwidth_quota.given " << p->m_ul_bandwidth_quota.given << "\n"; error_log << "peer_connection::get_peer_id " << p->get_peer_id() << "\n"; error_log << "place: " << place << "\n"; error_log.flush(); @@ -1007,12 +1018,26 @@ namespace libtorrent for (detail::session_impl::connection_map::iterator i = m_impl.m_connections.begin(); - i != m_impl.m_connections.end();) + i != m_impl.m_connections.end(); ++i) { - i->second->upload_bandwidth_quota()->given = std::numeric_limits::max(); -// i->second->update_send_quota_left(); + i->second->m_ul_bandwidth_quota.given = std::numeric_limits::max(); } + } + void session::set_download_rate_limit(int bytes_per_second) + { + assert(bytes_per_second > 0 || bytes_per_second == -1); + boost::mutex::scoped_lock l(m_impl.m_mutex); + m_impl.m_download_rate = bytes_per_second; + if (m_impl.m_download_rate != -1 || !m_impl.m_connections.empty()) + return; + + for (detail::session_impl::connection_map::iterator i + = m_impl.m_connections.begin(); + i != m_impl.m_connections.end(); ++i) + { + i->second->m_dl_bandwidth_quota.given = std::numeric_limits::max(); + } } std::auto_ptr session::pop_alert() diff --git a/src/storage.cpp b/src/storage.cpp index f344f8e88..67e2aa083 100755 --- a/src/storage.cpp +++ b/src/storage.cpp @@ -1189,8 +1189,9 @@ namespace libtorrent } assert(file_offset > current_offset); - int skip_blocks = (file_offset - current_offset + m_info.piece_length() - 1) - / m_info.piece_length(); + int skip_blocks = static_cast( + (file_offset - current_offset + m_info.piece_length() - 1) + / m_info.piece_length()); for (int i = current_slot; i < current_slot + skip_blocks; ++i) { diff --git a/src/torrent.cpp b/src/torrent.cpp index 4e577a730..1b81c67da 100755 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -166,6 +166,7 @@ namespace libtorrent , m_ratio(0.f) , m_net_interface(net_interface.ip(), address::any_port) , m_upload_bandwidth_limit(std::numeric_limits::max()) + , m_download_bandwidth_limit(std::numeric_limits::max()) { assert(torrent_file.begin_files() != torrent_file.end_files()); m_have_pieces.resize(torrent_file.num_pieces(), false); @@ -404,7 +405,7 @@ namespace libtorrent i->second->announce_piece(index); } - std::string torrent::tracker_password() const + std::string torrent::tracker_login() const { if (m_username.empty() && m_password.empty()) return ""; return m_username + ":" + m_password; @@ -639,9 +640,13 @@ namespace libtorrent m_policy->pulse(); } - m_upload_bandwidth_quota.used = 0; - m_upload_bandwidth_quota.max = 0; - m_upload_bandwidth_quota.min = 0; + m_ul_bandwidth_quota.used = 0; + m_ul_bandwidth_quota.max = 0; + m_ul_bandwidth_quota.min = 0; + + m_dl_bandwidth_quota.used = 0; + m_dl_bandwidth_quota.min = 0; + m_dl_bandwidth_quota.max = 0; for (peer_iterator i = m_connections.begin(); i != m_connections.end(); @@ -649,26 +654,49 @@ namespace libtorrent { peer_connection* p = i->second; m_stat += p->statistics(); + + // updates the peer connection's ul/dl bandwidth + // resource requests p->second_tick(); - m_upload_bandwidth_quota.used += p->m_upload_bandwidth_quota.used; - m_upload_bandwidth_quota.min += p->m_upload_bandwidth_quota.min; - m_upload_bandwidth_quota.max = saturated_add( - m_upload_bandwidth_quota.max - , p->m_upload_bandwidth_quota.max); + + m_ul_bandwidth_quota.used += p->m_ul_bandwidth_quota.used; + m_ul_bandwidth_quota.min += p->m_ul_bandwidth_quota.min; + m_dl_bandwidth_quota.used += p->m_dl_bandwidth_quota.used; + m_dl_bandwidth_quota.min += p->m_dl_bandwidth_quota.min; + + m_ul_bandwidth_quota.max = saturated_add( + m_ul_bandwidth_quota.max + , p->m_ul_bandwidth_quota.max); + + m_dl_bandwidth_quota.max = saturated_add( + m_dl_bandwidth_quota.max + , p->m_dl_bandwidth_quota.max); + } - m_upload_bandwidth_quota.max - = std::min(m_upload_bandwidth_quota.max, m_upload_bandwidth_limit); + m_ul_bandwidth_quota.max + = std::min(m_ul_bandwidth_quota.max, m_upload_bandwidth_limit); + + m_dl_bandwidth_quota.max + = std::min(m_dl_bandwidth_quota.max, m_download_bandwidth_limit); m_stat.second_tick(); } void torrent::distribute_resources() { - allocate_resources(m_upload_bandwidth_quota.given + // distribute allowed upload among the peers + allocate_resources(m_ul_bandwidth_quota.given , m_connections - , &peer_connection::m_upload_bandwidth_quota); + , &peer_connection::m_ul_bandwidth_quota); + // distribute allowed download among the peers + allocate_resources(m_dl_bandwidth_quota.given + , m_connections + , &peer_connection::m_dl_bandwidth_quota); + + // tell all peers to reset their used quota. This is + // a new second and they can again use up their quota for (std::map::iterator i = m_connections.begin(); i != m_connections.end(); ++i) { diff --git a/src/torrent_handle.cpp b/src/torrent_handle.cpp index 2ce6caa3b..e665ea0db 100755 --- a/src/torrent_handle.cpp +++ b/src/torrent_handle.cpp @@ -446,15 +446,15 @@ namespace libtorrent p.total_download = statistics.total_payload_download(); p.total_upload = statistics.total_payload_upload(); - if (peer->upload_bandwidth_quota()->given == std::numeric_limits::max()) + if (peer->m_ul_bandwidth_quota.given == std::numeric_limits::max()) p.upload_limit = -1; else - p.upload_limit = peer->upload_bandwidth_quota()->given; + p.upload_limit = peer->m_ul_bandwidth_quota.given; - if (peer->upload_bandwidth_quota()->max == std::numeric_limits::max()) + if (peer->m_ul_bandwidth_quota.max == std::numeric_limits::max()) p.upload_ceiling = -1; else - p.upload_ceiling = peer->upload_bandwidth_quota()->max; + p.upload_ceiling = peer->m_ul_bandwidth_quota.given; p.load_balancing = peer->total_free_upload();