forked from premiere/premiere-libtorrent
Increased performance. Fixed a bug. Extended torrent_handle interface.
This commit is contained in:
parent
c9bfb8aa36
commit
8b61436561
110
docs/index.html
110
docs/index.html
|
@ -53,6 +53,23 @@ The current state includes the following features:
|
|||
<li>gzipped tracker-responses
|
||||
<li>piece picking on block-level (as opposed to piece-level) like in
|
||||
<a href="http://azureus.sourceforge.net/">Azureus</a>
|
||||
<li>queues torrents for file check, instead of checking all of them in parallel.
|
||||
<li>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).
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
Functions that are yet to be implemented:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>optimistic unchoke
|
||||
<li>upload speed cap
|
||||
<li>Snubbing
|
||||
<li>end game mode
|
||||
<li>new allocation model
|
||||
<li>fast resume
|
||||
<li>file-level piece priority
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
|
@ -153,6 +170,12 @@ want to save the files. The <tt>save_path</tt> will be prepended to the director
|
|||
structure in the torrent-file.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If the torrent you are trying to add already exists in the session (is either queued
|
||||
for checking, being checked or downloading) <tt>add_torrent()</tt> will throw
|
||||
<tt>duplicate_torrent</tt> which derives from <tt>std::exception</tt>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<tt>fingerprint</tt> is a short string that will be used in the peer_id to
|
||||
identify the client. If the string is longer than 7 characters it will
|
||||
|
@ -453,28 +476,29 @@ Its declaration looks like this:
|
|||
struct torrent_handle
|
||||
{
|
||||
torrent_handle();
|
||||
|
||||
void get_peer_info(std::vector<peer_info>& v);
|
||||
void abort();
|
||||
|
||||
enum state_t
|
||||
{
|
||||
checking_files,
|
||||
connecting_to_tracker,
|
||||
downloading,
|
||||
seeding
|
||||
};
|
||||
|
||||
torrent_status status() const;
|
||||
void get_download_queue(std::vector<partial_piece_info>& queue);
|
||||
void get_peer_info(std::vector<peer_info>& v);
|
||||
};
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
The default constructor will initialize the handle to an invalid state. Which means you cannot
|
||||
perform any operation on it, unless you first assign it a valid handle. If you try to perform
|
||||
any operation they will simply return.
|
||||
</p>
|
||||
|
||||
|
||||
<p>
|
||||
<tt>abort()</tt> will close all peer connections associated with this torrent and tell
|
||||
the tracker that we've stopped participating in the swarm. This handle will become invalid
|
||||
shortly after this call has been made.
|
||||
</p>
|
||||
|
||||
<h3>status()</h3>
|
||||
|
||||
<p>
|
||||
<tt>status()</tt> will return a structure with information about the status of this
|
||||
torrent. It contains the following fields:
|
||||
|
@ -488,7 +512,6 @@ struct torrent_status
|
|||
invalid_handle,
|
||||
queued_for_checking,
|
||||
checking_files,
|
||||
connecting_to_tracker,
|
||||
downloading,
|
||||
seeding
|
||||
};
|
||||
|
@ -507,6 +530,15 @@ current task is in the <tt>state</tt> member, it will be one of the following:
|
|||
</p>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<tt>invalid_handle</tt>
|
||||
</td>
|
||||
<td>
|
||||
This will be the state if you called status on an uninitialized handle (a
|
||||
handle that was constructed with the default constructor).
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<tt>queued_for_checking</tt>
|
||||
|
@ -550,6 +582,52 @@ current task is in the <tt>state</tt> member, it will be one of the following:
|
|||
uploaded to all peers, accumulated.
|
||||
</p>
|
||||
|
||||
<h3>get_download_queue()</h3>
|
||||
|
||||
<p>
|
||||
<tt>get_download_queue()</tt> takes a non-const reference to a vector which it will fill
|
||||
information about pieces that are partially downloaded or not downloaded at all but partially
|
||||
requested. The entry in the vector (<tt>partial_piece_info</tt>) looks like this:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
struct partial_piece_info
|
||||
{
|
||||
enum { max_blocks_per_piece = <i>implementation-defined</i> };
|
||||
int piece_index;
|
||||
int blocks_in_piece;
|
||||
std::bitset<max_blocks_per_piece> requested_blocks;
|
||||
std::bitset<max_blocks_per_piece> finished_blocks;
|
||||
peer_id peer[max_blocks_per_piece];
|
||||
int num_downloads[max_blocks_per_piece];
|
||||
};
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
<tt>piece_index</tt> is the index of the piece in question. <tt>blocks_in_piece</tt> is the
|
||||
number of blocks in this particular piece. This number will be the same for most pieces, but
|
||||
the last piece may have fewer blocks than the standard pieces.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<tt>requested_blocks</tt> is a bitset with one bit per block in the piece. If a bit is set, it
|
||||
means that that block has been requested, but not necessarily fully downloaded yet. To know
|
||||
from whom the block has been requested, have a look in the <tt>peer</tt> array. The bit-index
|
||||
in the <tt>requested_blocks</tt> and <tt>finished_blocks</tt> correspons to the array-index into
|
||||
<tt>peers</tt> and <tt>num_downloads</tt>. The array of peers is contains the id of the
|
||||
peer the piece was requested from. If a piece hasn't been requested (the bit in
|
||||
<tt>requested_blocks</tt> is not set) the peer array entry will be undefined.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The <tt>finished_blocks</tt> is a bitset where each bit says if the block is fully downloaded
|
||||
or not. And the <tt>num_downloads</tt> array says how many times that block has been downloaded.
|
||||
When a piece fails a hash verification, single blocks may be redownloaded to see if the hash teast
|
||||
may pass then.
|
||||
</p>
|
||||
|
||||
<h3>get_peer_info()</h3>
|
||||
|
||||
<p>
|
||||
<tt>get_peer_info()</tt> takes a reference to a vector that will be cleared and filled
|
||||
with one entry for each peer connected to this torrent. Each entry contains information about
|
||||
|
@ -720,6 +798,16 @@ The sha1-algorithm used was implemented by Steve Reid and released as public dom
|
|||
For more info, see <tt>src/sha1.c</tt>.
|
||||
</p>
|
||||
|
||||
<h1>Feedback</h1>
|
||||
|
||||
<p>
|
||||
There's a <a href="http://lists.sourceforge.net/lists/listinfo/libtorrent-discuss">mailing list</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
You can usually find me as hydri in <tt>#btports @ irc.freenode.net</tt>.
|
||||
</p>
|
||||
|
||||
<h1>Credits</h1>
|
||||
|
||||
<p>
|
||||
|
|
|
@ -35,12 +35,15 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include <iterator>
|
||||
#include <exception>
|
||||
|
||||
#include <boost/format.hpp>
|
||||
|
||||
#include "libtorrent/entry.hpp"
|
||||
#include "libtorrent/bencode.hpp"
|
||||
#include "libtorrent/session.hpp"
|
||||
#include "libtorrent/http_settings.hpp"
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
#include <conio.h>
|
||||
|
||||
bool sleep_and_input(char* c)
|
||||
|
@ -54,6 +57,21 @@ bool sleep_and_input(char* c)
|
|||
return false;
|
||||
};
|
||||
|
||||
void set_cursor(int x, int y)
|
||||
{
|
||||
HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
COORD c = {x, y};
|
||||
SetConsoleCursorPosition(h, c);
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
COORD c = {0, 0};
|
||||
DWORD n;
|
||||
FillConsoleOutputCharacter(h, ' ', 80 * 50, c, &n);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include <stdlib.h>
|
||||
|
@ -155,6 +173,7 @@ int main(int argc, char* argv[])
|
|||
}
|
||||
|
||||
std::vector<peer_info> peers;
|
||||
std::vector<partial_piece_info> queue;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
|
@ -164,6 +183,8 @@ int main(int argc, char* argv[])
|
|||
if (c == 'q') break;
|
||||
}
|
||||
|
||||
clear();
|
||||
set_cursor(0, 0);
|
||||
for (std::vector<torrent_handle>::iterator i = handles.begin();
|
||||
i != handles.end();
|
||||
++i)
|
||||
|
@ -173,21 +194,19 @@ int main(int argc, char* argv[])
|
|||
switch(s.state)
|
||||
{
|
||||
case torrent_status::queued_for_checking:
|
||||
std::cout << "queued for checking: ";
|
||||
std::cout << "queued ";
|
||||
break;
|
||||
case torrent_status::checking_files:
|
||||
std::cout << "checking files: ";
|
||||
std::cout << "checking ";
|
||||
break;
|
||||
case torrent_status::downloading:
|
||||
std::cout << "downloading: ";
|
||||
std::cout << "dloading ";
|
||||
break;
|
||||
case torrent_status::seeding:
|
||||
std::cout << "seeding: ";
|
||||
std::cout << "seeding ";
|
||||
break;
|
||||
};
|
||||
|
||||
std::cout << s.progress*100 << "% ";
|
||||
|
||||
// calculate download and upload speeds
|
||||
i->get_peer_info(peers);
|
||||
float down = 0.f;
|
||||
|
@ -205,14 +224,35 @@ int main(int argc, char* argv[])
|
|||
total_down += i->total_download;
|
||||
total_up += i->total_upload;
|
||||
}
|
||||
|
||||
std::cout << "p:" << num_peers;
|
||||
|
||||
std::cout << " d:("
|
||||
<< add_suffix(total_down) << ") " << add_suffix(down) << "/s up:("
|
||||
/*
|
||||
std::cout << boost::format("%f%% p:%d d:(%s) %s/s u:(%s) %s/s\n")
|
||||
% (s.progress*100)
|
||||
% num_peers
|
||||
% add_suffix(total_down)
|
||||
% add_suffix(down)
|
||||
% add_suffix(total_up)
|
||||
% add_suffix(up);
|
||||
*/
|
||||
std::cout << (s.progress*100) << "% p:" << num_peers << " d:("
|
||||
<< add_suffix(total_down) << ") " << add_suffix(down) << "/s u:("
|
||||
<< add_suffix(total_up) << ") " << add_suffix(up) << "/s\n";
|
||||
|
||||
i->get_download_queue(queue);
|
||||
for (std::vector<partial_piece_info>::iterator i = queue.begin();
|
||||
i != queue.end();
|
||||
++i)
|
||||
{
|
||||
std::cout << i->piece_index << ": ";
|
||||
for (int j = 0; j < i->blocks_in_piece; ++j)
|
||||
{
|
||||
if (i->finished_blocks[j]) std::cout << "#";
|
||||
else if (i->requested_blocks[j]) std::cout << "-";
|
||||
else std::cout << ".";
|
||||
}
|
||||
std::cout << "\n";
|
||||
}
|
||||
std::cout << "___________________________________\n";
|
||||
}
|
||||
std::cout << "----\n";
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2003, Arvid Norberg
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the author nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef TORRENT_CONFIG_HPP_INCLUDED
|
||||
#define TORRENT_CONFIG_HPP_INCLUDED
|
||||
|
||||
#if defined(_WINDOWS)
|
||||
#if defined(_USRDLL)
|
||||
#define TORRENT_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define TORRENT_EXPORT __declspec(dllimport)
|
||||
#endif
|
||||
#else
|
||||
|
||||
#define TORRENT_EXPORT
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
|
@ -33,6 +33,8 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#ifndef TORRENT_PEER_ID_HPP_INCLUDED
|
||||
#define TORRENT_PEER_ID_HPP_INCLUDED
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
|
@ -63,11 +65,14 @@ namespace libtorrent
|
|||
return false;
|
||||
}
|
||||
|
||||
const unsigned char* begin() const { return m_number; }
|
||||
const unsigned char* end() const { return m_number+number_size; }
|
||||
typedef const unsigned char* const_iterator;
|
||||
typedef unsigned char* iterator;
|
||||
|
||||
unsigned char* begin() { return m_number; }
|
||||
unsigned char* end() { return m_number+number_size; }
|
||||
const_iterator begin() const { return m_number; }
|
||||
const_iterator end() const { return m_number+number_size; }
|
||||
|
||||
iterator begin() { return m_number; }
|
||||
iterator end() { return m_number+number_size; }
|
||||
|
||||
private:
|
||||
|
||||
|
@ -78,6 +83,19 @@ namespace libtorrent
|
|||
typedef big_number peer_id;
|
||||
typedef big_number sha1_hash;
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, const big_number& peer)
|
||||
{
|
||||
for (big_number::const_iterator i = peer.begin();
|
||||
i != peer.end();
|
||||
++i)
|
||||
{
|
||||
os << std::hex << std::setw(2) << std::setfill('0')
|
||||
<< static_cast<unsigned int>(*i);
|
||||
}
|
||||
os << std::dec << std::cout << std::setfill(' ');
|
||||
return os;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // TORRENT_PEER_ID_HPP_INCLUDED
|
||||
|
|
|
@ -37,6 +37,8 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include <bitset>
|
||||
#include <cassert>
|
||||
|
||||
#include "libtorrent/peer_id.hpp"
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
|
||||
|
@ -60,6 +62,37 @@ namespace libtorrent
|
|||
|
||||
enum { max_blocks_per_piece = 128 };
|
||||
|
||||
struct block_info
|
||||
{
|
||||
block_info(): num_downloads(0) {}
|
||||
// the peer this block was requested or
|
||||
// downloaded from
|
||||
peer_id peer;
|
||||
// the number of times this block has been downloaded
|
||||
int num_downloads;
|
||||
};
|
||||
|
||||
struct downloading_piece
|
||||
{
|
||||
int index;
|
||||
// each bit represents a block in the piece
|
||||
// set to one if the block has been requested
|
||||
std::bitset<max_blocks_per_piece> requested_blocks;
|
||||
// the bit is set to one if the block has been acquired
|
||||
std::bitset<max_blocks_per_piece> finished_blocks;
|
||||
// info about each block
|
||||
block_info info[max_blocks_per_piece];
|
||||
|
||||
// TODO: store a hash and a peer_connection reference
|
||||
// for each block. Then if the hash test fails on the
|
||||
// piece, redownload one block from another peer
|
||||
// then the first time, and check the hash again.
|
||||
// also maintain a counter how many times a piece-hash
|
||||
// has been confirmed. Download blocks that hasn't
|
||||
// been confirmed (since they are most probably the
|
||||
// invalid blocks)
|
||||
};
|
||||
|
||||
piece_picker(int blocks_per_piece,
|
||||
int total_num_blocks);
|
||||
|
||||
|
@ -92,7 +125,8 @@ namespace libtorrent
|
|||
// itself, by using the mark_as_downloading() member function.
|
||||
// THIS IS DONE BY THE peer_connection::request_piece() MEMBER FUNCTION!
|
||||
void pick_pieces(const std::vector<bool>& pieces,
|
||||
std::vector<piece_block>& interesting_blocks) const;
|
||||
std::vector<piece_block>& interesting_blocks,
|
||||
int num_pieces) const;
|
||||
|
||||
// returns true if any client is currently downloading this
|
||||
// piece-block, or if it's queued for downloading by some client
|
||||
|
@ -100,11 +134,11 @@ namespace libtorrent
|
|||
bool is_downloading(piece_block block) const;
|
||||
|
||||
// marks this piece-block as queued for downloading
|
||||
void mark_as_downloading(piece_block block);
|
||||
void mark_as_downloading(piece_block block, const peer_id& peer);
|
||||
void mark_as_finished(piece_block block);
|
||||
|
||||
// if a piece had a hash-failure, it must be restured and
|
||||
// made available fro redownloading
|
||||
// made available for redownloading
|
||||
void restore_piece(int index);
|
||||
|
||||
// clears the given piece's download flag
|
||||
|
@ -113,9 +147,17 @@ namespace libtorrent
|
|||
|
||||
bool is_piece_finished(int index) const;
|
||||
|
||||
// returns the number of blocks there is in the goven piece
|
||||
int blocks_in_piece(int index) const;
|
||||
|
||||
// the number of downloaded blocks that hasn't passed
|
||||
// the hash-check yet
|
||||
int unverified_blocks() const;
|
||||
|
||||
void get_downloaders(std::vector<peer_id>& d, int index);
|
||||
const std::vector<downloading_piece>& get_download_queue() const
|
||||
{ return m_downloads; }
|
||||
|
||||
#ifndef NDEBUG
|
||||
// used in debug mode
|
||||
void integrity_check(const torrent* t = 0) const;
|
||||
|
@ -147,20 +189,7 @@ namespace libtorrent
|
|||
|
||||
};
|
||||
|
||||
struct downloading_piece
|
||||
{
|
||||
int index;
|
||||
std::bitset<max_blocks_per_piece> requested_blocks;
|
||||
std::bitset<max_blocks_per_piece> finished_blocks;
|
||||
// TODO: store a hash and a peer_connection reference
|
||||
// for each block. Then if the hash test fails on the
|
||||
// piece, redownload one block from another peer
|
||||
// then the first time, and check the hash again.
|
||||
// also maintain a counter how many times a piece-hash
|
||||
// has been confirmed. Download blocks that hasn't
|
||||
// been confirmed (since they are most probably the
|
||||
// invalid blocks)
|
||||
};
|
||||
|
||||
|
||||
struct has_index
|
||||
{
|
||||
|
@ -173,9 +202,10 @@ namespace libtorrent
|
|||
void move(bool downloading, int vec_index, int elem_index);
|
||||
void remove(bool downloading, int vec_index, int elem_index);
|
||||
|
||||
bool add_interesting_blocks(const std::vector<int>& piece_list,
|
||||
int add_interesting_blocks(const std::vector<int>& piece_list,
|
||||
const std::vector<bool>& pieces,
|
||||
std::vector<piece_block>& interesting_pieces) const;
|
||||
std::vector<piece_block>& interesting_pieces,
|
||||
int num_blocks) const;
|
||||
|
||||
// this vector contains all pieces we don't have.
|
||||
// in the first entry (index 0) is a vector of all pieces
|
||||
|
|
|
@ -238,12 +238,21 @@ namespace libtorrent
|
|||
void monitor_readability(boost::shared_ptr<socket> s) { m_readable.push_back(s); }
|
||||
void monitor_writability(boost::shared_ptr<socket> s) { m_writable.push_back(s); }
|
||||
void monitor_errors(boost::shared_ptr<socket> s) { m_error.push_back(s); }
|
||||
|
||||
/*
|
||||
void clear_readable() { m_readable.clear(); }
|
||||
void clear_writable() { m_writable.clear(); }
|
||||
|
||||
*/
|
||||
void remove(boost::shared_ptr<socket> s);
|
||||
|
||||
void remove_writable(boost::shared_ptr<socket> s)
|
||||
{ m_writable.erase(std::find(m_writable.begin(), m_writable.end(), s)); }
|
||||
|
||||
bool is_writability_monitored(boost::shared_ptr<socket> s)
|
||||
{
|
||||
return std::find(m_writable.begin(), m_writable.end(), s)
|
||||
!= m_writable.end();
|
||||
}
|
||||
|
||||
void wait(int timeout
|
||||
, std::vector<boost::shared_ptr<socket> >& readable
|
||||
, std::vector<boost::shared_ptr<socket> >& writable
|
||||
|
|
|
@ -37,6 +37,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
#include "libtorrent/peer_id.hpp"
|
||||
#include "libtorrent/peer_info.hpp"
|
||||
#include "libtorrent/piece_picker.hpp"
|
||||
|
||||
|
||||
namespace libtorrent
|
||||
|
@ -47,6 +48,11 @@ namespace libtorrent
|
|||
struct checker_impl;
|
||||
}
|
||||
|
||||
struct duplicate_torrent: std::exception
|
||||
{
|
||||
virtual const char* what() const { return "torrent already exists in session"; }
|
||||
};
|
||||
|
||||
struct torrent_status
|
||||
{
|
||||
enum state_t
|
||||
|
@ -54,7 +60,6 @@ namespace libtorrent
|
|||
invalid_handle,
|
||||
queued_for_checking,
|
||||
checking_files,
|
||||
connecting_to_tracker,
|
||||
downloading,
|
||||
seeding
|
||||
};
|
||||
|
@ -65,6 +70,17 @@ namespace libtorrent
|
|||
std::size_t total_upload;
|
||||
};
|
||||
|
||||
struct partial_piece_info
|
||||
{
|
||||
enum { max_blocks_per_piece = piece_picker::max_blocks_per_piece };
|
||||
int piece_index;
|
||||
int blocks_in_piece;
|
||||
std::bitset<max_blocks_per_piece> requested_blocks;
|
||||
std::bitset<max_blocks_per_piece> finished_blocks;
|
||||
peer_id peer[max_blocks_per_piece];
|
||||
int num_downloads[max_blocks_per_piece];
|
||||
};
|
||||
|
||||
struct torrent_handle
|
||||
{
|
||||
friend class session;
|
||||
|
@ -76,6 +92,8 @@ namespace libtorrent
|
|||
|
||||
torrent_status status() const;
|
||||
|
||||
void get_download_queue(std::vector<partial_piece_info>& queue) const;
|
||||
|
||||
// TODO: add a 'time to next announce' query.
|
||||
|
||||
private:
|
||||
|
|
|
@ -399,12 +399,21 @@ bool libtorrent::peer_connection::dispatch_message()
|
|||
}
|
||||
else
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
std::cout << "hash-test failed. Some of these peers sent invalid data:\n";
|
||||
std::vector<peer_id> downloaders;
|
||||
picker.get_downloaders(downloaders, index);
|
||||
std::copy(downloaders.begin(), downloaders.end(), std::ostream_iterator<peer_id>(std::cout, "\n"));
|
||||
#endif
|
||||
// we have to let the piece_picker know that
|
||||
// this piece failed the check as it can restore it
|
||||
// and mark it as being interesting for download
|
||||
// TODO: do this more intelligently! and keep track
|
||||
// of how much crap (data that failed hash-check) and
|
||||
// how much redundant data we have downloaded
|
||||
// if some clients has sent more than one piece
|
||||
// start with redownloading the pieces that the client
|
||||
// that has sent the least number of pieces
|
||||
picker.restore_piece(index);
|
||||
}
|
||||
m_torrent->get_policy().piece_finished(*this, index, verified);
|
||||
|
@ -446,7 +455,7 @@ void libtorrent::peer_connection::request_block(piece_block block)
|
|||
assert(block.piece_index < m_torrent->torrent_file().num_pieces());
|
||||
assert(!m_torrent->picker().is_downloading(block));
|
||||
|
||||
m_torrent->picker().mark_as_downloading(block);
|
||||
m_torrent->picker().mark_as_downloading(block, m_peer_id);
|
||||
|
||||
m_download_queue.push_back(block);
|
||||
|
||||
|
@ -785,6 +794,8 @@ bool libtorrent::peer_connection::has_data() const throw()
|
|||
// throws exception when the client should be disconnected
|
||||
void libtorrent::peer_connection::send_data()
|
||||
{
|
||||
assert(has_data());
|
||||
|
||||
// only add new piece-chunks if the send buffer is empty
|
||||
// otherwise there will be no end to how large it will be!
|
||||
if (!m_requests.empty() && m_send_buffer.empty() && m_peer_interested && !m_choked)
|
||||
|
|
|
@ -360,10 +360,16 @@ namespace libtorrent
|
|||
#endif
|
||||
}
|
||||
|
||||
void piece_picker::pick_pieces(const std::vector<bool>& pieces, std::vector<piece_block>& interesting_pieces) const
|
||||
void piece_picker::pick_pieces(const std::vector<bool>& pieces,
|
||||
std::vector<piece_block>& interesting_pieces,
|
||||
int num_blocks) const
|
||||
{
|
||||
assert(pieces.size() == m_piece_map.size());
|
||||
|
||||
#ifndef NDEBUG
|
||||
integrity_check();
|
||||
#endif
|
||||
|
||||
// free refers to pieces that are free to download, noone else
|
||||
// is downloading them.
|
||||
// partial is pieces that are partially being downloaded, and
|
||||
|
@ -378,10 +384,8 @@ namespace libtorrent
|
|||
{
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
if (add_interesting_blocks(*partial, pieces, interesting_pieces))
|
||||
{
|
||||
return;
|
||||
}
|
||||
num_blocks = add_interesting_blocks(*partial, pieces, interesting_pieces, num_blocks);
|
||||
if (num_blocks == 0) return;
|
||||
++partial;
|
||||
if (partial == m_downloading_piece_info.end()) break;
|
||||
}
|
||||
|
@ -389,18 +393,17 @@ namespace libtorrent
|
|||
|
||||
if (free != m_piece_info.end())
|
||||
{
|
||||
if (add_interesting_blocks(*free, pieces, interesting_pieces))
|
||||
{
|
||||
return;
|
||||
}
|
||||
num_blocks = add_interesting_blocks(*free, pieces, interesting_pieces, num_blocks);
|
||||
if (num_blocks == 0) return;
|
||||
++free;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool piece_picker::add_interesting_blocks(const std::vector<int>& piece_list,
|
||||
int piece_picker::add_interesting_blocks(const std::vector<int>& piece_list,
|
||||
const std::vector<bool>& pieces,
|
||||
std::vector<piece_block>& interesting_blocks) const
|
||||
std::vector<piece_block>& interesting_blocks,
|
||||
int num_blocks) const
|
||||
{
|
||||
|
||||
for (std::vector<int>::const_iterator i = piece_list.begin();
|
||||
|
@ -418,15 +421,20 @@ namespace libtorrent
|
|||
|
||||
if (m_piece_map[*i].downloading == 0)
|
||||
{
|
||||
interesting_blocks.push_back(piece_block(*i, 0));
|
||||
return true; // we have found a piece that's free to download
|
||||
int piece_blocks = std::min(blocks_in_piece(*i), num_blocks);
|
||||
for (int j = 0; j < piece_blocks; ++j)
|
||||
{
|
||||
interesting_blocks.push_back(piece_block(*i, 0));
|
||||
}
|
||||
num_blocks -= piece_blocks;
|
||||
if (num_blocks == 0) return num_blocks;
|
||||
continue;
|
||||
}
|
||||
|
||||
// calculate the number of blocks in this
|
||||
// piece. It's always m_blocks_per_piece, except
|
||||
// in the last piece.
|
||||
int num_blocks_in_piece
|
||||
= (*i == m_piece_map.size()-1)?m_blocks_in_last_piece:m_blocks_per_piece;
|
||||
int num_blocks_in_piece = blocks_in_piece(*i);
|
||||
|
||||
std::vector<downloading_piece>::const_iterator p
|
||||
= std::find_if(m_downloads.begin(), m_downloads.end(), has_index(*i));
|
||||
|
@ -436,10 +444,14 @@ namespace libtorrent
|
|||
{
|
||||
interesting_blocks.push_back(piece_block(*i, j));
|
||||
if (p->requested_blocks[j] == 0)
|
||||
return true; // we have found a piece that's free to download
|
||||
{
|
||||
// we have found a piece that's free to download
|
||||
num_blocks--;
|
||||
if (num_blocks == 0) return num_blocks;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return num_blocks;
|
||||
}
|
||||
|
||||
bool piece_picker::is_piece_finished(int index) const
|
||||
|
@ -471,7 +483,7 @@ namespace libtorrent
|
|||
return i->requested_blocks[block.block_index];
|
||||
}
|
||||
|
||||
void piece_picker::mark_as_downloading(piece_block block)
|
||||
void piece_picker::mark_as_downloading(piece_block block, const peer_id& peer)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
integrity_check();
|
||||
|
@ -488,6 +500,7 @@ namespace libtorrent
|
|||
downloading_piece dp;
|
||||
dp.index = block.piece_index;
|
||||
dp.requested_blocks[block.block_index] = 1;
|
||||
dp.info[block.block_index].peer = peer;
|
||||
m_downloads.push_back(dp);
|
||||
}
|
||||
else
|
||||
|
@ -496,6 +509,7 @@ namespace libtorrent
|
|||
= std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index));
|
||||
assert(i != m_downloads.end());
|
||||
assert(i->requested_blocks[block.block_index] == 0);
|
||||
i->info[block.block_index].peer = peer;
|
||||
i->requested_blocks[block.block_index] = 1;
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
|
@ -518,11 +532,24 @@ namespace libtorrent
|
|||
assert(i != m_downloads.end());
|
||||
assert(i->requested_blocks[block.block_index] == 1);
|
||||
i->finished_blocks[block.block_index] = 1;
|
||||
i->info[block.block_index].num_downloads++;
|
||||
#ifndef NDEBUG
|
||||
integrity_check();
|
||||
#endif
|
||||
}
|
||||
|
||||
void piece_picker::get_downloaders(std::vector<peer_id>& d, int index)
|
||||
{
|
||||
std::vector<downloading_piece>::iterator i
|
||||
= std::find_if(m_downloads.begin(), m_downloads.end(), has_index(index));
|
||||
assert(i != m_downloads.end());
|
||||
|
||||
d.clear();
|
||||
for (int j = 0; j < blocks_in_piece(index); ++j)
|
||||
{
|
||||
d.push_back(i->info[j].peer);
|
||||
}
|
||||
}
|
||||
|
||||
void piece_picker::abort_download(piece_block block)
|
||||
{
|
||||
|
|
|
@ -39,13 +39,30 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
namespace
|
||||
{
|
||||
enum
|
||||
{
|
||||
// we try to maintain 4 requested blocks in the download
|
||||
// queue
|
||||
request_queue = 4
|
||||
};
|
||||
|
||||
|
||||
using namespace libtorrent;
|
||||
void request_a_block(torrent& t, peer_connection& c)
|
||||
{
|
||||
piece_picker& p = t.picker();
|
||||
std::vector<piece_block> interesting_pieces;
|
||||
interesting_pieces.reserve(100);
|
||||
p.pick_pieces(c.get_bitfield(), interesting_pieces);
|
||||
|
||||
int num_requests = request_queue - c.download_queue().size();
|
||||
if (num_requests <= 0) num_requests = 1;
|
||||
|
||||
// picks the interesting pieces from this peer
|
||||
// the integer is the number of pieces that
|
||||
// should be guaranteed to be available for download
|
||||
// (if this number is too big, too many pieces are
|
||||
// picked and cpu-time is wasted)
|
||||
p.pick_pieces(c.get_bitfield(), interesting_pieces, num_requests);
|
||||
|
||||
// this vector is filled with the interestin pieces
|
||||
// that some other peer is currently downloading
|
||||
|
@ -68,7 +85,8 @@ namespace
|
|||
// ok, we found a piece that's not being downloaded
|
||||
// by somebody else. request it from this peer
|
||||
c.request_block(*i);
|
||||
return;
|
||||
num_requests++;
|
||||
if (num_requests >= request_queue) return;
|
||||
}
|
||||
|
||||
// TODO: compare this peer's bandwidth against the
|
||||
|
|
|
@ -182,6 +182,8 @@ namespace libtorrent
|
|||
#if defined(TORRENT_VERBOSE_LOGGING)
|
||||
m_logger = create_log("main session");
|
||||
#endif
|
||||
try
|
||||
{
|
||||
|
||||
boost::shared_ptr<socket> listener(new socket(socket::tcp, false));
|
||||
int max_port = m_listen_port + 9;
|
||||
|
@ -233,6 +235,11 @@ namespace libtorrent
|
|||
#endif
|
||||
for(;;)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
std::clock_t time__ = std::clock();
|
||||
#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";
|
||||
|
@ -243,6 +250,10 @@ namespace libtorrent
|
|||
num_loops++;
|
||||
#endif
|
||||
|
||||
|
||||
assert(readable_clients.size() + writable_clients.size() + error_clients.size() > 0
|
||||
|| (std::clock() - time__) > CLOCKS_PER_SEC / 3);
|
||||
|
||||
// +1 for the listen socket
|
||||
assert(m_selector.count_read_monitors() == m_connections.size() + 1);
|
||||
|
||||
|
@ -262,6 +273,7 @@ namespace libtorrent
|
|||
break;
|
||||
}
|
||||
|
||||
|
||||
// ************************
|
||||
// RECEIVE SOCKETS
|
||||
// ************************
|
||||
|
@ -297,7 +309,6 @@ namespace libtorrent
|
|||
continue;
|
||||
}
|
||||
|
||||
|
||||
connection_map::iterator p = m_connections.find(*i);
|
||||
if(p == m_connections.end())
|
||||
{
|
||||
|
@ -321,7 +332,6 @@ namespace libtorrent
|
|||
}
|
||||
|
||||
|
||||
|
||||
// ************************
|
||||
// SEND SOCKETS
|
||||
// ************************
|
||||
|
@ -342,8 +352,15 @@ namespace libtorrent
|
|||
{
|
||||
try
|
||||
{
|
||||
assert(m_selector.is_writability_monitored(p->first));
|
||||
assert(p->second->has_data());
|
||||
// (*m_logger) << "writable: " << p->first->sender().as_string() << "\n";
|
||||
p->second->send_data();
|
||||
// if the peer doesn't have
|
||||
// any data left to send, remove it
|
||||
// from the writabilty monitor
|
||||
if (!p->second->has_data())
|
||||
m_selector.remove_writable(p->first);
|
||||
}
|
||||
catch(network_error&)
|
||||
{
|
||||
|
@ -374,16 +391,27 @@ namespace libtorrent
|
|||
if (p != m_connections.end()) m_connections.erase(p);
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
for (connection_map::iterator i = m_connections.begin();
|
||||
i != m_connections.end();
|
||||
++i)
|
||||
{
|
||||
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();
|
||||
/* 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
|
||||
|
@ -411,7 +439,7 @@ namespace libtorrent
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
// (*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;
|
||||
|
@ -422,18 +450,41 @@ namespace libtorrent
|
|||
// THE SECTION BELOW IS EXECUTED ONCE EVERY SECOND
|
||||
// ************************
|
||||
|
||||
|
||||
#ifdef TORRENT_DEBUG_SOCKETS
|
||||
std::cout << "\nloops: " << num_loops << "\n";
|
||||
assert(loops < 1300);
|
||||
if (num_loops > 1300)
|
||||
{
|
||||
int i = 0;
|
||||
}
|
||||
num_loops = 0;
|
||||
#endif
|
||||
|
||||
|
||||
// do the second_tick() on each connection
|
||||
// this will update their statistics (download and upload speeds)
|
||||
for (connection_map::iterator i = m_connections.begin(); i != m_connections.end(); ++i)
|
||||
// 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();)
|
||||
{
|
||||
i->second->second_tick();
|
||||
|
||||
connection_map::iterator j = i;
|
||||
++i;
|
||||
// if this socket has timed out
|
||||
// close it.
|
||||
if (j->second->has_timed_out())
|
||||
{
|
||||
m_selector.remove(j->first);
|
||||
m_connections.erase(j);
|
||||
continue;
|
||||
}
|
||||
|
||||
j->second->keep_alive();
|
||||
|
||||
if (j->second->has_data() && !m_selector.is_writability_monitored(j->first))
|
||||
m_selector.monitor_writability(j->first);
|
||||
}
|
||||
|
||||
// check each torrent for abortion or
|
||||
|
@ -484,6 +535,17 @@ namespace libtorrent
|
|||
t.nsec += 1000000;
|
||||
boost::thread::sleep(t);
|
||||
}
|
||||
}
|
||||
catch(const std::exception& e)
|
||||
{
|
||||
std::cout << e.what() << "\n";
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
std::cout << "error\n";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// the return value from this function is valid only as long as the
|
||||
|
@ -498,6 +560,7 @@ namespace libtorrent
|
|||
|
||||
}
|
||||
|
||||
// if the torrent already exists, this will throw duplicate_torrent
|
||||
torrent_handle session::add_torrent(const torrent_info& ti,
|
||||
const std::string& save_path)
|
||||
{
|
||||
|
@ -507,9 +570,8 @@ namespace libtorrent
|
|||
boost::mutex::scoped_lock l(m_impl.m_mutex);
|
||||
|
||||
// is the torrent already active?
|
||||
// TODO: this should throw
|
||||
if (m_impl.find_torrent(ti.info_hash()))
|
||||
return torrent_handle(&m_impl, &m_checker_impl, ti.info_hash());
|
||||
throw duplicate_torrent();
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -517,9 +579,8 @@ namespace libtorrent
|
|||
boost::mutex::scoped_lock l(m_checker_impl.m_mutex);
|
||||
|
||||
// is the torrent currently being checked?
|
||||
// TODO: This should throw
|
||||
if (m_checker_impl.find_torrent(ti.info_hash()))
|
||||
return torrent_handle(&m_impl, &m_checker_impl, ti.info_hash());
|
||||
throw duplicate_torrent();
|
||||
}
|
||||
|
||||
// create the torrent and the data associated with
|
||||
|
|
|
@ -187,9 +187,9 @@ namespace libtorrent
|
|||
std::cout << std::hex << std::setw(2) << std::setfill('0')
|
||||
<< static_cast<unsigned int>(*j);
|
||||
}
|
||||
std::cout << " " << extract_fingerprint(i->id) << "\n";
|
||||
std::cout << std::dec << " " << extract_fingerprint(i->id) << "\n";
|
||||
}
|
||||
std::cout << std::dec << std::setfill(' ');
|
||||
std::cout << std::setfill(' ');
|
||||
|
||||
|
||||
// for each of the peers we got from the tracker
|
||||
|
|
|
@ -143,11 +143,44 @@ namespace libtorrent
|
|||
}
|
||||
}
|
||||
|
||||
void torrent_handle::get_download_queue(std::vector<partial_piece_info>& queue) const
|
||||
{
|
||||
queue.clear();
|
||||
|
||||
if (m_ses == 0) return;
|
||||
|
||||
boost::mutex::scoped_lock l(m_ses->m_mutex);
|
||||
torrent* t = m_ses->find_torrent(m_info_hash);
|
||||
if (t == 0) return;
|
||||
|
||||
const piece_picker& p = t->picker();
|
||||
|
||||
const std::vector<piece_picker::downloading_piece>& q
|
||||
= p.get_download_queue();
|
||||
|
||||
for (std::vector<piece_picker::downloading_piece>::const_iterator i
|
||||
= q.begin();
|
||||
i != q.end();
|
||||
++i)
|
||||
{
|
||||
partial_piece_info pi;
|
||||
pi.finished_blocks = i->finished_blocks;
|
||||
pi.requested_blocks = i->requested_blocks;
|
||||
for (int j = 0; j < partial_piece_info::max_blocks_per_piece; ++j)
|
||||
{
|
||||
pi.peer[j] = i->info[j].peer;
|
||||
pi.num_downloads[j] = i->info[j].num_downloads;
|
||||
}
|
||||
pi.piece_index = i->index;
|
||||
pi.blocks_in_piece = p.blocks_in_piece(i->index);
|
||||
queue.push_back(pi);
|
||||
}
|
||||
}
|
||||
|
||||
void torrent_handle::abort()
|
||||
{
|
||||
if (m_ses == 0) return;
|
||||
|
||||
|
||||
{
|
||||
boost::mutex::scoped_lock l(m_ses->m_mutex);
|
||||
torrent* t = m_ses->find_torrent(m_info_hash);
|
||||
|
|
Loading…
Reference in New Issue