+ Tracker communication uses fewer resources
+ More accurate bandwidth management
+ Bandwidth displays now include BitTorrent protocol overhead
- + Files are preallocated as soon as any data is received
+ + Files are preallocated as soon as any data is received for that file
+ Stability, security, and performance improvements to the RPC/Web UI server
+ Support compression when serving Web UI and RPC responses
+ Simplify the RPC whitelist
#include "crypto.h"
#include "net.h"
#include "peer-io.h"
-#include "ratecontrol.h"
#include "trevent.h"
#include "utils.h"
#define IO_TIMEOUT_SECS 8
+static size_t
+addPacketOverhead( size_t d )
+{
+ /**
+ * http://sd.wareonearth.com/~phil/net/overhead/
+ *
+ * TCP over Ethernet:
+ * Assuming no header compression (e.g. not PPP)
+ * Add 20 IPv4 header or 40 IPv6 header (no options)
+ * Add 20 TCP header
+ * Add 12 bytes optional TCP timestamps
+ * Max TCP Payload data rates over ethernet are thus:
+ * (1500-40)/(38+1500) = 94.9285 % IPv4, minimal headers
+ * (1500-52)/(38+1500) = 94.1482 % IPv4, TCP timestamps
+ * (1500-52)/(42+1500) = 93.9040 % 802.1q, IPv4, TCP timestamps
+ * (1500-60)/(38+1500) = 93.6281 % IPv6, minimal headers
+ * (1500-72)/(38+1500) = 92.8479 % IPv6, TCP timestamps
+ * (1500-72)/(42+1500) = 92.6070 % 802.1q, IPv6, ICP timestamps
+ */
+ static const double assumed_payload_data_rate = 94.0;
+
+ return (size_t)( d * ( 100.0 / assumed_payload_data_rate ) );
+}
+
/**
***
**/
size_t bufferSize[2];
struct tr_bandwidth bandwidth[2];
- tr_ratecontrol * speedometer[2];
tr_crypto * crypto;
};
if( len < io->bufferSize[TR_UP] )
{
- const size_t n = io->bufferSize[TR_UP] - len;
+ const size_t payload = io->bufferSize[TR_UP] - len;
+ const size_t n = addPacketOverhead( payload );
struct tr_bandwidth * b = &io->bandwidth[TR_UP];
b->bytesLeft -= MIN( b->bytesLeft, (size_t)n );
b->bytesUsed += n;
- tr_rcTransferred( io->speedometer[TR_UP], n );
dbgmsg( io,
"wrote %zu bytes to peer... upload bytesLeft is now %zu",
n,
/* if the input buffer has grown, record the bytes that were read */
if( len > io->bufferSize[TR_DOWN] )
{
- const size_t n = len - io->bufferSize[TR_DOWN];
+ const size_t payload = len - io->bufferSize[TR_DOWN];
+ const size_t n = addPacketOverhead( payload );
struct tr_bandwidth * b = io->bandwidth + TR_DOWN;
b->bytesLeft -= MIN( b->bytesLeft, (size_t)n );
b->bytesUsed += n;
- tr_rcTransferred( io->speedometer[TR_DOWN], n );
dbgmsg( io,
"%zu new input bytes. bytesUsed is %zu, bytesLeft is %zu",
n, b->bytesUsed,
io->output = evbuffer_new( );
io->bandwidth[TR_UP].isUnlimited = 1;
io->bandwidth[TR_DOWN].isUnlimited = 1;
- io->speedometer[TR_UP] = tr_rcInit( );
- io->speedometer[TR_DOWN] = tr_rcInit( );
bufevNew( io );
return io;
}
{
tr_peerIo * io = vio;
- tr_rcClose( io->speedometer[TR_DOWN] );
- tr_rcClose( io->speedometer[TR_UP] );
evbuffer_free( io->output );
bufferevent_free( io->bufev );
tr_netClose( io->socket );
adjustOutputBuffer( io );
}
-double
-tr_peerIoGetRateToClient( const tr_peerIo * io )
-{
- return tr_rcRate( io->speedometer[TR_DOWN] );
-}
-
-double
-tr_peerIoGetRateToPeer( const tr_peerIo * io )
-{
- return tr_rcRate( io->speedometer[TR_UP] );
-}
-
/**
***
**/
void tr_peerIoSetBandwidthUnlimited( tr_peerIo * io,
tr_direction direction );
-double tr_peerIoGetRateToClient( const tr_peerIo * io );
-
-double tr_peerIoGetRateToPeer( const tr_peerIo * io );
-
#endif
struct tr_bitfield;
struct tr_peerIo;
struct tr_peermsgs;
+struct tr_ratecontrol;
enum
{
typedef struct tr_peer
{
- unsigned int peerIsChoked : 1;
- unsigned int peerIsInterested : 1;
- unsigned int clientIsChoked : 1;
- unsigned int clientIsInterested : 1;
- unsigned int doPurge : 1;
+ unsigned int peerIsChoked : 1;
+ unsigned int peerIsInterested : 1;
+ unsigned int clientIsChoked : 1;
+ unsigned int clientIsInterested : 1;
+ unsigned int doPurge : 1;
/* number of bad pieces they've contributed to */
- uint8_t strikes;
+ uint8_t strikes;
- uint8_t encryption_preference;
- uint16_t port;
- struct in_addr in_addr;
- struct tr_peerIo * io;
+ uint8_t encryption_preference;
+ uint16_t port;
+ struct in_addr in_addr;
+ struct tr_peerIo * io;
- struct tr_bitfield * blame;
- struct tr_bitfield * have;
- float progress;
+ struct tr_bitfield * blame;
+ struct tr_bitfield * have;
+
+ /** how complete the peer's copy of the torrent is. [0.0...1.0] */
+ float progress;
/* the client name from the `v' string in LTEP's handshake dictionary */
- char * client;
+ char * client;
+
+ time_t peerSentPieceDataAt;
+ time_t chokeChangedAt;
+ time_t pieceDataActivityDate;
- time_t peerSentPieceDataAt;
- time_t chokeChangedAt;
- time_t pieceDataActivityDate;
+ struct tr_peermsgs * msgs;
+ tr_publisher_tag msgsTag;
- struct tr_peermsgs * msgs;
- tr_publisher_tag msgsTag;
+ /* the rate at which pieces are being transferred between client and peer.
+ * protocol overhead is NOT included; this is only the piece data */
+ struct tr_ratecontrol * pieceSpeed[2];
}
tr_peer;
+double tr_peerGetPieceSpeed( const tr_peer * peer,
+ tr_direction direction );
+
#endif
#include "peer-mgr-private.h"
#include "peer-msgs.h"
#include "ptrarray.h"
+#include "ratecontrol.h"
#include "stats.h" /* tr_statsAddUploaded, tr_statsAddDownloaded */
#include "torrent.h"
#include "trevent.h"
tr_ptrArray * torrents; /* Torrent */
tr_ptrArray * incomingHandshakes; /* tr_handshake */
tr_timer * bandwidthTimer;
- double rateHistory[2][BANDWIDTH_PULSE_HISTORY];
double globalPoolHistory[2][BANDWIDTH_PULSE_HISTORY];
};
p = tr_new0( tr_peer, 1 );
memcpy( &p->in_addr, in_addr, sizeof( struct in_addr ) );
+ p->pieceSpeed[TR_CLIENT_TO_PEER] = tr_rcInit( );
+ p->pieceSpeed[TR_PEER_TO_CLIENT] = tr_rcInit( );
return p;
}
tr_bitfieldFree( peer->have );
tr_bitfieldFree( peer->blame );
tr_free( peer->client );
+
+ tr_rcClose( peer->pieceSpeed[TR_CLIENT_TO_PEER] );
+ tr_rcClose( peer->pieceSpeed[TR_PEER_TO_CLIENT] );
tr_free( peer );
}
tr_torrent * tor = t->tor;
tor->activityDate = now;
tor->uploadedCur += e->length;
+ tr_rcTransferred ( peer->pieceSpeed[TR_CLIENT_TO_PEER], e->length );
+ tr_rcTransferred ( tor->pieceSpeed[TR_CLIENT_TO_PEER], e->length );
+ tr_rcTransferred ( tor->session->pieceSpeed[TR_CLIENT_TO_PEER], e->length );
tr_statsAddUploaded( tor->session, e->length );
if( peer )
{
const time_t now = time( NULL );
tr_torrent * tor = t->tor;
tor->activityDate = now;
+ tr_statsAddDownloaded( tor->session, e->length );
+ tr_rcTransferred ( peer->pieceSpeed[TR_PEER_TO_CLIENT], e->length );
+ tr_rcTransferred ( tor->pieceSpeed[TR_PEER_TO_CLIENT], e->length );
+ tr_rcTransferred ( tor->session->pieceSpeed[TR_PEER_TO_CLIENT], e->length );
/* only add this to downloadedCur if we got it from a peer --
* webseeds shouldn't count against our ratio. As one tracker
* admin put it, "Those pieces are downloaded directly from the
* into the jurisdiction of the tracker." */
if( peer )
tor->downloadedCur += e->length;
- tr_statsAddDownloaded( tor->session, e->length );
if( peer ) {
struct peer_atom * a = getExistingAtom( t, &peer->in_addr );
a->piece_data_time = now;
managerUnlock( manager );
}
-double
-tr_peerMgrGetRate( const tr_peerMgr * manager,
- tr_direction direction )
-{
- int i;
- double bytes = 0;
-
- assert( manager != NULL );
- assert( direction == TR_UP || direction == TR_DOWN );
-
- for( i = 0; i < BANDWIDTH_PULSE_HISTORY; ++i )
- bytes += manager->rateHistory[direction][i];
-
- return ( BANDWIDTH_PULSES_PER_SECOND * bytes )
- / ( BANDWIDTH_PULSE_HISTORY * 1024 );
-}
-
float*
tr_peerMgrWebSpeeds( const tr_peerMgr * manager,
const uint8_t * torrentHash )
return ret;
}
+double
+tr_peerGetPieceSpeed( const tr_peer * peer,
+ tr_direction direction )
+{
+ assert( peer );
+ assert( direction==TR_CLIENT_TO_PEER || direction==TR_PEER_TO_CLIENT );
+
+ return tr_rcRate( peer->pieceSpeed[direction] );
+}
+
+
struct tr_peer_stat *
tr_peerMgrPeerStats( const tr_peerMgr * manager,
const uint8_t * torrentHash,
stat->from = atom->from;
stat->progress = peer->progress;
stat->isEncrypted = tr_peerIoIsEncrypted( peer->io ) ? 1 : 0;
- stat->rateToPeer = tr_peerIoGetRateToPeer( peer->io );
- stat->rateToClient = tr_peerIoGetRateToClient( peer->io );
+ stat->rateToPeer = tr_peerGetPieceSpeed( peer, TR_CLIENT_TO_PEER );
+ stat->rateToClient = tr_peerGetPieceSpeed( peer, TR_PEER_TO_CLIENT );
stat->peerIsChoked = peer->peerIsChoked;
stat->peerIsInterested = peer->peerIsInterested;
stat->clientIsChoked = peer->clientIsChoked;
tr_peerMsgsSetChoke( peer->msgs, TRUE );
else
{
- struct ChokeData * node = &choke[size++];
- node->peer = peer;
- node->isInterested = peer->peerIsInterested;
- node->rateToClient = tr_peerIoGetRateToClient( peer->io );
- node->rateToPeer = tr_peerIoGetRateToPeer( peer->io );
+ struct ChokeData * n = &choke[size++];
+ n->peer = peer;
+ n->isInterested = peer->peerIsInterested;
+ n->rateToPeer = tr_peerGetPieceSpeed( peer, TR_CLIENT_TO_PEER );
+ n->rateToClient = tr_peerGetPieceSpeed( peer, TR_PEER_TO_CLIENT );
}
}
{
const double baseline = desiredAvgKB * 1024.0 /
BANDWIDTH_PULSES_PER_SECOND;
- const double min = baseline * 0.66;
- const double max = baseline * 1.33;
+ const double min = baseline * 0.90;
+ const double max = baseline * 1.10;
int i;
double usedBytes;
double n;
/* allocate the upload and download bandwidth */
for( i = 0; i < 2; ++i )
- mgr->rateHistory[i][mgr->bandwidthPulseNumber] =
- allocateBandwidth( mgr, i );
+ allocateBandwidth( mgr, i );
managerUnlock( mgr );
return TRUE;
int * setmePeersGettingFromUs,
int * setmePeersFrom ); /* TR_PEER_FROM__MAX */
-double tr_peerMgrGetRate( const tr_peerMgr * manager,
- tr_direction direction );
-
struct tr_peer_stat* tr_peerMgrPeerStats( const tr_peerMgr * manager,
const uint8_t * torrentHash,
int * setmeCount );
ratePulse( void * vpeer )
{
tr_peermsgs * peer = vpeer;
- const double rateToClient = tr_peerIoGetRateToClient( peer->io );
- const int estimatedBlocksInNext30Seconds =
- ( rateToClient * 30 * 1024 ) / peer->torrent->blockSize;
+ const double rateToClient = tr_peerGetPieceSpeed( peer->info,
+ TR_PEER_TO_CLIENT );
+ const int estimatedBlocksInNext30Seconds =
+ ( rateToClient * 30 * 1024 ) / peer->torrent->blockSize;
peer->minActiveRequests = 4;
peer->maxActiveRequests = peer->minActiveRequests +
typedef struct tr_ratecontrol tr_ratecontrol;
-tr_ratecontrol * tr_rcInit( void );
-void tr_rcTransferred( tr_ratecontrol *,
- size_t byteCount );
+tr_ratecontrol * tr_rcInit ( void );
-float tr_rcRate( const tr_ratecontrol * );
+void tr_rcClose ( tr_ratecontrol * ratecontrol );
+
+void tr_rcTransferred ( tr_ratecontrol * ratecontrol,
+ size_t byteCount );
+
+float tr_rcRate ( const tr_ratecontrol * ratecontrol );
-void tr_rcClose( tr_ratecontrol * );
#endif
#include "peer-mgr.h"
#include "platform.h" /* tr_lock */
#include "port-forwarding.h"
+#include "ratecontrol.h"
#include "rpc-server.h"
#include "stats.h"
#include "torrent.h"
h->isProxyAuthEnabled = proxyAuthIsEnabled != 0;
h->proxyUsername = tr_strdup( proxyUsername );
h->proxyPassword = tr_strdup( proxyPassword );
+ h->pieceSpeed[TR_PEER_TO_CLIENT] = tr_rcInit( );
+ h->pieceSpeed[TR_CLIENT_TO_PEER] = tr_rcInit( );
if( configDir == NULL )
configDir = tr_getDefaultConfigDir( );
float * toPeer )
{
if( session && toClient )
- *toClient = tr_peerMgrGetRate( session->peerMgr, TR_DOWN );
+ *toClient = tr_rcRate( session->pieceSpeed[TR_PEER_TO_CLIENT] );
if( session && toPeer )
- *toPeer = tr_peerMgrGetRate( session->peerMgr, TR_UP );
+ *toPeer = tr_rcRate( session->pieceSpeed[TR_CLIENT_TO_PEER] );
}
int
}
/* free the session memory */
+ tr_rcClose( session->pieceSpeed[TR_PEER_TO_CLIENT] );
+ tr_rcClose( session->pieceSpeed[TR_CLIENT_TO_PEER] );
tr_lockFree( session->lock );
for( i = 0; i < session->metainfoLookupCount; ++i )
tr_free( session->metainfoLookup[i].filename );
char * filename;
};
+struct tr_ratecontrol;
+
struct tr_handle
{
unsigned int isPortSet : 1;
struct tr_metainfo_lookup * metainfoLookup;
int metainfoLookupCount;
+
+ /* the rate at which pieces are being transferred between client and peer.
+ * protocol overhead is NOT included; this is only the piece data */
+ struct tr_ratecontrol * pieceSpeed[2];
};
const char * tr_sessionFindTorrentFile( const tr_session * session,
randomizeTiers( info );
+ tor->pieceSpeed[TR_CLIENT_TO_PEER] = tr_rcInit( );
+ tor->pieceSpeed[TR_PEER_TO_CLIENT] = tr_rcInit( );
+
tor->blockSize = getBlockSize( info->pieceSize );
tor->lastPieceSize = info->totalSize % info->pieceSize;
tr_torrentGetRate( const tr_torrent * tor,
tr_direction direction )
{
- int i;
- double bytes = 0;
-
assert( tor != NULL );
assert( direction == TR_UP || direction == TR_DOWN );
- for( i = 0; i < BANDWIDTH_PULSE_HISTORY; ++i )
- bytes += tor->rateHistory[direction][i];
-
- return ( BANDWIDTH_PULSES_PER_SECOND * bytes )
- / ( BANDWIDTH_PULSE_HISTORY * 1024 );
+ return tr_rcRate( tor->pieceSpeed[direction] );
}
const tr_stat *
assert( h->torrentCount >= 1 );
h->torrentCount--;
+ tr_rcClose( t->pieceSpeed[TR_PEER_TO_CLIENT] );
+ tr_rcClose( t->pieceSpeed[TR_CLIENT_TO_PEER] );
+
tr_metainfoFree( inf );
tr_free( tor );
if( !stat( path, &sb )
&& S_ISREG( sb.st_mode )
- && ( sb.st_size <= it->length ) )
+ && ( (uint64_t)sb.st_size <= it->length ) )
bytesLeft -= sb.st_size;
tr_free( path );
#ifndef TR_TORRENT_H
#define TR_TORRENT_H 1
+struct tr_ratecontrol;
+
/**
*** Package-visible ctor API
**/
int uniqueId;
+ /* this is the count of raw bytes transferred between the
+ * client and its peers over the past HISTORY time slices.
+ * this count is used for bandwidth allocation, and includes
+ * piece data, protocol overhead, and estimated tcp header overhead. */
double rateHistory[2][BANDWIDTH_PULSE_HISTORY];
+
+ /* the rate at which pieces are being transferred between client and
+ * its peers. protocol overhead is NOT included; only the piece data */
+ struct tr_ratecontrol * pieceSpeed[2];
};
#endif