From 420ab6bff2204962eaf83887299c772826432d9b Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Sun, 9 Nov 2003 18:17:09 +0000 Subject: [PATCH] *** empty log message *** --- Jamfile | 9 +- docs/index.html | 26 ++- examples/client_test.cpp | 7 +- include/libtorrent/bencode.hpp | 2 +- include/libtorrent/debug.hpp | 6 + include/libtorrent/peer_connection.hpp | 53 +++++- include/libtorrent/peer_info.hpp | 1 + include/libtorrent/session.hpp | 19 ++- project-root.jam | 0 src/entry.cpp | 2 +- src/peer_connection.cpp | 217 ++++++++++++++++--------- src/piece_picker.cpp | 2 +- src/session.cpp | 141 ++++++++-------- src/storage.cpp | 2 +- src/torrent.cpp | 1 + src/torrent_handle.cpp | 2 + 16 files changed, 310 insertions(+), 180 deletions(-) create mode 100755 project-root.jam diff --git a/Jamfile b/Jamfile index 265207753..eb569a1b8 100755 --- a/Jamfile +++ b/Jamfile @@ -1,5 +1,4 @@ -project boost : $(BOOST_ROOT) ; SOURCES = entry.cpp @@ -7,7 +6,6 @@ SOURCES = piece_picker.cpp policy.cpp session.cpp -# socket_bsd.cpp socket_win.cpp stat.cpp storage.cpp @@ -20,6 +18,9 @@ SOURCES = lib torrent : src/$(SOURCES) + zlib//zlib + $(BOOST_ROOT)/libs/filesystem/build//boost_filesystem + $(BOOST_ROOT)/libs/thread/build//boost_thread : $(BOOST_ROOT) $(BOOST_ROOT) ./include @@ -32,9 +33,7 @@ lib torrent exe client_test : examples/client_test.cpp - torrent - @boost/libs/filesystem/build/boost_filesystem - @boost/libs/thread/build/boost_thread + torrent : $(BOOST_ROOT) $(BOOST_ROOT) ./include diff --git a/docs/index.html b/docs/index.html index 7c4576e4a..fb5c2bbbd 100755 --- a/docs/index.html +++ b/docs/index.html @@ -56,6 +56,7 @@ 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

    @@ -64,12 +65,12 @@ Functions that are yet to be implemented:

    • optimistic unchoke -
    • upload speed cap
    • Snubbing
    • end game mode
    • new allocation model
    • fast resume
    • file-level piece priority +
    • a good upload speed cap

    @@ -159,6 +160,7 @@ class session: public boost::noncopyable void remove_torrent(const torrent_handle& h); void set_http_settings(const http_settings& settings); + void set_upload_rate_limit(int bytes_per_second); }; @@ -188,6 +190,20 @@ identify the client. If the string is longer than 7 characters it will be trimmed down to 7 characters. The default is an empty string.

    +

    +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). +

    + +

    +The destructor of session will notify all trackers that our torrents has been shut down. +If some trackers are down, they will timout. All this before the destructor of session +returns. So, it's adviced that any kind of interface (such as windows) are closed before +destructing the sessoin object. Because it can take a few second for it to finish. The +timeout can be set with set_http_settings(). +

    +

    How to parse a torrent file and create a torrent_info object is described below.

    @@ -655,6 +671,7 @@ struct peer_info unsigned int total_upload; peer_id id; std::vector<bool> pieces; + int upload_limit; }; @@ -695,6 +712,11 @@ in the torrent. Each boolean tells you if the peer has that piece (if it's set t or if the peer miss that piece (set to false).

    +

    +upload_limit is the number of bytes per second we are allowed to send to this +peer every second. It may be -1 if there's no limit. +

    +

    TODO: address

    @@ -724,7 +746,7 @@ struct http_settings

    tracker_timeout is the number of seconds the tracker connection will -wait until it considers the tracker to have timed-out. Default value is 30 +wait until it considers the tracker to have timed-out. Default value is 10 seconds.

    diff --git a/examples/client_test.cpp b/examples/client_test.cpp index 91f2dd2b1..728c59ddf 100755 --- a/examples/client_test.cpp +++ b/examples/client_test.cpp @@ -44,6 +44,11 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/http_settings.hpp" #ifdef WIN32 + +#if defined(_MSC_VER) +# define for if (false) {} else for +#endif + #include #include @@ -164,7 +169,7 @@ int main(int argc, char* argv[]) { std::vector handles; session s(6881, "E\x1"); - + s.set_upload_rate_limit(10240); s.set_http_settings(settings); for (int i = 0; i < argc-1; ++i) { diff --git a/include/libtorrent/bencode.hpp b/include/libtorrent/bencode.hpp index a5fa1e298..03b5afb73 100755 --- a/include/libtorrent/bencode.hpp +++ b/include/libtorrent/bencode.hpp @@ -68,7 +68,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/entry.hpp" -#if defined(_MSC_VER) && _MSC_VER < 1300 +#if defined(_MSC_VER) namespace std { using ::isdigit; diff --git a/include/libtorrent/debug.hpp b/include/libtorrent/debug.hpp index 382adabfe..eccfa04e5 100755 --- a/include/libtorrent/debug.hpp +++ b/include/libtorrent/debug.hpp @@ -77,6 +77,12 @@ namespace libtorrent virtual ~logger() {} }; + struct null_logger: libtorrent::logger + { + public: + virtual void log(const char* text) {} + }; + struct cout_logger: libtorrent::logger { public: diff --git a/include/libtorrent/peer_connection.hpp b/include/libtorrent/peer_connection.hpp index 98ab2b2a4..f2db0074c 100755 --- a/include/libtorrent/peer_connection.hpp +++ b/include/libtorrent/peer_connection.hpp @@ -151,13 +151,36 @@ namespace libtorrent const stat& statistics() const { return m_statistics; } // is called once every second by the main loop - void second_tick() { m_statistics.second_tick(); } + void second_tick() + { + m_statistics.second_tick(); + m_send_quota_left = m_send_quota; + if (m_send_quota > 0) send_buffer_updated(); + } boost::shared_ptr get_socket() const { return m_socket; } const peer_id& get_peer_id() const { return m_peer_id; } const std::vector& get_bitfield() const { return m_have_piece; } + // sets the number of bytes this peer + // is allowed to send until it should + // stop sending. When it stops sending + // it will simply wait for another call + // to second_tick() where it will get + // more send quota. + void set_send_quota(int num_bytes); + + // 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 { return m_send_quota_left; } + + // returns the send quota assigned to this + // peer. + int send_quota() const { return m_send_quota; } + #ifndef NDEBUG boost::shared_ptr m_logger; #endif @@ -176,7 +199,7 @@ namespace libtorrent enum state { read_protocol_length = 0, - read_protocol_version, + read_protocol_string, read_info_hash, read_peer_id, @@ -264,6 +287,20 @@ namespace libtorrent std::vector m_download_queue; stat m_statistics; + + // this is used to limit upload bandwidth. + // it is reset to the allowed number of + // bytes to send frequently. Every time + // thie peer send some data, + // m_send_quota_left variable will be decreased + // so it can limit the number of bytes next + // time it sends data. when it reaches zero + // the client will stop send data and await + // more quota. if it is set to -1, the peer + // will ignore the qouta and send at maximum + // speed + int m_send_quota; + int m_send_quota_left; }; // this is called each time this peer generates some @@ -271,8 +308,18 @@ namespace libtorrent // the writibility monitor in the selector. inline void peer_connection::send_buffer_updated() { - if (!has_data()) return; + if (!has_data()) + { + if (m_added_to_selector) + { + m_selector.remove_writable(m_socket); + m_added_to_selector = false; + } + assert(!m_selector.is_writability_monitored(m_socket)); + return; + } + assert(has_data()); if (!m_added_to_selector) { m_selector.monitor_writability(m_socket); diff --git a/include/libtorrent/peer_info.hpp b/include/libtorrent/peer_info.hpp index 14233dcec..e5f95b3bc 100755 --- a/include/libtorrent/peer_info.hpp +++ b/include/libtorrent/peer_info.hpp @@ -56,6 +56,7 @@ namespace libtorrent unsigned int total_upload; peer_id id; std::vector pieces; + int upload_limit; }; } diff --git a/include/libtorrent/session.hpp b/include/libtorrent/session.hpp index 4beeb0d42..8a491aac4 100755 --- a/include/libtorrent/session.hpp +++ b/include/libtorrent/session.hpp @@ -173,16 +173,18 @@ namespace libtorrent // the settings for the client http_settings m_settings; + // set to true when the session object + // is being destructed and the thread + // should exit volatile bool m_abort; - -#ifndef NDEBUG - boost::shared_ptr create_log(std::string name) - { - name = "libtorrent_log_" + name + ".log"; - // current options are file_logger and cout_logger - return boost::shared_ptr(new file_logger(name.c_str())); - } + // maximum upload rate given in + // bytes per second. -1 means + // unlimited + int m_upload_rate; + +#ifndef NDEBUG + boost::shared_ptr create_log(std::string name); boost::shared_ptr m_logger; #endif }; @@ -208,6 +210,7 @@ namespace libtorrent void remove_torrent(const torrent_handle& h); void set_http_settings(const http_settings& s); + void set_upload_rate_limit(int bytes_per_second); private: diff --git a/project-root.jam b/project-root.jam new file mode 100755 index 000000000..e69de29bb diff --git a/src/entry.cpp b/src/entry.cpp index afc6fbe04..8b68f63b6 100755 --- a/src/entry.cpp +++ b/src/entry.cpp @@ -32,7 +32,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/entry.hpp" -#if defined(_MSC_VER) && _MSV_CER < 1300 +#if defined(_MSC_VER) namespace std { using ::isprint; diff --git a/src/peer_connection.cpp b/src/peer_connection.cpp index 69495b7e8..3f5da2085 100755 --- a/src/peer_connection.cpp +++ b/src/peer_connection.cpp @@ -37,7 +37,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/peer_connection.hpp" #include "libtorrent/session.hpp" -#if defined(_MSC_VER) && _MSC_VER < 1300 +#if defined(_MSC_VER) #define for if (false) {} else for #endif @@ -94,10 +94,12 @@ libtorrent::peer_connection::peer_connection( , m_peer_choked(true) , m_interesting(false) , m_choked(true) + , m_send_quota(-1) + , m_send_quota_left(-1) { assert(m_torrent != 0); -#if defined(TORRENT_VERBOSE_LOGGING) +#ifndef NDEBUG m_logger = m_ses->create_log(s->sender().as_string().c_str()); #endif @@ -135,9 +137,11 @@ libtorrent::peer_connection::peer_connection( , m_peer_choked(true) , m_interesting(false) , m_choked(true) + , m_send_quota(-1) + , m_send_quota_left(-1) { -#if defined(TORRENT_VERBOSE_LOGGING) +#ifndef NDEBUG m_logger = m_ses->create_log(s->sender().as_string().c_str()); #endif @@ -156,20 +160,57 @@ libtorrent::peer_connection::~peer_connection() if (m_torrent) m_torrent->remove_peer(this); } +void libtorrent::peer_connection::set_send_quota(int num_bytes) +{ + assert(num_bytes >= 0); + m_send_quota = num_bytes; + m_send_quota_left = num_bytes; + send_buffer_updated(); +} + + void libtorrent::peer_connection::send_handshake() { assert(m_send_buffer.size() == 0); // add handshake to the send buffer - m_send_buffer.resize(68); - const char* version_string = "BitTorrent protocol"; - m_send_buffer[0] = 19; - std::copy(version_string, version_string+19, m_send_buffer.begin()+1); - std::fill(m_send_buffer.begin() + 20, m_send_buffer.begin() + 28, 0); - std::copy(m_torrent->torrent_file().info_hash().begin(), m_torrent->torrent_file().info_hash().end(), m_send_buffer.begin() + 28); - std::copy(m_ses->get_peer_id().begin(), m_ses->get_peer_id().end(), m_send_buffer.begin() + 48); + const char version_string[] = "BitTorrent protocol"; + const int string_len = sizeof(version_string)-1; + int pos = 1; -#if defined(TORRENT_VERBOSE_LOGGING) + m_send_buffer.resize(1 + string_len + 8 + 20 + 20); + + // length of version string + m_send_buffer[0] = string_len; + + // version string itself + std::copy( + version_string + , version_string+string_len + , m_send_buffer.begin()+pos); + pos += string_len; + + // 8 zeroes + std::fill( + m_send_buffer.begin() + pos + , m_send_buffer.begin() + pos + 8 + , 0); + pos += 8; + + // info hash + std::copy( + m_torrent->torrent_file().info_hash().begin() + , m_torrent->torrent_file().info_hash().end() + , m_send_buffer.begin() + pos); + pos += 20; + + // peer id + std::copy( + m_ses->get_peer_id().begin() + , m_ses->get_peer_id().end() + , m_send_buffer.begin() + pos); + +#ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " ==> HANDSHAKE\n"; #endif @@ -187,7 +228,7 @@ bool libtorrent::peer_connection::dispatch_message() // *************** CHOKE *************** case msg_choke: -#if defined(TORRENT_VERBOSE_LOGGING) +#ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " <== CHOKE\n"; #endif m_peer_choked = true; @@ -211,7 +252,7 @@ bool libtorrent::peer_connection::dispatch_message() // *************** UNCHOKE *************** case msg_unchoke: -#if defined(TORRENT_VERBOSE_LOGGING) +#ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " <== UNCHOKE\n"; #endif m_peer_choked = false; @@ -221,7 +262,7 @@ bool libtorrent::peer_connection::dispatch_message() // *************** INTERESTED *************** case msg_interested: -#if defined(TORRENT_VERBOSE_LOGGING) +#ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " <== INTERESTED\n"; #endif m_peer_interested = true; @@ -231,7 +272,7 @@ bool libtorrent::peer_connection::dispatch_message() // *************** NOT INTERESTED *************** case msg_not_interested: -#if defined(TORRENT_VERBOSE_LOGGING) +#ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " <== NOT_INTERESTED\n"; #endif m_peer_interested = false; @@ -248,13 +289,13 @@ bool libtorrent::peer_connection::dispatch_message() if (index >= m_have_piece.size()) return false; -#if defined(TORRENT_VERBOSE_LOGGING) +#ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " <== HAVE [ piece: " << index << "]\n"; #endif if (m_have_piece[index]) { -#if defined(TORRENT_VERBOSE_LOGGING) +#ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " oops.. we already knew that: " << index << "\n"; #endif } @@ -276,7 +317,7 @@ bool libtorrent::peer_connection::dispatch_message() if (m_packet_size - 1 != (m_have_piece.size() + 7) / 8) return false; -#if defined(TORRENT_VERBOSE_LOGGING) +#ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " <== BITFIELD\n"; #endif bool interesting = false; @@ -298,7 +339,7 @@ bool libtorrent::peer_connection::dispatch_message() } if (is_seed) { -#if defined(TORRENT_VERBOSE_LOGGING) +#ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " *** THIS IS A SEED ***\n"; #endif } @@ -323,7 +364,7 @@ bool libtorrent::peer_connection::dispatch_message() send_buffer_updated(); } -#if defined(TORRENT_VERBOSE_LOGGING) +#ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " <== REQUEST [ piece: " << r.piece << " | s: " << r.start << " | l: " << r.length << " ]\n"; #endif @@ -338,7 +379,7 @@ bool libtorrent::peer_connection::dispatch_message() std::size_t index = read_int(&m_recv_buffer[1]); if (index < 0 || index >= m_torrent->torrent_file().num_pieces()) { -#if defined(TORRENT_VERBOSE_LOGGING) +#ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " piece index invalid\n"; #endif return false; @@ -348,7 +389,7 @@ bool libtorrent::peer_connection::dispatch_message() if (offset < 0) { -#if defined(TORRENT_VERBOSE_LOGGING) +#ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " offset < 0\n"; #endif return false; @@ -356,7 +397,7 @@ bool libtorrent::peer_connection::dispatch_message() if (offset + len > m_torrent->torrent_file().piece_size(index)) { -#if defined(TORRENT_VERBOSE_LOGGING) +#ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " piece packet contains more data than the piece size\n"; #endif return false; @@ -364,7 +405,7 @@ bool libtorrent::peer_connection::dispatch_message() if (offset % m_torrent->block_size() != 0) { -#if defined(TORRENT_VERBOSE_LOGGING) +#ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " piece packet contains unaligned offset\n"; #endif return false; @@ -373,7 +414,7 @@ bool libtorrent::peer_connection::dispatch_message() piece_block req = m_download_queue.front(); if (req.piece_index != index) { -#if defined(TORRENT_VERBOSE_LOGGING) +#ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " piece packet contains unrequested index\n"; #endif return false; @@ -381,13 +422,13 @@ bool libtorrent::peer_connection::dispatch_message() if (req.block_index != offset / m_torrent->block_size()) { -#if defined(TORRENT_VERBOSE_LOGGING) +#ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " piece packet contains unrequested offset\n"; #endif return false; } */ -#if defined(TORRENT_VERBOSE_LOGGING) +#ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " <== PIECE [ piece: " << index << " | s: " << offset << " | l: " << len << " ]\n"; #endif @@ -479,7 +520,7 @@ bool libtorrent::peer_connection::dispatch_message() m_selector.remove_writable(m_socket); } -#if defined(TORRENT_VERBOSE_LOGGING) +#ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " <== CANCEL [ piece: " << r.piece << " | s: " << r.start << " | l: " << r.length << " ]\n"; #endif break; @@ -530,7 +571,7 @@ void libtorrent::peer_connection::cancel_block(piece_block block) // length write_int(block_size, &m_send_buffer[start_offset]); start_offset += 4; -#if defined(TORRENT_VERBOSE_LOGGING) +#ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " ==> CANCEL [ piece: " << block.piece_index << " | s: " << block_offset << " | l: " << block_size << " | " << block.block_index << " ]\n"; #endif assert(start_offset == m_send_buffer.size()); @@ -577,7 +618,7 @@ void libtorrent::peer_connection::request_block(piece_block block) // length write_int(block_size, &m_send_buffer[start_offset]); start_offset += 4; -#if defined(TORRENT_VERBOSE_LOGGING) +#ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " ==> REQUEST [ piece: " << block.piece_index << " | s: " << block_offset << " | l: " << block_size << " | " << block.block_index << " ]\n"; #endif assert(start_offset == m_send_buffer.size()); @@ -587,7 +628,7 @@ void libtorrent::peer_connection::request_block(piece_block block) void libtorrent::peer_connection::send_bitfield() { -#if defined(TORRENT_VERBOSE_LOGGING) +#ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " ==> BITFIELD\n"; #endif const int packet_size = (m_have_piece.size() + 7) / 8 + 5; @@ -610,7 +651,7 @@ void libtorrent::peer_connection::choke() char msg[] = {0,0,0,1,msg_choke}; m_send_buffer.insert(m_send_buffer.end(), msg, msg+sizeof(msg)); m_choked = true; -#if defined(TORRENT_VERBOSE_LOGGING) +#ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " ==> CHOKE\n"; #endif send_buffer_updated(); @@ -622,7 +663,7 @@ void libtorrent::peer_connection::unchoke() char msg[] = {0,0,0,1,msg_unchoke}; m_send_buffer.insert(m_send_buffer.end(), msg, msg+sizeof(msg)); m_choked = false; -#if defined(TORRENT_VERBOSE_LOGGING) +#ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " ==> UNCHOKE\n"; #endif send_buffer_updated(); @@ -634,7 +675,7 @@ void libtorrent::peer_connection::interested() char msg[] = {0,0,0,1,msg_interested}; m_send_buffer.insert(m_send_buffer.end(), msg, msg+sizeof(msg)); m_interesting = true; -#if defined(TORRENT_VERBOSE_LOGGING) +#ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " ==> INTERESTED\n"; #endif send_buffer_updated(); @@ -646,7 +687,7 @@ void libtorrent::peer_connection::not_interested() char msg[] = {0,0,0,1,msg_not_interested}; m_send_buffer.insert(m_send_buffer.end(), msg, msg+sizeof(msg)); m_interesting = false; -#if defined(TORRENT_VERBOSE_LOGGING) +#ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " ==> NOT_INTERESTED\n"; #endif send_buffer_updated(); @@ -657,7 +698,7 @@ void libtorrent::peer_connection::send_have(int index) char msg[9] = {0,0,0,5,msg_have}; write_int(index, msg+5); m_send_buffer.insert(m_send_buffer.end(), msg, msg+9); -#if defined(TORRENT_VERBOSE_LOGGING) +#ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " ==> HAVE [ piece: " << index << " ]\n"; #endif send_buffer_updated(); @@ -681,11 +722,13 @@ void libtorrent::peer_connection::receive_data() // an error if (received < 0) { - // would block means that no data was ready to be received + // would_block means that no data was ready to be received + // this should never happen, since we have a selector + //assert(m_socket->last_error() != socket::would_block); if (m_socket->last_error() == socket::would_block) return; // the connection was closed - throw network_error(0); + throw network_error(m_socket->last_error()); } if (received > 0) @@ -701,28 +744,25 @@ void libtorrent::peer_connection::receive_data() { case read_protocol_length: m_packet_size = reinterpret_cast(m_recv_buffer[0]); - #if defined(TORRENT_VERBOSE_LOGGING) + #ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " protocol length: " << m_packet_size << "\n"; #endif - m_state = read_protocol_version; - if (m_packet_size != 19) - throw network_error(0); + m_state = read_protocol_string; m_recv_buffer.resize(m_packet_size); m_recv_pos = 0; break; - case read_protocol_version: + case read_protocol_string: { - const char* protocol_version = "BitTorrent protocol"; - #if defined(TORRENT_VERBOSE_LOGGING) - (*m_logger) << m_socket->sender().as_string() << " protocol name: " << std::string(m_recv_buffer.begin(), m_recv_buffer.end()) << "\n"; + #ifndef NDEBUG + (*m_logger) << m_socket->sender().as_string() << " protocol: '" << std::string(m_recv_buffer.begin(), m_recv_buffer.end()) << "'\n"; #endif - if (!std::equal(m_recv_buffer.begin(), m_recv_buffer.end(), protocol_version)) - { - // unknown protocol, close connection + const char protocol_string[] = "BitTorrent protocol"; + const int protocol_len = sizeof(protocol_string) - 1; + if (!std::equal(m_recv_buffer.begin(), m_recv_buffer.end(), protocol_string)) throw network_error(0); - } + m_state = read_info_hash; m_packet_size = 28; m_recv_pos = 0; @@ -738,7 +778,11 @@ void libtorrent::peer_connection::receive_data() if (m_torrent == 0) { - // no, we have to see if there's a torrent with the + // TODO: if the protocol is to be extended + // these 8 bytes would be used to describe the + // extensions available on the other side + + // now, we have to see if there's a torrent with the // info_hash we got from the peer sha1_hash info_hash; std::copy(m_recv_buffer.begin()+8, m_recv_buffer.begin() + 28, (char*)info_hash.begin()); @@ -747,7 +791,7 @@ void libtorrent::peer_connection::receive_data() if (m_torrent == 0) { // we couldn't find the torrent! - #if defined(TORRENT_VERBOSE_LOGGING) + #ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " couldn't find a torrent with the given info_hash\n"; #endif throw network_error(0); @@ -769,7 +813,7 @@ void libtorrent::peer_connection::receive_data() // verify info hash if (!std::equal(m_recv_buffer.begin()+8, m_recv_buffer.begin() + 28, (const char*)m_torrent->torrent_file().info_hash().begin())) { - #if defined(TORRENT_VERBOSE_LOGGING) + #ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " received invalid info_hash\n"; #endif throw network_error(0); @@ -780,7 +824,7 @@ void libtorrent::peer_connection::receive_data() m_packet_size = 20; m_recv_pos = 0; m_recv_buffer.resize(20); - #if defined(TORRENT_VERBOSE_LOGGING) + #ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " info_hash received\n"; #endif break; @@ -796,7 +840,7 @@ void libtorrent::peer_connection::receive_data() // can this be correct? if (!std::equal(m_recv_buffer.begin(), m_recv_buffer.begin() + 20, (const char*)m_peer_id.begin())) { - #if defined(TORRENT_VERBOSE_LOGGING) + #ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " invalid peer_id (it doesn't equal the one from the tracker)\n"; #endif throw network_error(0); @@ -809,7 +853,7 @@ void libtorrent::peer_connection::receive_data() std::copy(m_recv_buffer.begin(), m_recv_buffer.begin() + 20, (char*)m_peer_id.begin()); if (m_torrent->num_connections(m_peer_id) > 1) { - #if defined(TORRENT_VERBOSE_LOGGING) + #ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " duplicate connection, closing\n"; #endif throw network_error(0); @@ -820,7 +864,7 @@ void libtorrent::peer_connection::receive_data() m_packet_size = 4; m_recv_pos = 0; m_recv_buffer.resize(4); - #if defined(TORRENT_VERBOSE_LOGGING) + #ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " received peer_id\n"; #endif break; @@ -833,7 +877,7 @@ void libtorrent::peer_connection::receive_data() // don't accept packets larger than 1 MB if (m_packet_size > 1024*1024 || m_packet_size < 0) { - #if defined(TORRENT_VERBOSE_LOGGING) + #ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " packet too large (packet_size > 1 Megabyte), abort\n"; #endif // packet too large @@ -857,7 +901,7 @@ void libtorrent::peer_connection::receive_data() case read_packet: if (!dispatch_message()) { - #if defined(TORRENT_VERBOSE_LOGGING) + #ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " received invalid packet\n"; #endif // invalid message @@ -880,7 +924,10 @@ bool libtorrent::peer_connection::has_data() const throw() { // if we have requests or pending data to be sent or announcements to be made // we want to send data - return (!m_requests.empty() && !m_choked) || !m_send_buffer.empty() || !m_announce_queue.empty(); + return ((!m_requests.empty() && !m_choked) + || !m_send_buffer.empty() + || !m_announce_queue.empty()) + && m_send_quota_left != 0; } // -------------------------- @@ -913,18 +960,19 @@ void libtorrent::peer_connection::send_data() throw network_error(0); } - if (m_sending_piece.index() != r.piece) - { - m_sending_piece.open( - m_torrent->filesystem() - , r.piece - , piece_file::in - , r.start); + m_sending_piece.open( + m_torrent->filesystem() + , r.piece + , piece_file::in + , r.start); #ifndef NDEBUG - assert(m_torrent->filesystem()->verify_piece(m_sending_piece) && "internal error"); - m_sending_piece.open(m_torrent->filesystem(), r.piece, piece_file::in); + assert(m_torrent->filesystem()->verify_piece(m_sending_piece) && "internal error"); + m_sending_piece.open( + m_torrent->filesystem() + , r.piece + , piece_file::in + , r.start); #endif - } const int send_buffer_offset = m_send_buffer.size(); const int packet_size = 4 + 5 + 4 + r.length; m_send_buffer.resize(send_buffer_offset + packet_size); @@ -936,7 +984,7 @@ void libtorrent::peer_connection::send_data() assert(r.start == m_sending_piece.tell()); m_sending_piece.read(&m_send_buffer[send_buffer_offset+13], r.length); -#if defined(TORRENT_VERBOSE_LOGGING) +#ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " ==> PIECE [ piece: " << r.piece << " | s: " << r.start << " | l: " << r.length << " ]\n"; #endif // let the torrent keep track of how much we have uploaded @@ -944,7 +992,7 @@ void libtorrent::peer_connection::send_data() } else { -#if defined(TORRENT_VERBOSE_LOGGING) +#ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " *** WARNING [ illegal piece request idx: " << r.piece << " | s: " << r.start @@ -970,22 +1018,33 @@ void libtorrent::peer_connection::send_data() m_announce_queue.clear(); } + assert(m_send_quota_left != 0); + // send the actual buffer if (!m_send_buffer.empty()) { + int amount_to_send = m_send_buffer.size(); + assert(m_send_quota_left != 0); + if (m_send_quota_left > 0) + amount_to_send = std::min(m_send_quota_left, amount_to_send); // we have data that's scheduled for sending int sent = m_socket->send( &m_send_buffer[0] - , m_send_buffer.size()); + , amount_to_send); - #if defined(TORRENT_VERBOSE_LOGGING) + #ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " ==> SENT [ length: " << sent << " ]\n"; #endif if (sent > 0) { m_statistics.sent_bytes(sent); + if (m_send_quota_left != -1) + { + assert(m_send_quota_left >= sent); + m_send_quota_left -= sent; + } // empty the entire buffer at once or if // only a part of the buffer could be sent @@ -1011,13 +1070,9 @@ void libtorrent::peer_connection::send_data() } assert(m_added_to_selector); - if (!has_data()) - { - m_selector.remove_writable(m_socket); - m_added_to_selector = false; - } + send_buffer_updated(); #ifndef NDEBUG - else + if (has_data()) { if (m_socket->is_writable()) { @@ -1037,7 +1092,7 @@ void libtorrent::peer_connection::keep_alive() char noop[] = {0,0,0,0}; m_send_buffer.insert(m_send_buffer.end(), noop, noop+4); m_last_sent = boost::posix_time::second_clock::local_time(); -#if defined(TORRENT_VERBOSE_LOGGING) +#ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " ==> NOP\n"; #endif send_buffer_updated(); diff --git a/src/piece_picker.cpp b/src/piece_picker.cpp index f19c7da81..cdcd38d00 100755 --- a/src/piece_picker.cpp +++ b/src/piece_picker.cpp @@ -40,7 +40,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/peer_connection.hpp" #endif -#if defined(_MSC_VER) && _MSC_VER < 1300 +#if defined(_MSC_VER) #define for if (false) {} else for #endif diff --git a/src/session.cpp b/src/session.cpp index 0c6d8ab1c..4d6831aa9 100755 --- a/src/session.cpp +++ b/src/session.cpp @@ -59,20 +59,6 @@ namespace std }; #endif -/* - -DESIGN OVERVIEW AND RATIONALE - -The main goal of this library is to be efficient, primarily memory-wise -but also CPU-wise. This goal has a number of implications: - -* It must handle multiple torrents (multiple processes uses much more memory) -* It relies on a well working disk chache, since it will download directly to disk. This is - to scale better when having many peer connections. -* - -*/ - namespace libtorrent { namespace detail @@ -149,6 +135,7 @@ namespace libtorrent : m_abort(false) , m_tracker_manager(m_settings) , m_listen_port(listen_port) + , m_upload_rate(-1) { // ---- generate a peer id ---- @@ -178,11 +165,9 @@ namespace libtorrent void session_impl::operator()() { eh_initializer(); -#if defined(TORRENT_VERBOSE_LOGGING) +#ifndef NDEBUG m_logger = create_log("main session"); #endif - try - { boost::shared_ptr listener(new socket(socket::tcp, false)); int max_port = m_listen_port + 9; @@ -206,7 +191,7 @@ namespace libtorrent break; } -#if defined(TORRENT_VERBOSE_LOGGING) +#ifndef NDEBUG (*m_logger) << "listening on port: " << m_listen_port << "\n"; #endif m_selector.monitor_readability(listener); @@ -233,6 +218,17 @@ namespace libtorrent for(;;) { + +#ifndef NDEBUG + for (connection_map::iterator i = m_connections.begin(); + i != m_connections.end(); + ++i) + { + assert(i->second->has_data() == m_selector.is_writability_monitored(i->first)); + } +#endif + + // if nothing happens within 500000 microseconds (0.5 seconds) // do the loop anyway to check if anything else has changed // (*m_logger) << "sleeping\n"; @@ -264,9 +260,6 @@ namespace libtorrent // RECEIVE SOCKETS // ************************ - // TODO: once every second or so, all sockets should receive_data() to purge connections - // that has been closed. Otherwise we have to wait 2 minutes for their timeout - // let the readable clients receive data for (std::vector >::iterator i = readable_clients.begin(); i != readable_clients.end(); @@ -280,14 +273,16 @@ namespace libtorrent if (s) { // we got a connection request! -#if defined(TORRENT_VERBOSE_LOGGING) +#ifndef NDEBUG (*m_logger) << s->sender().as_string() << " <== INCOMING CONNECTION\n"; #endif // TODO: the send buffer size should be controllable from the outside // s->set_send_bufsize(2048); // TODO: add some possibility to filter IP:s - boost::shared_ptr c(new peer_connection(this, m_selector, s)); + boost::shared_ptr c( + new peer_connection(this, m_selector, s)); + if (m_upload_rate != -1) c->set_send_quota(100); m_connections.insert(std::make_pair(s, c)); m_selector.monitor_readability(s); m_selector.monitor_errors(s); @@ -378,51 +373,10 @@ namespace libtorrent ++i) { assert(i->second->has_data() == m_selector.is_writability_monitored(i->first)); -// if (m_selector.is_writability_monitored(i->first)) -// assert(i->second->has_data()); } #endif - // clear all writablility monitors and add - // the ones who still has data to send -/* m_selector.clear_writable(); - - - // ************************ - // BUILD WRITER LIST - // ************************ - - // TODO: REWRITE THIS! DON'T GO THROUGH THIS LOOP EVERY TIME! - - // loop over all clients and purge the ones that has timed out - // and check if they have pending data to be sent - for (connection_map::iterator i = m_connections.begin(); - i != m_connections.end();) - { - connection_map::iterator j = i; - ++i; - if (j->second->has_timed_out()) - { - m_selector.remove(j->first); - m_connections.erase(j); - } - else - { - j->second->keep_alive(); - if (j->second->has_data()) - { - // (*m_logger) << j->first->sender().as_string() << " has data\n"; - m_selector.monitor_writability(j->first); - } - else - { - // (*m_logger) << j->first->sender().as_string() << " has NO data\n"; - } - } - } -*/ - // (*m_logger) << "time: " << std::clock()-timer << "\n"; boost::posix_time::time_duration d = boost::posix_time::second_clock::local_time() - timer; if (d.seconds() < 1) continue; timer = boost::posix_time::second_clock::local_time(); @@ -432,6 +386,23 @@ namespace libtorrent // THE SECTION BELOW IS EXECUTED ONCE EVERY SECOND // ************************ + // 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. + if (m_upload_rate != -1 && !m_connections.empty()) + { + assert(m_upload_rate >= 0); + int share = m_upload_rate / m_connections.size(); + for (connection_map::iterator i = m_connections.begin(); + i != m_connections.end(); + ++i) + { + i->second->set_send_quota(share); + } + } + // do the second_tick() on each connection // this will update their statistics (download and upload speeds) // also purge sockets that have timed out @@ -481,7 +452,7 @@ namespace libtorrent } m_tracker_manager.tick(); -#if defined(TORRENT_VERBOSE_LOGGING) +#ifndef NDEBUG (*m_logger) << "peers: " << m_connections.size() << " \n"; for (connection_map::iterator i = m_connections.begin(); i != m_connections.end(); @@ -503,17 +474,6 @@ namespace libtorrent t.nsec += 1000000; boost::thread::sleep(t); } - } - catch(const std::exception& e) - { - std::cerr << e.what() << "\n"; - } - catch(...) - { - std::cerr << "error\n"; - } - - } @@ -527,6 +487,19 @@ namespace libtorrent return 0; } +#ifndef NDEBUG + boost::shared_ptr session_impl::create_log(std::string name) + { + name = "libtorrent_log_" + name + ".log"; + // current options are file_logger and cout_logger +#if defined(TORRENT_VERBOSE_LOGGING) + return boost::shared_ptr(new file_logger(name.c_str())); +#else + return boost::shared_ptr(new null_logger()); +#endif + } +#endif + } session::session(int listen_port, const std::string& fingerprint) @@ -627,6 +600,22 @@ namespace libtorrent m_checker_thread.join(); } + void session::set_upload_rate_limit(int bytes_per_second) + { + assert(bytes_per_second > 0); + boost::mutex::scoped_lock l(m_impl.m_mutex); + m_impl.m_upload_rate = bytes_per_second; + if (m_impl.m_upload_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->second->set_send_quota(-1); + } + } + // TODO: document // TODO: if the first 4 charachters are printable // maybe they should be considered a fingerprint? diff --git a/src/storage.cpp b/src/storage.cpp index 654e19483..b790a2623 100755 --- a/src/storage.cpp +++ b/src/storage.cpp @@ -48,7 +48,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/hasher.hpp" #include "libtorrent/session.hpp" -#if defined(_MSC_VER) && _MSV_CER < 1300 +#if defined(_MSC_VER) #define for if (false) {} else for #endif diff --git a/src/torrent.cpp b/src/torrent.cpp index 831dbd1ec..a11290477 100755 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -347,6 +347,7 @@ namespace libtorrent , this , s , id)); + if (m_ses->m_upload_rate != -1) c->set_send_quota(100); detail::session_impl::connection_map::iterator p = m_ses->m_connections.insert(std::make_pair(s, c)).first; attach_peer(boost::get_pointer(p->second)); diff --git a/src/torrent_handle.cpp b/src/torrent_handle.cpp index 8779dc87e..6280b393e 100755 --- a/src/torrent_handle.cpp +++ b/src/torrent_handle.cpp @@ -139,6 +139,8 @@ namespace libtorrent p.total_download = statistics.total_download(); p.total_upload = statistics.total_upload(); + p.upload_limit = peer->send_quota(); + p.flags = 0; if (peer->is_interesting()) p.flags |= peer_info::interesting; if (peer->has_choked()) p.flags |= peer_info::choked;