diff --git a/examples/client_test.cpp b/examples/client_test.cpp index 237ee420b..179520c0e 100755 --- a/examples/client_test.cpp +++ b/examples/client_test.cpp @@ -182,8 +182,9 @@ int main(int argc, char* argv[]) in.unsetf(std::ios_base::skipws); entry e = bdecode(std::istream_iterator(in), std::istream_iterator()); torrent_info t(e); +// t.convert_file_names(); t.print(std::cout); - handles.push_back(s.add_torrent(t, "")); + handles.push_back(s.add_torrent(t, boost::filesystem::path("", boost::filesystem::native))); } catch (std::exception& e) { diff --git a/include/libtorrent/peer_connection.hpp b/include/libtorrent/peer_connection.hpp index 8d91d8313..8eb137311 100755 --- a/include/libtorrent/peer_connection.hpp +++ b/include/libtorrent/peer_connection.hpp @@ -137,7 +137,7 @@ namespace libtorrent void cancel_block(piece_block block); bool is_interesting() const throw() { return m_interesting; } - bool has_choked() const throw() { return m_choked; } + bool is_choked() const throw() { return m_choked; } bool is_peer_interested() const throw() { return m_peer_interested; } bool has_peer_choked() const throw() { return m_peer_choked; } @@ -146,7 +146,7 @@ namespace libtorrent // may be zero if the connection is an incoming connection // and it hasn't received enough information to determine // which torrent it should be associated with - torrent* associated_torrent() const throw() { return m_torrent; } + torrent* associated_torrent() const throw() { return m_attached_to_torrent?m_torrent:0; } const stat& statistics() const { return m_statistics; } @@ -239,7 +239,20 @@ namespace libtorrent selector& m_selector; boost::shared_ptr m_socket; + + // this is the torrent this connection is + // associated with. If the connection is an + // incoming conncetion, this is set to zero + // until the info_hash is received. Then it's + // set to the torrent it belongs to. torrent* m_torrent; + + // this is set to false until the peer_id + // is received from the other end. Or is + // true if the conenction was actively + // opened from our side. + bool m_attached_to_torrent; + detail::session_impl* m_ses; // is true if it was we that connected to the peer // and false if we got an incomming connection diff --git a/include/libtorrent/policy.hpp b/include/libtorrent/policy.hpp index d21f40d6c..e0f4d8663 100755 --- a/include/libtorrent/policy.hpp +++ b/include/libtorrent/policy.hpp @@ -36,6 +36,9 @@ POSSIBILITY OF SUCH DAMAGE. #include #include +#include +#include + #include "libtorrent/peer.hpp" #include "libtorrent/piece_picker.hpp" @@ -53,9 +56,13 @@ namespace libtorrent public: policy(torrent* t); - // this is called each time we get an incoming connection - // return true to accept the connection - bool accept_connection(const address& remote); + + // this is called every 10 seconds to allow + // for peer choking management + void pulse(); + + // called when an incoming connection is accepted + void new_connection(const boost::weak_ptr& c); // this is called once for every peer we get from // the tracker @@ -83,8 +90,77 @@ namespace libtorrent // the peer is not interested in our pieces void not_interested(peer_connection& c); +#ifndef NDEBUG + bool has_connection(const peer_connection* p); +#endif + private: + struct peer + { + peer(const peer_id& pid) + : id(pid) + , last_optimistically_unchoked(boost::posix_time::second_clock::local_time()) + , connected(boost::posix_time::second_clock::local_time()) + , optimistic_unchokes(0) + , prev_amount_upload(0) + , prev_amount_download(0) + {} + + bool operator==(const peer_id& pid) const + { return id == pid; } + + // the id of the peer. This is needed to store information + // about peers that aren't connected right now. This + // is to avoid peers reconnecting. unconnected entries + // will be saved a limited amount of time + peer_id id; + + // the time when this peer was optimistically unchoked + // the last time. + boost::posix_time::ptime last_optimistically_unchoked; + + // the time when the peer connected to us + // or disconnected if it isn't connected right now + boost::posix_time::ptime connected; + + // the number of optimistic unchokes this peer has + // been given + int optimistic_unchokes; + + // this is the accumulated amount of + // uploaded and downloaded data to this + // peer. It only accounts for what was + // shared during the last connection to + // this peer. i.e. These are only updated + // when the connection is closed. For the + // total amount of upload and download + // we'll have to add thes figures with the + // statistics from the peer_connection. + int prev_amount_upload; + int prev_amount_download; + + // if the peer is connected now, this + // will refer to a valid peer_connection + boost::weak_ptr connection; + }; + + // a functor that identifies peers that have disconnected and that + // are too old for still being saved. + struct old_disconnected_peer + { + bool operator()(const peer& p) + { + using namespace boost::posix_time; + + return p.connection.expired() + && second_clock::local_time() - p.connected > seconds(5*60); + } + }; + + + std::vector m_peers; + int m_num_peers; torrent* m_torrent; diff --git a/include/libtorrent/session.hpp b/include/libtorrent/session.hpp index 86b661c08..c0817ca79 100755 --- a/include/libtorrent/session.hpp +++ b/include/libtorrent/session.hpp @@ -171,6 +171,7 @@ namespace libtorrent alert_manager m_alerts; #ifndef NDEBUG + void assert_invariant(); boost::shared_ptr create_log(std::string name); boost::shared_ptr m_logger; #endif diff --git a/include/libtorrent/socket.hpp b/include/libtorrent/socket.hpp index be153fe51..b741e4c1a 100755 --- a/include/libtorrent/socket.hpp +++ b/include/libtorrent/socket.hpp @@ -33,6 +33,9 @@ POSSIBILITY OF SUCH DAMAGE. #ifndef TORRENT_SOCKET_WIN_HPP_INCLUDED #define TORRENT_SOCKET_WIN_HPP_INCLUDED +// TODO: remove the dependency of +// platform specific headers here. + #if defined(_WIN32) #include #else @@ -135,57 +138,32 @@ namespace libtorrent enum error_code { -#if defined(_WIN32) - netdown = WSAENETDOWN, - fault = WSAEFAULT, - access = WSAEACCES, - address_in_use = WSAEADDRINUSE, - address_not_available = WSAEADDRNOTAVAIL, - in_progress = WSAEINPROGRESS, - interrupted = WSAEINTR, - invalid = WSAEINVAL, - net_reset = WSAENETRESET, - not_connected = WSAENOTCONN, - no_buffers = WSAENOBUFS, - operation_not_supported = WSAEOPNOTSUPP, - not_socket = WSAENOTSOCK, - shutdown = WSAESHUTDOWN, - would_block = WSAEWOULDBLOCK, - connection_reset = WSAECONNRESET, - timed_out = WSAETIMEDOUT, - connection_aborted = WSAECONNABORTED, - message_size = WSAEMSGSIZE, - not_ready = WSAEALREADY, - no_support = WSAEAFNOSUPPORT, - connection_refused = WSAECONNREFUSED, - is_connected = WSAEISCONN, - net_unreachable = WSAENETUNREACH -#else - netdown = ENETDOWN, - fault = EFAULT, - access = EACCES, - address_in_use = EADDRINUSE, - address_not_available = EADDRNOTAVAIL, - in_progress = EINPROGRESS, - interrupted = EINTR, - invalid = EINVAL, - net_reset = ENETRESET, - not_connected = ENOTCONN, - no_buffers = ENOMEM, - operation_not_supported = EOPNOTSUPP, - not_socket = ENOTSOCK, - shutdown = ESHUTDOWN, - would_block = EAGAIN, - connection_reset = ECONNRESET, - timed_out = ETIMEDOUT, - connection_aborted = ECONNABORTED, - message_size = EMSGSIZE, - not_ready = EALREADY, - no_support = EAFNOSUPPORT, - connection_refused = ECONNREFUSED, - is_connected = EISCONN, - net_unreachable = ENETUNREACH -#endif + netdown, + fault, + access, + address_in_use, + address_not_available, + in_progress, + interrupted, + invalid, + net_reset, + not_connected, + no_buffers, + operation_not_supported, + not_socket, + shutdown, + would_block, + connection_reset, + timed_out, + connection_aborted, + message_size, + not_ready, + no_support, + connection_refused, + is_connected, + net_unreachable, + not_initialized, + unknown_error }; error_code last_error() const; diff --git a/include/libtorrent/stat.hpp b/include/libtorrent/stat.hpp index 0d1d6544c..85b75ceb9 100755 --- a/include/libtorrent/stat.hpp +++ b/include/libtorrent/stat.hpp @@ -58,6 +58,10 @@ namespace libtorrent std::fill(m_upload_per_second_history, m_upload_per_second_history+history, 0); } + // TODO: these function should take two arguments + // to be able to count both total data sent and also + // count only the actual payload (not counting the + // protocol chatter) void received_bytes(int num_bytes) { m_downloaded += num_bytes; m_total_download += num_bytes; } void sent_bytes(int num_bytes) diff --git a/include/libtorrent/storage.hpp b/include/libtorrent/storage.hpp index 5efc94144..cc6f7bb07 100755 --- a/include/libtorrent/storage.hpp +++ b/include/libtorrent/storage.hpp @@ -44,6 +44,7 @@ POSSIBILITY OF SUCH DAMAGE. #include #include +#include #include #include "libtorrent/entry.hpp" @@ -115,7 +116,7 @@ namespace libtorrent void reopen(); // the file itself - std::fstream m_file; + boost::filesystem::fstream m_file; // the mode with which this file was opened open_mode m_mode; diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index 9bae38470..64b9e883c 100755 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -87,6 +87,9 @@ namespace libtorrent void abort() { m_abort = true; m_event = event_stopped; } bool is_aborted() const { return m_abort; } + // is called every second by session. + void second_tick(); + // returns true if it time for this torrent to make another // tracker request bool should_request() const throw() @@ -96,9 +99,6 @@ namespace libtorrent return m_next_request < boost::posix_time::second_clock::local_time(); } - bool failed() const throw() { return !m_failed.empty(); } - const char* fail_reason() const throw() { return m_failed.c_str(); } - void print(std::ostream& os) const; void allocate_files(detail::piece_checker_data* data, @@ -114,9 +114,12 @@ namespace libtorrent torrent_status status() const; - void connect_to_peer(const address& a, const peer_id& id); + boost::weak_ptr connect_to_peer( + const address& a + , const peer_id& id); - const torrent_info& torrent_file() const throw() { return m_torrent_file; } + const torrent_info& torrent_file() const throw() + { return m_torrent_file; } policy& get_policy() { return *m_policy; } storage* filesystem() { return &m_storage; } @@ -128,11 +131,7 @@ namespace libtorrent // used by peer_connection to attach itself to a torrent // since incoming connections don't know what torrent // they're a part of until they have received an info_hash. - void attach_peer(peer_connection* p) - { - assert(std::find(m_connections.begin(), m_connections.end(), p) == m_connections.end()); - m_connections.push_back(p); - } + void attach_peer(peer_connection* p); // this will remove the peer and make sure all // the pieces it had have their reference counter @@ -142,9 +141,9 @@ namespace libtorrent // the number of peers that belong to this torrent int num_peers() const { return m_connections.size(); } - // returns the number of connections this torrent has to - // the given peer_id (should be kept at max 1) - int num_connections(const peer_id& id) const; + // 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::vector::iterator peer_iterator; typedef std::vector::const_iterator peer_const_iterator; @@ -270,7 +269,6 @@ namespace libtorrent // from the tracker int m_duration; - std::string m_failed; std::vector m_connections; // ----------------------------- @@ -285,6 +283,10 @@ namespace libtorrent int m_last_working_tracker; int m_currently_trying_tracker; + // this is a counter that is increased every + // second, and when it reaches 10, the policy::pulse() + // is called and the time scaler is reset to 0. + int m_time_scaler; }; } diff --git a/include/libtorrent/torrent_info.hpp b/include/libtorrent/torrent_info.hpp index b4b65912c..bdaa8cd0c 100755 --- a/include/libtorrent/torrent_info.hpp +++ b/include/libtorrent/torrent_info.hpp @@ -122,6 +122,8 @@ namespace libtorrent const std::string& name() const { return m_name; } void print(std::ostream& os) const; + void convert_file_names(); + entry::integer_type piece_size(unsigned int index) const { if (index == num_pieces()-1) diff --git a/src/alert.cpp b/src/alert.cpp index 0278714dd..134f7e55c 100755 --- a/src/alert.cpp +++ b/src/alert.cpp @@ -59,9 +59,9 @@ namespace libtorrent { { boost::mutex::scoped_lock lock(m_mutex); - assert(pending()); + assert(!m_alerts.empty()); - alert* result(m_alerts.front()); + alert* result = m_alerts.front(); m_alerts.pop(); return std::auto_ptr(result); } diff --git a/src/peer_connection.cpp b/src/peer_connection.cpp index 058bdc383..2369fb634 100755 --- a/src/peer_connection.cpp +++ b/src/peer_connection.cpp @@ -86,6 +86,7 @@ libtorrent::peer_connection::peer_connection( , m_selector(sel) , m_socket(s) , m_torrent(t) + , m_attached_to_torrent(true) , m_ses(ses) , m_active(true) , m_added_to_selector(false) @@ -130,6 +131,7 @@ libtorrent::peer_connection::peer_connection( , m_selector(sel) , m_socket(s) , m_torrent(0) + , m_attached_to_torrent(0) , m_ses(ses) , m_active(false) , m_added_to_selector(false) @@ -159,7 +161,11 @@ libtorrent::peer_connection::peer_connection( libtorrent::peer_connection::~peer_connection() { m_selector.remove(m_socket); - if (m_torrent) m_torrent->remove_peer(this); + if (m_attached_to_torrent) + { + assert(m_torrent != 0); + m_torrent->remove_peer(this); + } } void libtorrent::peer_connection::set_send_quota(int num_bytes) @@ -715,13 +721,17 @@ void libtorrent::peer_connection::send_have(int index) void libtorrent::peer_connection::receive_data() { assert(!m_socket->is_blocking()); + assert(m_packet_size > 0); for(;;) { -// m_socket->set_blocking(false); + assert(m_packet_size > 0); int received = m_socket->receive(&m_recv_buffer[m_recv_pos], m_packet_size - m_recv_pos); // connection closed - if (received == 0) throw network_error(0); + if (received == 0) + { + throw network_error(0); + } // an error if (received < 0) @@ -754,6 +764,14 @@ void libtorrent::peer_connection::receive_data() m_state = read_protocol_string; m_recv_buffer.resize(m_packet_size); m_recv_pos = 0; + + if (m_packet_size == 0) + { + #ifndef NDEBUG + (*m_logger) << "incorrect protocol length\n"; + #endif + throw network_error(0); + } break; @@ -765,7 +783,12 @@ void libtorrent::peer_connection::receive_data() 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)) + { + #ifndef NDEBUG + (*m_logger) << "incorrect protocol name\n"; + #endif throw network_error(0); + } m_state = read_info_hash; m_packet_size = 28; @@ -800,7 +823,6 @@ void libtorrent::peer_connection::receive_data() #endif throw network_error(0); } - m_torrent->attach_peer(this); // assume the other end has no pieces m_have_piece.resize(m_torrent->torrent_file().num_pieces()); @@ -855,13 +877,18 @@ void libtorrent::peer_connection::receive_data() // check to make sure we don't have another connection with the same // info_hash and peer_id. If we do. close this connection. 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 (m_torrent->has_peer(m_peer_id)) { #ifndef NDEBUG (*m_logger) << m_socket->sender().as_string() << " duplicate connection, closing\n"; #endif throw network_error(0); } + + m_attached_to_torrent = true; + m_torrent->attach_peer(this); + assert(m_torrent->get_policy().has_connection(this)); } m_state = read_packet_size; @@ -900,6 +927,7 @@ void libtorrent::peer_connection::receive_data() m_recv_buffer.resize(m_packet_size); } m_recv_pos = 0; + assert(m_packet_size > 0); break; case read_packet: @@ -917,11 +945,13 @@ void libtorrent::peer_connection::receive_data() m_packet_size = 4; m_recv_buffer.resize(4); m_recv_pos = 0; + assert(m_packet_size > 0); break; } } } } + assert(m_packet_size > 0); } diff --git a/src/piece_picker.cpp b/src/piece_picker.cpp index f6bdb586d..aa7a8fbaa 100755 --- a/src/piece_picker.cpp +++ b/src/piece_picker.cpp @@ -32,6 +32,7 @@ POSSIBILITY OF SUCH DAMAGE. #include #include +#include #include "libtorrent/piece_picker.hpp" @@ -40,8 +41,13 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/peer_connection.hpp" #endif -#if defined(_MSC_VER) +#if defined(_MSC_VER) && _MSC_VER < 1300 #define for if (false) {} else for +namespace std +{ + template + inline T min(T a, T b) { return arequested_blocks[block.block_index] = 1; } #ifndef NDEBUG - integrity_check(); +// integrity_check(); #endif } void piece_picker::mark_as_finished(piece_block block, const peer_id& peer) { #ifndef NDEBUG - integrity_check(); +// integrity_check(); #endif assert(block.piece_index < m_piece_map.size()); assert(block.block_index < blocks_in_piece(block.piece_index)); @@ -564,7 +570,7 @@ namespace libtorrent i->finished_blocks[block.block_index] = 1; } #ifndef NDEBUG - integrity_check(); +// integrity_check(); #endif } /* @@ -610,7 +616,7 @@ namespace libtorrent void piece_picker::abort_download(piece_block block) { #ifndef NDEBUG - integrity_check(); +// integrity_check(); #endif assert(block.piece_index < m_piece_map.size()); @@ -643,7 +649,7 @@ namespace libtorrent move(true, m_piece_map[block.piece_index].peer_count, m_piece_map[block.piece_index].index); } #ifndef NDEBUG - integrity_check(); +// integrity_check(); #endif } diff --git a/src/policy.cpp b/src/policy.cpp index c59a773e1..db72d29bd 100755 --- a/src/policy.cpp +++ b/src/policy.cpp @@ -32,6 +32,8 @@ POSSIBILITY OF SUCH DAMAGE. #include +#include + #include "libtorrent/policy.hpp" #include "libtorrent/torrent.hpp" #include "libtorrent/socket.hpp" @@ -137,7 +139,7 @@ namespace peer_connection* peer = 0; float down_speed = -1.f; // find the peer with the lowest download - // speed that also has a piece thatt this + // speed that also has a piece that this // peer could send us for (torrent::peer_iterator i = t.begin(); i != t.end(); @@ -171,6 +173,7 @@ namespace num_requests--; } + } namespace libtorrent @@ -197,14 +200,6 @@ namespace libtorrent TODO: to implement choking/unchoking we need a list with all connected peers. Something like this: - struct peer - { - peer_id id; - boost::posix_time::ptime last_optimistically_unchoked; - float average_down_rate; - boost::weak_ptr connection; - }; - */ @@ -213,21 +208,92 @@ namespace libtorrent , m_torrent(t) {} - - // this is called when a connection is made, before any - // handshake (it's possible to ban certain ip:s). - bool policy::accept_connection(const address& remote) + void policy::pulse() { - m_num_peers++; - return true; + using namespace boost::posix_time; + + // remove old disconnected peers from the list + m_peers.erase( + std::remove_if(m_peers.begin() + , m_peers.end() + , old_disconnected_peer()) + , m_peers.end()); + + // choke peers that have leeched too much without giving anything back + for (std::vector::iterator i = m_peers.begin(); i != m_peers.end(); ++i) + { + boost::shared_ptr c = i->connection.lock(); + if (c.get() == 0) continue; + + int downloaded = i->prev_amount_download + c->statistics().total_download(); + int uploaded = i->prev_amount_upload + c->statistics().total_upload(); + + if (uploaded - downloaded > m_torrent->torrent_file().piece_length() + && !c->is_choked()) + { + // if we have uploaded more than a piece for free, choke peer and + // wait until we catch up with our download. + c->choke(); + } + else if (uploaded - downloaded <= m_torrent->block_size() + && c->is_choked() && c->is_peer_interested()) + { + // we have catched up. We have now shared the same amount + // to eachother. Unchoke this peer. + c->unchoke(); + } + } + } + + void policy::new_connection(const boost::weak_ptr& c) + { + boost::shared_ptr con = c.lock(); + assert(con.get() != 0); + if (con.get() == 0) return; + + std::vector::iterator i + = std::find(m_peers.begin(), m_peers.end(), con->get_peer_id()); + if (i == m_peers.end()) + { + // we don't have ny info about this peer. + // add a new entry + peer p(con->get_peer_id()); + m_peers.push_back(p); + i = m_peers.end()-1; + } + else + { + assert(i->connection.expired()); + } + + i->connected = boost::posix_time::second_clock::local_time(); + i->connection = c; } void policy::peer_from_tracker(const address& remote, const peer_id& id) { try { - m_torrent->connect_to_peer(remote, id); - m_num_peers++; + std::vector::iterator i = std::find(m_peers.begin(), m_peers.end(), id); + if (i == m_peers.end()) + { + // we don't have ny info about this peer. + // add a new entry + peer p(id); + m_peers.push_back(p); + i = m_peers.end()-1; + } + else if (!i->connection.expired()) + { + // this means we're already connected + // to this peer. don't connect to + // it again. + return; + } + + i->connected = boost::posix_time::second_clock::local_time(); + i->connection = m_torrent->connect_to_peer(remote, id); + } catch(network_error&) {} } @@ -237,9 +303,9 @@ namespace libtorrent // anything for a while void policy::choked(peer_connection& c) { - c.choke(); } + // TODO: the peer_connection argument here should be removed. void policy::piece_finished(peer_connection& c, int index, bool successfully_verified) { // TODO: if verification failed, mark the peers that were involved @@ -248,8 +314,8 @@ namespace libtorrent void policy::block_finished(peer_connection& c, piece_block b) { - if (c.has_peer_choked()) return; - request_a_block(*m_torrent, c); + // if the peer hasn't choked us, ask for another piece + if (!c.has_peer_choked()) request_a_block(*m_torrent, c); } // this is called when we are unchoked by a peer @@ -257,21 +323,33 @@ namespace libtorrent // data from now on void policy::unchoked(peer_connection& c) { - c.unchoke(); - if (c.is_interesting()) request_a_block(*m_torrent, c); + if (c.is_interesting()) + { + request_a_block(*m_torrent, c); + } } void policy::interested(peer_connection& c) { - c.unchoke(); + // if we're interested in the peer, we unchoke it + // and hopes it will unchoke us too } void policy::not_interested(peer_connection& c) { } + // this is called whenever a peer connection is closed void policy::connection_closed(const peer_connection& c) { + std::vector::iterator i + = std::find(m_peers.begin(), m_peers.end(), c.get_peer_id()); + + assert(i != m_peers.end()); + + i->connected = boost::posix_time::second_clock::local_time(); + i->prev_amount_download += c.statistics().total_download(); + i->prev_amount_upload += c.statistics().total_upload(); } void policy::peer_is_interesting(peer_connection& c) @@ -280,4 +358,11 @@ namespace libtorrent if (c.has_peer_choked()) return; request_a_block(*m_torrent, c); } + +#ifndef NDEBUG + bool policy::has_connection(const peer_connection* p) + { + return std::find(m_peers.begin(), m_peers.end(), p->get_peer_id()) != m_peers.end(); + } +#endif } diff --git a/src/session.cpp b/src/session.cpp index 79228cef6..09d252d6d 100755 --- a/src/session.cpp +++ b/src/session.cpp @@ -105,7 +105,7 @@ namespace libtorrent std::make_pair(t->info_hash, t->torrent_ptr)).first; } } - catch(const boost::filesystem::filesystem_error& e) + catch(const std::exception& e) { #ifndef NDEBUG std::cerr << "error while checking files: " << e.what() << "\n"; @@ -206,40 +206,26 @@ namespace libtorrent m_selector.monitor_readability(listener); m_selector.monitor_errors(listener); - /* - // temp - const peer& p = *m_peer_list.begin(); - boost::shared_ptr s(new socket(socket::tcp, false)); - address a(p.ip, p.port); - s->connect(a); - - m_connections.insert(std::make_pair(s, peer_connection(this, s, p.id))); - m_selector.monitor_readability(s); - m_selector.monitor_errors(s); - // ~temp - */ - - std::vector > readable_clients; std::vector > writable_clients; std::vector > error_clients; boost::posix_time::ptime timer = boost::posix_time::second_clock::local_time(); +#ifndef NDEBUG + int loops_per_second = 0; +#endif 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)); - } + assert_invariant(); + loops_per_second++; #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"; + // << "sleeping\n"; m_selector.wait(500000, readable_clients, writable_clients, error_clients); boost::mutex::scoped_lock l(m_mutex); @@ -263,6 +249,9 @@ namespace libtorrent break; } +#ifndef NDEBUG + assert_invariant(); +#endif // ************************ // RECEIVE SOCKETS // ************************ @@ -286,18 +275,18 @@ namespace libtorrent // TODO: the send buffer size should be controllable from the outside // s->set_send_bufsize(2048); - // TODO: add some possibility to filter IP:s + // TODO: filter ip:s + boost::shared_ptr c( new peer_connection(this, m_selector, s)); + if (m_upload_rate != -1) c->set_send_quota(0); m_connections.insert(std::make_pair(s, c)); m_selector.monitor_readability(s); m_selector.monitor_errors(s); } - continue; } - connection_map::iterator p = m_connections.find(*i); if(p == m_connections.end()) { @@ -320,6 +309,10 @@ namespace libtorrent } } +#ifndef NDEBUG + assert_invariant(); +#endif + // ************************ // SEND SOCKETS // ************************ @@ -373,12 +366,7 @@ namespace libtorrent } #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)); - } + assert_invariant(); #endif boost::posix_time::time_duration d = boost::posix_time::second_clock::local_time() - timer; @@ -389,6 +377,11 @@ namespace libtorrent // THE SECTION BELOW IS EXECUTED ONCE EVERY SECOND // ************************ +#ifndef NDEBUG + // std::cout << "\n\nloops: " << loops_per_second << "\n"; + loops_per_second = 0; +#endif + // distribute the maximum upload rate among the peers // TODO: implement an intelligent algorithm that // will shift bandwidth from the peers that can't @@ -458,6 +451,8 @@ namespace libtorrent i->second->generate_tracker_request(m_listen_port), boost::get_pointer(i->second)); } + + i->second->second_tick(); ++i; } m_tracker_manager.tick(); @@ -527,6 +522,23 @@ namespace libtorrent } #endif +#ifndef NDEBUG + void session_impl::assert_invariant() + { + 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)); + if (i->second->associated_torrent()) + { + assert(i->second->associated_torrent() + ->get_policy().has_connection(boost::get_pointer(i->second))); + } + } + } +#endif + } session::session(int listen_port, const std::string& fingerprint) diff --git a/src/storage.cpp b/src/storage.cpp index f2a530e3e..b035161b1 100755 --- a/src/storage.cpp +++ b/src/storage.cpp @@ -156,7 +156,7 @@ void libtorrent::piece_file::reopen() m_file.close(); m_file.clear(); - m_file.open(path.native_file_string().c_str(), m_file_mode); + m_file.open(path, m_file_mode); if (m_mode == in) m_file.seekg(m_file_offset, std::ios_base::beg); else m_file.seekp(m_file_offset, std::ios_base::beg); @@ -242,7 +242,7 @@ void libtorrent::piece_file::open(storage* s, int index, open_mode o, int seek_o m_file.clear(); m_file_mode = m; - m_file.open(path.native_file_string().c_str(), m_file_mode); + m_file.open(path, m_file_mode); if (m_mode == in) m_file.seekg(m_file_offset, std::ios_base::beg); else m_file.seekp(m_file_offset, std::ios_base::beg); @@ -294,7 +294,7 @@ void libtorrent::piece_file::open(storage* s, int index, open_mode o, int seek_o m_file.clear(); m_file_mode = m; - m_file.open(p.native_file_string().c_str(), m_file_mode); + m_file.open(p, m_file_mode); // std::cout << "opening file: '" << p.native_file_string() << "'\n"; if (m_file.fail()) { @@ -373,6 +373,7 @@ int libtorrent::piece_file::read(char* buf, int size, bool lock_) left_to_read -= read_bytes; buf_pos += read_bytes; + assert(buf_pos >= 0); m_file_offset += read_bytes; m_piece_offset += read_bytes; @@ -385,7 +386,7 @@ int libtorrent::piece_file::read(char* buf, int size, bool lock_) m_file_offset = 0; m_file.close(); m_file.clear(); - m_file.open(path.native_file_string().c_str(), m_file_mode); + m_file.open(path, m_file_mode); } } @@ -413,11 +414,14 @@ int libtorrent::piece_file::read(char* buf, int size, bool lock_) int available = std::min(static_cast(m_file_iter->size - m_file_offset), static_cast(left_to_read)); + assert(buf_pos >= 0); m_file.read(buf + buf_pos, available); int read = m_file.gcount(); + assert(read > 0); left_to_read -= read; read_total += read; buf_pos += read; + assert(buf_pos >= 0); m_file_offset += read; m_piece_offset += read; @@ -432,7 +436,7 @@ int libtorrent::piece_file::read(char* buf, int size, bool lock_) m_file_offset = 0; m_file.close(); m_file.clear(); - m_file.open(path.native_file_string().c_str(), m_file_mode); + m_file.open(path, m_file_mode); // std::cout << "opening file: '" << path.native_file_string() << "'\n"; if (m_file.fail()) { @@ -503,10 +507,13 @@ void libtorrent::piece_file::write(const char* buf, int size, bool lock_) if (m_file_offset + write_bytes > m_file_iter->size) write_bytes = m_file_iter->size - m_file_offset; + assert(buf_pos >= 0); + assert(write_bytes > 0); m_file.write(buf + buf_pos, write_bytes); left_to_write -= write_bytes; buf_pos += write_bytes; + assert(buf_pos >= 0); m_file_offset += write_bytes; m_piece_offset += write_bytes; @@ -522,12 +529,12 @@ void libtorrent::piece_file::write(const char* buf, int size, bool lock_) m_file_offset = 0; m_file.close(); m_file.clear(); - m_file.open(path.native_file_string().c_str(), m_file_mode); + m_file.open(path, m_file_mode); } } #if 0 // old implementation - + /* assert(m_mode == out); int left_to_write = size; @@ -558,7 +565,7 @@ void libtorrent::piece_file::write(const char* buf, int size, bool lock_) m_file_offset = 0; m_file.close(); - m_file.open(path.native_file_string().c_str(), m_file_mode); + m_file.open(path, m_file_mode); // std::cout << "opening file: '" << path.native_file_string() << "'\n"; if (m_file.fail()) { @@ -567,7 +574,7 @@ void libtorrent::piece_file::write(const char* buf, int size, bool lock_) } } } while (left_to_write > 0); - +*/ #endif } @@ -608,7 +615,7 @@ void libtorrent::piece_file::seek_forward(int step, bool lock_) path /= m_file_iter->filename; m_file.close(); - m_file.open(path.native_file_string().c_str(), std::ios_base::in | std::ios_base::binary); + m_file.open(path, std::ios_base::in | std::ios_base::binary); } m_file_offset += left_to_seek; @@ -771,9 +778,9 @@ void libtorrent::storage::allocate_pieces(int num) fs::ofstream out; if (fs::exists(path)) - out.open(path.native_file_string().c_str(), std::ios_base::binary | std::ios_base::in); + out.open(path, std::ios_base::binary | std::ios_base::in); else - out.open(path.native_file_string().c_str(), std::ios_base::binary); + out.open(path, std::ios_base::binary); // std::ofstream out((m_save_path / file_iter->path / file_iter->filename).native_file_string().c_str() // , std::ios_base::binary | std::ios_base::in); @@ -831,7 +838,7 @@ entry::integer_type libtorrent::storage::piece_storage(int piece) if (m_free_pieces.empty()) { - allocate_pieces(5000); + allocate_pieces(5); assert(!m_free_pieces.empty()); } @@ -1182,7 +1189,7 @@ void libtorrent::storage::initialize_pieces(torrent* t, { in.close(); in.clear(); - in.open(path.native_file_string().c_str(), std::ios_base::binary); + in.open(path, std::ios_base::binary); changed_file = false; diff --git a/src/torrent.cpp b/src/torrent.cpp index c2abdb712..699782afe 100755 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -128,6 +128,13 @@ namespace return true; } + bool operator()(const peer_connection* p) const + { + if (p->get_peer_id() != id) return false; + if (tor != p->associated_torrent()) return false; + return true; + } + const peer_id& id; const torrent* tor; }; @@ -151,6 +158,7 @@ namespace libtorrent (torrent_file.total_size()+m_block_size-1)/m_block_size) , m_last_working_tracker(0) , m_currently_trying_tracker(0) + , m_time_scaler(0) { } @@ -219,16 +227,17 @@ namespace libtorrent } - int torrent::num_connections(const peer_id& id) const + bool torrent::has_peer(const peer_id& id) const { - int num = 0; - for (detail::session_impl::connection_map::const_iterator i = m_ses->m_connections.begin(); - i != m_ses->m_connections.end(); - ++i) - { - if (i->second->get_peer_id() == id && i->second->associated_torrent() == this) ++num; - } - return num; + assert(std::count_if(m_connections.begin() + , m_connections.end() + , find_peer(id, this)) <= 1); + + return std::find_if( + m_connections.begin() + , m_connections.end() + , find_peer(id, this)) + != m_connections.end(); } void torrent::announce_piece(int index) @@ -236,10 +245,6 @@ namespace libtorrent m_picker.we_have(index); for (std::vector::iterator i = m_connections.begin(); i != m_connections.end(); ++i) (*i)->announce_piece(index); - - #ifndef NDEBUG - m_picker.integrity_check(this); - #endif } std::string torrent::generate_tracker_request(int port) @@ -335,7 +340,7 @@ namespace libtorrent #endif } - void torrent::connect_to_peer(const address& a, const peer_id& id) + boost::weak_ptr torrent::connect_to_peer(const address& a, const peer_id& id) { boost::shared_ptr s(new socket(socket::tcp, false)); // TODO: the send buffer size should be controllable from the outside @@ -350,10 +355,30 @@ namespace libtorrent if (m_ses->m_upload_rate != -1) c->set_send_quota(0); 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)); + + // add the newly connected peer to this torrent's peer list + assert(std::find(m_connections.begin() + , m_connections.end() + , boost::get_pointer(p->second)) + == m_connections.end()); + + m_connections.push_back(boost::get_pointer(p->second)); + m_ses->m_selector.monitor_readability(s); m_ses->m_selector.monitor_errors(s); // std::cout << "connecting to: " << a.as_string() << ":" << a.port() << "\n"; + return c; + } + + void torrent::attach_peer(peer_connection* p) + { + assert(std::find(m_connections.begin(), m_connections.end(), p) == m_connections.end()); + m_connections.push_back(p); + detail::session_impl::connection_map::iterator i + = m_ses->m_connections.find(p->get_socket()); + assert(i != m_ses->m_connections.end()); + + m_policy->new_connection(i->second); } void torrent::close_all_connections() @@ -412,6 +437,16 @@ namespace libtorrent #endif } + void torrent::second_tick() + { + m_time_scaler++; + if (m_time_scaler >= 10) + { + m_time_scaler = 0; + m_policy->pulse(); + } + } + torrent_status torrent::status() const { torrent_status st; diff --git a/src/torrent_handle.cpp b/src/torrent_handle.cpp index 77e620db2..0388f54ae 100755 --- a/src/torrent_handle.cpp +++ b/src/torrent_handle.cpp @@ -165,6 +165,8 @@ namespace libtorrent p.id = peer->get_peer_id(); p.ip = peer->get_socket()->sender(); + // TODO: add the prev_amount_downloaded and prev_amount_uploaded + // from the peer list in the policy p.total_download = statistics.total_download(); p.total_upload = statistics.total_upload(); @@ -172,7 +174,7 @@ namespace libtorrent p.flags = 0; if (peer->is_interesting()) p.flags |= peer_info::interesting; - if (peer->has_choked()) p.flags |= peer_info::choked; + if (peer->is_choked()) p.flags |= peer_info::choked; if (peer->is_peer_interested()) p.flags |= peer_info::remote_interested; if (peer->has_peer_choked()) p.flags |= peer_info::remote_choked; diff --git a/src/torrent_info.cpp b/src/torrent_info.cpp index a343ce1a7..14bc241ff 100755 --- a/src/torrent_info.cpp +++ b/src/torrent_info.cpp @@ -204,6 +204,35 @@ namespace libtorrent std::copy(hash_string.begin() + i*20, hash_string.begin() + (i+1)*20, m_piece_hash[i].begin()); } + void torrent_info::convert_file_names() + { + for (std::vector::iterator i = m_files.begin(); i != m_files.end(); ++i) + { + // replace all dots in directory names with underscores + std::string& path = i->path; + std::string& filename = i->filename; + for (std::string::iterator c = path.begin(); c != path.end(); ++c) + { + if (*c == '.') *c = '_'; + if (*c == ' ') *c = '_'; + if (*c == '[') *c = '_'; + if (*c == ']') *c = '_'; + } + + // replace all dots, but the last one, + // in file names with underscores + std::string::reverse_iterator last_dot + = std::find(filename.rbegin(), filename.rend(), '.'); + for (std::string::reverse_iterator c = filename.rbegin(); c != filename.rend(); ++c) + { + if (c != last_dot && *c == '.') *c = '_'; + if (*c == ' ') *c = '_'; + if (*c == '[') *c = '_'; + if (*c == ']') *c = '_'; + } + } + } + int torrent_info::prioritize_tracker(int index) { if (index > m_urls.size()) return m_urls.size()-1;