diff --git a/docs/manual.html b/docs/manual.html index 2d1e5d2b9..436d6c89f 100755 --- a/docs/manual.html +++ b/docs/manual.html @@ -135,13 +135,13 @@ boost.filesystem, boost.date_time and various other boost libraries as well as z
  • Windows 2000 vc7.1
  • Linux x86 (debian) GCC 3.0.4, GCC 3.2.3
  • Windows 2000, msvc6 sp5 (does not support 64-bit values due to problems with operator<<(ostream&, __int64))
  • -
  • Cygwin GCC 3.3.1
  • Fails on:

    libtorrent is released under the BSD-license.

    @@ -166,10 +166,7 @@ bjam <toolset> "force conformance in for loop scope" and "treat wchar_t as built-in type" to Yes.

    If you're building in developer studio 6, you will probably have to use the previous version of boost, boost 1.30.2.

    -

    There are two versions of the socket code, one that works with unix systems (and bsd-sockets) and -one that uses winsock. If you're building in windows, the file socket_win.cpp is supposed to -be included in the build while socket_bsd.cpp is supposed to be excluded.

    -

    The file abstraction has the same kind of separation. There's one file_win.cpp which +

    There are two versions of the file abstraction. There's one file_win.cpp which relies on windows file API that supports files larger than 2 Gigabytes. This does not work in vc6 for some reason, possibly because it may require windows NT and above. The other file, file.cpp is the default implementation that simply relies on the standard library's fstream, @@ -249,8 +246,12 @@ the session, it contains the m

     class session: public boost::noncopyable
     {
    -        session(std::pair<int, int> listen_port_range, const fingerprint& print);
    +
             session(std::pair<int, int> listen_port_range);
    +        session(
    +                std::pair<int, int> listen_port_range
    +                , const fingerprint& print
    +                , const char* listen_interface = 0);
     
             torrent_handle add_torrent(
                     const torrent_info& t
    @@ -262,6 +263,13 @@ class session: public boost::noncopyable
             void set_http_settings(const http_settings& settings);
             void set_upload_rate_limit(int bytes_per_second);
     
    +        bool is_listening() const;
    +        unsigned short listen_port() const;
    +        bool listen_on(
    +                std::pair<int, int> const& port_range
    +                , const char* interface = 0);
    +
    +
             std::auto_ptr<alert> pop_alert();
             void set_severity_level(alert::severity_t s);
     
    @@ -290,6 +298,21 @@ 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).

    +

    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.

    +

    listen_port() returns the port we ended up listening on. Since you just pass +a port-range to the constructor and to listen_on(), to know which port it +ended up using, you have to ask the session using this function.

    +

    listen_on() will change the listen port and/or the listen interface. If the +session is already listening on a port, this socket will be closed and a new socket +will be opened with these new settings. The port range is the ports it will try +to listen on, if the first port fails, it will continue trying the next port within +the range and so on. The interface parameter can be left as 0, in that case the +os will decide which interface to listen on, otherwise it should be the ip-address +of the interface you want the listener socket bound to. listen_on() returns true +if it managed to open the socket, and false if it failed. If it fails, it will also +generate an appropriate alert (listen_failed_alert).

    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 @@ -302,7 +325,11 @@ increase the port number by one and try again. If it still fails it will continu increasing the port number until it succeeds or has reached the end of the range. If it fails with all ports, a listen_failed_alert will be posted and the session thread will exit. The only thing you can do with your session if this alert is posted is to destruct -it and possibly try again or change the port range.

    +it and possibly try again or change the port range. The listen interaface string is +the name (ip address) of the interface you want to listen on. If this is left as +0, the os will decide which interface to listen on (works in most cases). All torrents +will use this interface to open outgoing connections on by default. You can change +which interface to use for outgoing connections on a per torrent basis. See torrent_handle.

    For information about the pop_alert() function, see alerts.

    @@ -535,6 +562,8 @@ struct torrent_handle void set_tracker_login(std::string const& username, std::string const& password); + void use_interface(const char* net_interface); + boost::filsystem::path save_path() const; void set_max_uploads(int max_uploads); @@ -569,6 +598,8 @@ attempt to upload in return for each download. e.g. if set to 2, the client will as a standard client.

    set_tracker_login() sets a username and password that will be sent along in the HTTP-request of the tracker announce. Set this if the tracker requires authorization.

    +

    use_interface() sets the network interface this torrent will use when it opens outgoing +connections. By default, it uses the same interface as the session uses to listen on.

    info_hash() returns the info hash for the torrent.

    set_max_uploads() sets the maximum number of peers that's unchoked at the same time on this torrent. If you set this to -1, there will be no limit.

    @@ -1089,8 +1120,8 @@ user in different ways.

    listen_failed_alert

    This alert is generated when none of the ports, given in the port range, to -session can be opened for listening. Without a listening port the session -object will exit its thread. This alert is generated as severity level fatal.

    +session can be opened for listening. This alert is generated as severity +level fatal.

     struct listen_failed_alert: alert
     {
    diff --git a/docs/manual.rst b/docs/manual.rst
    index 717f5b7e0..d263f8ef9 100755
    --- a/docs/manual.rst
    +++ b/docs/manual.rst
    @@ -202,6 +202,13 @@ The ``session`` class has the following synopsis::
     		void set_http_settings(const http_settings& settings);
     		void set_upload_rate_limit(int bytes_per_second);
     
    +		bool is_listening() const;
    +		unsigned short listen_port() const;
    +		bool listen_on(
    +			std::pair const& port_range
    +			, const char* interface = 0);
    +
    +
     		std::auto_ptr pop_alert();
     		void set_severity_level(alert::severity_t s);
     
    @@ -236,6 +243,24 @@ fingerprint class.
     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).
     
    +``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.
    +
    +``listen_port()`` returns the port we ended up listening on. Since you just pass
    +a port-range to the constructor and to ``listen_on()``, to know which port it
    +ended up using, you have to ask the session using this function.
    +
    +``listen_on()`` will change the listen port and/or the listen interface. If the
    +session is already listening on a port, this socket will be closed and a new socket
    +will be opened with these new settings. The port range is the ports it will try
    +to listen on, if the first port fails, it will continue trying the next port within
    +the range and so on. The interface parameter can be left as 0, in that case the
    +os will decide which interface to listen on, otherwise it should be the ip-address
    +of the interface you want the listener socket bound to. ``listen_on()`` returns true
    +if it managed to open the socket, and false if it failed. If it fails, it will also
    +generate an appropriate alert (listen_failed_alert_).
    +
     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
    @@ -1136,8 +1161,8 @@ listen_failed_alert
     -------------------
     
     This alert is generated when none of the ports, given in the port range, to
    -session_ can be opened for listening. Without a listening port the session
    -object will exit its thread. This alert is generated as severity level ``fatal``.
    +session_ can be opened for listening. This alert is generated as severity
    +level ``fatal``.
     
     ::
     
    diff --git a/include/libtorrent/session.hpp b/include/libtorrent/session.hpp
    index e30805c04..88d07df52 100755
    --- a/include/libtorrent/session.hpp
    +++ b/include/libtorrent/session.hpp
    @@ -139,7 +139,7 @@ namespace libtorrent
     			// the torrent is added to the session
     			session_impl& m_ses;
     
    -			boost::mutex m_mutex;
    +			mutable boost::mutex m_mutex;
     			boost::condition m_cond;
     
     			// a list of all torrents that are currently checking
    @@ -164,9 +164,11 @@ namespace libtorrent
     
     			void operator()();
     
    +			void open_listen_port();
    +
     			// must be locked to access the data
     			// in this struct
    -			boost::mutex m_mutex;
    +			mutable boost::mutex m_mutex;
     			torrent* find_torrent(const sha1_hash& info_hash);
     			const peer_id& get_peer_id() const { return m_peer_id; }
     
    @@ -202,6 +204,8 @@ namespace libtorrent
     			// them
     			selector m_selector;
     
    +			boost::shared_ptr m_listen_socket;
    +
     			// the settings for the client
     			http_settings m_settings;
     
    @@ -257,8 +261,25 @@ namespace libtorrent
     			, const boost::filesystem::path& save_path
     			, const entry& resume_data = entry());
     
    -		// TODO: add the ability to change listen-port on the fly
    -		// TODO: make it possible to ask for the current listen port
    +		bool is_listening() const;
    +
    +		// if the listen port failed in some way
    +		// you can retry to listen on another port-
    +		// range with this function. If the listener
    +		// succeeded and is currently listening,
    +		// a call to this function will shut down the
    +		// listen port and reopen it using these new
    +		// properties (the given interface and port range).
    +		// As usual, if the interface is left as 0
    +		// this function will return false on failure.
    +		// If it fails, it will also generate alerts describing
    +		// the error. It will return true on success.
    +		bool listen_on(
    +			std::pair const& port_range
    +			, const char* interface = 0);
    +
    +		// returns the port we ended up listening on
    +		unsigned short listen_port() const;
     
     		void remove_torrent(const torrent_handle& h);
     
    diff --git a/include/libtorrent/socket.hpp b/include/libtorrent/socket.hpp
    index d0cffe547..1a16db550 100755
    --- a/include/libtorrent/socket.hpp
    +++ b/include/libtorrent/socket.hpp
    @@ -52,9 +52,6 @@ POSSIBILITY OF SUCH DAMAGE.
     #include 
     
     // TODO: support ToS
    -// TODO: support binding to selected interface
    -// Distinguish between binding listen sockets and
    -// outgoing connections.
     
     namespace libtorrent
     {
    @@ -102,8 +99,8 @@ namespace libtorrent
     
     		unsigned short port;
     
    -		const static unsigned short any_port = 0;
    -		const static unsigned int any_addr = 0;
    +		BOOST_STATIC_CONSTANT(unsigned short, any_port = 0);
    +		BOOST_STATIC_CONSTANT(unsigned int, any_addr = 0);
     
     	private:
     
    diff --git a/include/libtorrent/torrent_handle.hpp b/include/libtorrent/torrent_handle.hpp
    index 2bd415af9..6de861c18 100755
    --- a/include/libtorrent/torrent_handle.hpp
    +++ b/include/libtorrent/torrent_handle.hpp
    @@ -169,7 +169,7 @@ namespace libtorrent
     		// (make a rerequest from the tracker)
     		void force_reannounce() const;
     
    -		// TODO: add a feature where the user can ask the torrent
    +		// TODO: add a feature where the user can tell the torrent
     		// to finish all pieces currently in the pipeline, and then
     		// abort the torrent.
     
    diff --git a/src/allocate_resources.cpp b/src/allocate_resources.cpp
    index 97320af63..41f246272 100644
    --- a/src/allocate_resources.cpp
    +++ b/src/allocate_resources.cpp
    @@ -36,6 +36,10 @@ POSSIBILITY OF SUCH DAMAGE.
     #include 
     #include 
     
    +#if defined(_MSC_VER) && _MSC_VER < 1300
    +#define for if (false) {} else for
    +#endif
    +
     namespace libtorrent
     {
     	namespace
    diff --git a/src/session.cpp b/src/session.cpp
    index 0c0c6ecc0..9401a725c 100755
    --- a/src/session.cpp
    +++ b/src/session.cpp
    @@ -160,159 +160,153 @@ namespace
     */
     }
     
    -namespace libtorrent
    +namespace libtorrent { namespace detail
     {
    -	namespace detail
    +	void checker_impl::operator()()
     	{
    -		void checker_impl::operator()()
    +		eh_initializer();
    +		for (;;)
     		{
    -			eh_initializer();
    -			for (;;)
    +			piece_checker_data* t;
     			{
    -				piece_checker_data* t;
    -				{
    -					boost::mutex::scoped_lock l(m_mutex);
    -
    -					// if the job queue is empty and
    -					// we shouldn't abort
    -					// wait for a signal
    -					if (m_torrents.empty() && !m_abort)
    -						m_cond.wait(l);
    -
    -					if (m_abort) return;
    -
    -					assert(!m_torrents.empty());
    -					
    -					t = &m_torrents.front();
    -					if (t->abort)
    -					{
    -						m_torrents.pop_front();
    -						continue;
    -					}
    -				}
    -
    -				try
    -				{
    -					assert(t != 0);
    -					t->torrent_ptr->check_files(*t, m_mutex);
    -					// lock the session to add the new torrent
    -
    -					boost::mutex::scoped_lock l(m_mutex);
    -					if (!t->abort)
    -					{
    -						boost::mutex::scoped_lock l(m_ses.m_mutex);
    -
    -						m_ses.m_torrents.insert(
    -							std::make_pair(t->info_hash, t->torrent_ptr)).first;
    -
    -						peer_id id;
    -						std::fill(id.begin(), id.end(), 0);
    -						for (std::vector
    ::const_iterator i = t->peers.begin(); - i != t->peers.end(); - ++i) - { - t->torrent_ptr->get_policy().peer_from_tracker(*i, id); - } - } - } - catch(const std::exception& e) - { - // TODO: generate an alert here! -#ifndef NDEBUG - std::cerr << "error while checking files: " << e.what() << "\n"; -#endif - } - catch(...) - { -#ifndef NDEBUG - std::cerr << "error while checking files\n"; -#endif - } - - // remove ourself from the 'checking'-list - // (we're no longer in the checking state) boost::mutex::scoped_lock l(m_mutex); - m_torrents.pop_front(); + + // if the job queue is empty and + // we shouldn't abort + // wait for a signal + if (m_torrents.empty() && !m_abort) + m_cond.wait(l); + + if (m_abort) return; + + assert(!m_torrents.empty()); + + t = &m_torrents.front(); + if (t->abort) + { + m_torrents.pop_front(); + continue; + } } - } - - detail::piece_checker_data* checker_impl::find_torrent(const sha1_hash& info_hash) - { - for (std::deque::iterator i - = m_torrents.begin(); - i != m_torrents.end(); - ++i) - { - if (i->info_hash == info_hash) return &(*i); - } - return 0; - } - - session_impl::session_impl( - std::pair listen_port_range - , const fingerprint& cl_fprint - , const char* listen_interface = 0) - : m_tracker_manager(m_settings) - , m_listen_port_range(listen_port_range) - , m_listen_interface(listen_interface, listen_port_range.first) - , m_abort(false) - , m_upload_rate(-1) - , m_incoming_connection(false) - { - assert(listen_port_range.first > 0); - assert(listen_port_range.first < listen_port_range.second); - assert(m_listen_interface.port > 0); - - // ---- generate a peer id ---- - - std::srand((unsigned int)std::time(0)); - - std::string print = cl_fprint.to_string(); - assert(print.length() == 8); - - // the client's fingerprint - std::copy( - print.begin() - , print.begin() + print.length() - , m_peer_id.begin()); - - // the random number - for (unsigned char* i = m_peer_id.begin() + print.length(); - i != m_peer_id.end(); - ++i) - { - *i = rand(); - } - } - - void session_impl::purge_connections() - { - while (!m_disconnect_peer.empty()) - { - m_connections.erase(m_disconnect_peer.back()); - m_disconnect_peer.pop_back(); - assert(m_selector.count_read_monitors() == (int)m_connections.size() + 1); - } - } - - void session_impl::operator()() - { - eh_initializer(); -#ifndef NDEBUG - m_logger = create_log("main session"); try { + assert(t != 0); + t->torrent_ptr->check_files(*t, m_mutex); + // lock the session to add the new torrent + + boost::mutex::scoped_lock l(m_mutex); + if (!t->abort) + { + boost::mutex::scoped_lock l(m_ses.m_mutex); + + m_ses.m_torrents.insert( + std::make_pair(t->info_hash, t->torrent_ptr)).first; + + peer_id id; + std::fill(id.begin(), id.end(), 0); + for (std::vector
    ::const_iterator i = t->peers.begin(); + i != t->peers.end(); + ++i) + { + t->torrent_ptr->get_policy().peer_from_tracker(*i, id); + } + } + } + catch(const std::exception& e) + { + // TODO: generate an alert here! + // This will happen if the storage fails to initialize +#ifndef NDEBUG + std::cerr << "error while checking files: " << e.what() << "\n"; #endif + } + catch(...) + { +#ifndef NDEBUG + std::cerr << "error while checking files\n"; +#endif + } + + // remove ourself from the 'checking'-list + // (we're no longer in the checking state) + boost::mutex::scoped_lock l(m_mutex); + m_torrents.pop_front(); + } + } + + detail::piece_checker_data* checker_impl::find_torrent(const sha1_hash& info_hash) + { + for (std::deque::iterator i + = m_torrents.begin(); + i != m_torrents.end(); + ++i) + { + if (i->info_hash == info_hash) return &(*i); + } + return 0; + } + + session_impl::session_impl( + std::pair listen_port_range + , const fingerprint& cl_fprint + , const char* listen_interface = 0) + : m_tracker_manager(m_settings) + , m_listen_port_range(listen_port_range) + , m_listen_interface(listen_interface, listen_port_range.first) + , m_abort(false) + , m_upload_rate(-1) + , m_incoming_connection(false) + { + assert(listen_port_range.first > 0); + assert(listen_port_range.first < listen_port_range.second); + assert(m_listen_interface.port > 0); + + // ---- generate a peer id ---- + + std::srand((unsigned int)std::time(0)); + + std::string print = cl_fprint.to_string(); + assert(print.length() == 8); + + // the client's fingerprint + std::copy( + print.begin() + , print.begin() + print.length() + , m_peer_id.begin()); + + // the random number + for (unsigned char* i = m_peer_id.begin() + print.length(); + i != m_peer_id.end(); + ++i) + { + *i = rand(); + } + } + + void session_impl::purge_connections() + { + while (!m_disconnect_peer.empty()) + { + m_connections.erase(m_disconnect_peer.back()); + m_disconnect_peer.pop_back(); + assert(m_selector.count_read_monitors() == (int)m_connections.size() + 1); + } + } + + void session_impl::open_listen_port() + { + try + { // create listener socket - // TODO: if this fails, no alert is generated - boost::shared_ptr listener(new socket(socket::tcp, false)); + m_listen_socket = boost::shared_ptr(new socket(socket::tcp, false)); for(;;) { try { - listener->listen(m_listen_interface, 10); + m_listen_socket->listen(m_listen_interface, 10); + break; } catch (network_error& e) { @@ -320,435 +314,474 @@ namespace libtorrent { std::string msg = "cannot listen on the given interface '" + m_listen_interface.as_string() + "'"; m_alerts.post_alert(listen_failed_alert(msg)); - return; +#ifndef NDEBUG + (*m_logger) << msg << "\n"; +#endif + m_listen_socket.reset(); + break; } m_listen_interface.port++; if (m_listen_interface.port > m_listen_port_range.second) { - m_alerts.post_alert(listen_failed_alert( - "none of the ports in the given range could be opened")); - return; + std::stringstream msg; + msg << "none of the ports in the range [" + << m_listen_port_range.first + << ", " << m_listen_port_range.second + << "] could be opened for listening"; + m_alerts.post_alert(listen_failed_alert(msg.str())); +#ifndef NDEBUG + (*m_logger) << msg.str() << "\n"; +#endif + m_listen_socket.reset(); + break; } - continue; } + } + } + catch (network_error& e) + { + m_alerts.post_alert(listen_failed_alert(e.what())); + } + +#ifndef NDEBUG + if (m_listen_socket) + { + (*m_logger) << "listening on port: " << m_listen_interface.port << "\n"; + } +#endif + if (m_listen_socket) + { + m_selector.monitor_readability(m_listen_socket); + m_selector.monitor_errors(m_listen_socket); + } + } + + + void session_impl::operator()() + { + eh_initializer(); +#ifndef NDEBUG + m_logger = create_log("main session"); + + try + { +#endif + + open_listen_port(); + + 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 + check_invariant("loops_per_second++"); + loops_per_second++; +#endif + + + // if nothing happens within 500000 microseconds (0.5 seconds) + // do the loop anyway to check if anything else has changed + // << "sleeping\n"; + m_selector.wait(500000, readable_clients, writable_clients, error_clients); + +#ifndef NDEBUG + for (std::vector >::iterator i = + writable_clients.begin(); + i != writable_clients.end(); + ++i) + { + assert((*i)->is_writable()); + } +#endif + boost::mutex::scoped_lock l(m_mutex); + + // +1 for the listen socket + assert(m_selector.count_read_monitors() == (int)m_connections.size() + 1); + + if (m_abort) + { + m_tracker_manager.abort_all_requests(); + for (std::map >::iterator i = + m_torrents.begin(); + i != m_torrents.end(); + ++i) + { + i->second->abort(); + m_tracker_manager.queue_request(i->second->generate_tracker_request(m_listen_interface.port)); + } + m_connections.clear(); + m_torrents.clear(); break; } #ifndef NDEBUG - (*m_logger) << "listening on port: " << m_listen_interface.port << "\n"; + check_invariant("before SEND SOCKETS"); #endif - m_selector.monitor_readability(listener); - m_selector.monitor_errors(listener); - std::vector > readable_clients; - std::vector > writable_clients; - std::vector > error_clients; - boost::posix_time::ptime timer = boost::posix_time::second_clock::local_time(); + // ************************ + // SEND SOCKETS + // ************************ -#ifndef NDEBUG - int loops_per_second = 0; -#endif - for(;;) + // let the writable clients send data + for (std::vector >::iterator i + = writable_clients.begin(); + i != writable_clients.end(); + ++i) { - -#ifndef NDEBUG - check_invariant("loops_per_second++"); - loops_per_second++; -#endif - - - // if nothing happens within 500000 microseconds (0.5 seconds) - // do the loop anyway to check if anything else has changed - // << "sleeping\n"; - m_selector.wait(500000, readable_clients, writable_clients, error_clients); - -#ifndef NDEBUG - for (std::vector >::iterator i = - writable_clients.begin(); - i != writable_clients.end(); - ++i) + assert((*i)->is_writable()); + connection_map::iterator p = m_connections.find(*i); + // the connection may have been disconnected in the receive phase + if (p == m_connections.end()) { - assert((*i)->is_writable()); - } -#endif - boost::mutex::scoped_lock l(m_mutex); - - // +1 for the listen socket - assert(m_selector.count_read_monitors() == (int)m_connections.size() + 1); - - if (m_abort) - { - m_tracker_manager.abort_all_requests(); - for (std::map >::iterator i = - m_torrents.begin(); - i != m_torrents.end(); - ++i) - { - i->second->abort(); - m_tracker_manager.queue_request(i->second->generate_tracker_request(m_listen_interface.port)); - } - m_connections.clear(); - m_torrents.clear(); - break; - } - -#ifndef NDEBUG - check_invariant("before SEND SOCKETS"); -#endif - - // ************************ - // SEND SOCKETS - // ************************ - - // let the writable clients send data - for (std::vector >::iterator i - = writable_clients.begin(); - i != writable_clients.end(); - ++i) - { - assert((*i)->is_writable()); - connection_map::iterator p = m_connections.find(*i); - // the connection may have been disconnected in the receive phase - if (p == m_connections.end()) - { - m_selector.remove(*i); - } - else - { - try - { - assert(m_selector.is_writability_monitored(p->first)); - assert(p->second->has_data()); - assert(p->second->get_socket()->is_writable()); - p->second->send_data(); - } - catch (file_error& e) - { - torrent* t = p->second->associated_torrent(); - assert(t != 0); - - if (m_alerts.should_post(alert::fatal)) - { - m_alerts.post_alert( - file_error_alert( - t->get_handle() - , e.what())); - } - - m_selector.remove(*i); - m_connections.erase(p); - assert(m_selector.count_read_monitors() == (int)m_connections.size() + 1); - t->abort(); - } - catch (std::exception& e) - { - // the connection wants to disconnect for some reason, - // remove it from the connection-list - if (m_alerts.should_post(alert::debug)) - { - m_alerts.post_alert( - peer_error_alert(p->first->sender(), e.what())); - } - - m_selector.remove(*i); - m_connections.erase(p); - assert(m_selector.count_read_monitors() == (int)m_connections.size() + 1); - } - } - } - purge_connections(); - -#ifndef NDEBUG - check_invariant("after SEND SOCKETS"); -#endif - // ************************ - // RECEIVE SOCKETS - // ************************ - - // let the readable clients receive data - for (std::vector >::iterator i = readable_clients.begin(); - i != readable_clients.end(); - ++i) - { - // special case for listener socket - if (*i == listener) - { - boost::shared_ptr s = (*i)->accept(); - if (s) - { - s->set_blocking(false); - // we got a connection request! - m_incoming_connection = true; -#ifndef NDEBUG - (*m_logger) << s->sender().as_string() << " <== INCOMING CONNECTION\n"; -#endif - // TODO: filter ip:s - - boost::shared_ptr c( - new peer_connection(*this, m_selector, s)); - - if (m_upload_rate != -1) - { - c->upload_bandwidth.given = 0; - c->update_send_quota_left(); - } - - 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()) - { - m_selector.remove(*i); - } - else - { - try - { -// (*m_logger) << "readable: " << p->first->sender().as_string() << "\n"; - p->second->receive_data(); - } - catch (file_error& e) - { - torrent* t = p->second->associated_torrent(); - assert(t != 0); - - if (m_alerts.should_post(alert::fatal)) - { - m_alerts.post_alert( - file_error_alert( - t->get_handle() - , e.what())); - } - - m_selector.remove(*i); - m_connections.erase(p); - assert(m_selector.count_read_monitors() == (int)m_connections.size() + 1); - t->abort(); - } - catch (std::exception& e) - { - if (m_alerts.should_post(alert::debug)) - { - m_alerts.post_alert( - peer_error_alert(p->first->sender(), e.what())); - } - // the connection wants to disconnect for some reason, remove it - // from the connection-list - m_selector.remove(*i); - m_connections.erase(p); - assert(m_selector.count_read_monitors() == (int)m_connections.size() + 1); - } - } - } - purge_connections(); -#ifndef NDEBUG - check_invariant("after RECEIVE SOCKETS"); -#endif - - // ************************ - // ERROR SOCKETS - // ************************ - - - // disconnect the one we couldn't connect to - for (std::vector >::iterator i = error_clients.begin(); - i != error_clients.end(); - ++i) - { - connection_map::iterator p = m_connections.find(*i); - if (m_alerts.should_post(alert::debug)) - { - m_alerts.post_alert( - peer_error_alert( - p->first->sender() - , "socket received an exception")); - } - m_selector.remove(*i); - // the connection may have been disconnected in the receive or send phase - if (p != m_connections.end()) + } + else + { + try { + assert(m_selector.is_writability_monitored(p->first)); + assert(p->second->has_data()); + assert(p->second->get_socket()->is_writable()); + p->second->send_data(); + } + catch (file_error& e) + { + torrent* t = p->second->associated_torrent(); + assert(t != 0); + + if (m_alerts.should_post(alert::fatal)) + { + m_alerts.post_alert( + file_error_alert( + t->get_handle() + , e.what())); + } + + m_selector.remove(*i); + m_connections.erase(p); + assert(m_selector.count_read_monitors() == (int)m_connections.size() + 1); + t->abort(); + } + catch (std::exception& e) + { + // the connection wants to disconnect for some reason, + // remove it from the connection-list + if (m_alerts.should_post(alert::debug)) + { + m_alerts.post_alert( + peer_error_alert(p->first->sender(), e.what())); + } + + m_selector.remove(*i); m_connections.erase(p); assert(m_selector.count_read_monitors() == (int)m_connections.size() + 1); } } + } + purge_connections(); #ifndef NDEBUG - check_invariant("after ERROR SOCKETS"); + check_invariant("after SEND SOCKETS"); #endif + // ************************ + // RECEIVE SOCKETS + // ************************ - 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(); - - // ************************ - // THE SECTION BELOW IS EXECUTED ONCE EVERY SECOND - // ************************ - -#ifndef NDEBUG - // std::cout << "\n\nloops: " << loops_per_second << "\n"; - loops_per_second = 0; -#endif - - // do the second_tick() on each connection - // this will update their statistics (download and upload speeds) - // also purge sockets that have timed out - // and keep sockets open by keeping them alive. - for (connection_map::iterator i = m_connections.begin(); - i != m_connections.end();) + // let the readable clients receive data + for (std::vector >::iterator i = readable_clients.begin(); + i != readable_clients.end(); + ++i) + { + // special case for m_listen_socket socket + if (*i == m_listen_socket) { - connection_map::iterator j = i; - ++i; - // if this socket has timed out - // close it. - if (j->second->has_timed_out()) + assert(m_listen_socket); + boost::shared_ptr s = (*i)->accept(); + if (s) + { + s->set_blocking(false); + // we got a connection request! + m_incoming_connection = true; +#ifndef NDEBUG + (*m_logger) << s->sender().as_string() << " <== INCOMING CONNECTION\n"; +#endif + // TODO: filter ip:s + + boost::shared_ptr c( + new peer_connection(*this, m_selector, s)); + + if (m_upload_rate != -1) + { + c->upload_bandwidth.given = 0; + c->update_send_quota_left(); + } + + 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()) + { + m_selector.remove(*i); + } + else + { + try + { +// (*m_logger) << "readable: " << p->first->sender().as_string() << "\n"; + p->second->receive_data(); + } + catch (file_error& e) + { + torrent* t = p->second->associated_torrent(); + assert(t != 0); + + if (m_alerts.should_post(alert::fatal)) + { + m_alerts.post_alert( + file_error_alert( + t->get_handle() + , e.what())); + } + + m_selector.remove(*i); + m_connections.erase(p); + assert(m_selector.count_read_monitors() == (int)m_connections.size() + 1); + t->abort(); + } + catch (std::exception& e) { if (m_alerts.should_post(alert::debug)) { m_alerts.post_alert( - peer_error_alert(j->first->sender(), "connection timed out")); + peer_error_alert(p->first->sender(), e.what())); } - m_selector.remove(j->first); - m_connections.erase(j); + // the connection wants to disconnect for some reason, remove it + // from the connection-list + m_selector.remove(*i); + m_connections.erase(p); assert(m_selector.count_read_monitors() == (int)m_connections.size() + 1); - continue; } - - j->second->keep_alive(); } - - // check each torrent for abortion or - // tracker updates - for (std::map >::iterator i - = m_torrents.begin(); - i != m_torrents.end();) - { - if (i->second->is_aborted()) - { - m_tracker_manager.queue_request( - i->second->generate_tracker_request(m_listen_interface.port)); - i->second->disconnect_all(); + } + purge_connections(); #ifndef NDEBUG - sha1_hash i_hash = i->second->torrent_file().info_hash(); -#endif - std::map >::iterator j = i; - ++i; - m_torrents.erase(j); - assert(m_torrents.find(i_hash) == m_torrents.end()); - continue; - } - else if (i->second->should_request()) - { - m_tracker_manager.queue_request( - i->second->generate_tracker_request(m_listen_interface.port) - , boost::get_pointer(i->second)); - } - - i->second->second_tick(); - ++i; - } - purge_connections(); - - // distribute the maximum upload rate among the peers - - control_upload_rates( - m_upload_rate == -1 - ? std::numeric_limits::max() - : m_upload_rate - , m_connections); - - - m_tracker_manager.tick(); - } - - while (!m_tracker_manager.send_finished()) - { - m_tracker_manager.tick(); - boost::xtime t; - boost::xtime_get(&t, boost::TIME_UTC); - t.nsec += 100000000; - boost::thread::sleep(t); - } - -#ifndef NDEBUG - } - catch (std::bad_cast& e) - { - std::cerr << e.what() << "\n"; - } - catch (std::exception& e) - { - std::cerr << e.what() << "\n"; - } - catch (...) - { - std::cerr << "error!\n"; - } -#endif - } - - - // the return value from this function is valid only as long as the - // session is locked! - torrent* session_impl::find_torrent(const sha1_hash& info_hash) - { - std::map >::iterator i - = m_torrents.find(info_hash); - if (i != m_torrents.end()) return boost::get_pointer(i->second); - 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 - } + check_invariant("after RECEIVE SOCKETS"); #endif -#ifndef NDEBUG - void session_impl::check_invariant(const char *place) - { - assert(place); + // ************************ + // ERROR SOCKETS + // ************************ - for (connection_map::iterator i = m_connections.begin(); - i != m_connections.end(); + + // disconnect the one we couldn't connect to + for (std::vector >::iterator i = error_clients.begin(); + i != error_clients.end(); ++i) { - if (i->second->has_data() != m_selector.is_writability_monitored(i->first)) + connection_map::iterator p = m_connections.find(*i); + if (m_alerts.should_post(alert::debug)) { - 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.given " << p->upload_bandwidth.given << "\n"; - error_log << "peer_connection::get_peer_id " << p->get_peer_id() << "\n"; - error_log << "place: " << place << "\n"; - error_log.flush(); - assert(false); + m_alerts.post_alert( + peer_error_alert( + p->first->sender() + , "socket received an exception")); } - if (i->second->associated_torrent()) + + m_selector.remove(*i); + // the connection may have been disconnected in the receive or send phase + if (p != m_connections.end()) { - assert(i->second->associated_torrent() - ->get_policy().has_connection(boost::get_pointer(i->second))); + m_connections.erase(p); + assert(m_selector.count_read_monitors() == (int)m_connections.size() + 1); } } - } + +#ifndef NDEBUG + check_invariant("after ERROR SOCKETS"); #endif + 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(); + + // ************************ + // THE SECTION BELOW IS EXECUTED ONCE EVERY SECOND + // ************************ + +#ifndef NDEBUG + // std::cout << "\n\nloops: " << loops_per_second << "\n"; + loops_per_second = 0; +#endif + + // do the second_tick() on each connection + // this will update their statistics (download and upload speeds) + // also purge sockets that have timed out + // and keep sockets open by keeping them alive. + for (connection_map::iterator i = m_connections.begin(); + i != m_connections.end();) + { + connection_map::iterator j = i; + ++i; + // if this socket has timed out + // close it. + if (j->second->has_timed_out()) + { + if (m_alerts.should_post(alert::debug)) + { + m_alerts.post_alert( + peer_error_alert(j->first->sender(), "connection timed out")); + } + m_selector.remove(j->first); + m_connections.erase(j); + assert(m_selector.count_read_monitors() == (int)m_connections.size() + 1); + continue; + } + + j->second->keep_alive(); + } + + // check each torrent for abortion or + // tracker updates + for (std::map >::iterator i + = m_torrents.begin(); + i != m_torrents.end();) + { + if (i->second->is_aborted()) + { + m_tracker_manager.queue_request( + i->second->generate_tracker_request(m_listen_interface.port)); + i->second->disconnect_all(); +#ifndef NDEBUG + sha1_hash i_hash = i->second->torrent_file().info_hash(); +#endif + std::map >::iterator j = i; + ++i; + m_torrents.erase(j); + assert(m_torrents.find(i_hash) == m_torrents.end()); + continue; + } + else if (i->second->should_request()) + { + m_tracker_manager.queue_request( + i->second->generate_tracker_request(m_listen_interface.port) + , boost::get_pointer(i->second)); + } + + i->second->second_tick(); + ++i; + } + purge_connections(); + + // distribute the maximum upload rate among the peers + + control_upload_rates( + m_upload_rate == -1 + ? std::numeric_limits::max() + : m_upload_rate + , m_connections); + + + m_tracker_manager.tick(); + } + + while (!m_tracker_manager.send_finished()) + { + m_tracker_manager.tick(); + boost::xtime t; + boost::xtime_get(&t, boost::TIME_UTC); + t.nsec += 100000000; + boost::thread::sleep(t); + } + +#ifndef NDEBUG + } + catch (std::bad_cast& e) + { + std::cerr << e.what() << "\n"; + } + catch (std::exception& e) + { + std::cerr << e.what() << "\n"; + } + catch (...) + { + std::cerr << "error!\n"; + } +#endif } + + // the return value from this function is valid only as long as the + // session is locked! + torrent* session_impl::find_torrent(const sha1_hash& info_hash) + { + std::map >::iterator i + = m_torrents.find(info_hash); + if (i != m_torrents.end()) return boost::get_pointer(i->second); + 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 + +#ifndef NDEBUG + void session_impl::check_invariant(const char *place) + { + assert(place); + + for (connection_map::iterator i = m_connections.begin(); + i != m_connections.end(); + ++i) + { + if (i->second->has_data() != m_selector.is_writability_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.given " << p->upload_bandwidth.given << "\n"; + error_log << "peer_connection::get_peer_id " << p->get_peer_id() << "\n"; + error_log << "place: " << place << "\n"; + error_log.flush(); + assert(false); + } + if (i->second->associated_torrent()) + { + assert(i->second->associated_torrent() + ->get_policy().has_connection(boost::get_pointer(i->second))); + } + } + } +#endif + +}} + +namespace libtorrent +{ + session::session( std::pair listen_port_range , const fingerprint& id @@ -861,6 +894,36 @@ namespace libtorrent } } + bool session::listen_on( + std::pair const& port_range + , const char* net_interface) + { + boost::mutex::scoped_lock l(m_impl.m_mutex); + + if (m_impl.m_listen_socket) + { + m_impl.m_selector.remove(m_impl.m_listen_socket); + m_impl.m_listen_socket.reset(); + } + + m_impl.m_listen_port_range = port_range; + m_impl.m_listen_interface = address(net_interface, port_range.first); + m_impl.open_listen_port(); + return m_impl.m_listen_socket; + } + + unsigned short session::listen_port() const + { + boost::mutex::scoped_lock l(m_impl.m_mutex); + return m_impl.m_listen_interface.port; + } + + bool session::is_listening() const + { + boost::mutex::scoped_lock l(m_impl.m_mutex); + return m_impl.m_listen_socket; + } + void session::set_http_settings(const http_settings& s) { boost::mutex::scoped_lock l(m_impl.m_mutex);