* improve the unchoking algorithm.
* when deciding which peers to connect to, take peer's previous speeds into account * longer delay interval before reconnecting to peers that didn't give us data * added uTorrent-inspired "flags" column in tr_peer_stat and gtk's torrent details' "peer" tab
This commit is contained in:
parent
c4cba5ad2a
commit
591f7f8b10
2
NEWS
2
NEWS
|
@ -12,7 +12,7 @@ NEWS file for Transmission <http://transmission.m0k.org/>
|
|||
- OS X:
|
||||
+ Leopard: Better behavior with Time Machine
|
||||
- GTK+:
|
||||
+ New Chinese, Dutch, and Turkish translations
|
||||
+ New Brazilian Portuguese, Chinese, Dutch, and Turkish translations
|
||||
+ Fix 1.00 desktop internationalization error
|
||||
|
||||
1.00 (2008/01/04)
|
||||
|
|
|
@ -290,7 +290,7 @@ peer_row_set (GtkTreeStore * store,
|
|||
PEER_COL_DOWNLOAD_RATE, peer->downloadFromRate,
|
||||
PEER_COL_IS_UPLOADING, peer->isUploading,
|
||||
PEER_COL_UPLOAD_RATE, peer->uploadToRate,
|
||||
PEER_COL_STATUS, peer->status,
|
||||
PEER_COL_STATUS, peer->flagStr,
|
||||
-1);
|
||||
}
|
||||
|
||||
|
@ -311,16 +311,16 @@ static GtkTreeModel*
|
|||
peer_model_new (tr_torrent * tor)
|
||||
{
|
||||
GtkTreeStore * m = gtk_tree_store_new (N_PEER_COLS,
|
||||
G_TYPE_STRING, /* addr */
|
||||
G_TYPE_INT, /* port */
|
||||
G_TYPE_STRING, /* client */
|
||||
G_TYPE_INT, /* progress [0..100] */
|
||||
G_TYPE_BOOLEAN, /* isEncrypted */
|
||||
G_TYPE_BOOLEAN, /* isDownloading */
|
||||
G_TYPE_FLOAT, /* downloadFromRate */
|
||||
G_TYPE_BOOLEAN, /* isUploading */
|
||||
G_TYPE_FLOAT, /* uploadToRate */
|
||||
G_TYPE_INT ); /* tr_peer_status */
|
||||
G_TYPE_STRING, /* addr */
|
||||
G_TYPE_INT, /* port */
|
||||
G_TYPE_STRING, /* client */
|
||||
G_TYPE_INT, /* progress [0..100] */
|
||||
G_TYPE_BOOLEAN, /* isEncrypted */
|
||||
G_TYPE_BOOLEAN, /* isDownloading */
|
||||
G_TYPE_FLOAT, /* downloadFromRate */
|
||||
G_TYPE_BOOLEAN, /* isUploading */
|
||||
G_TYPE_FLOAT, /* uploadToRate */
|
||||
G_TYPE_STRING ); /* flagString */
|
||||
|
||||
int n_peers = 0;
|
||||
tr_peer_stat * peers = tr_torrentPeers (tor, &n_peers);
|
||||
|
@ -346,46 +346,6 @@ render_encrypted (GtkTreeViewColumn * column UNUSED,
|
|||
NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
render_status( GtkTreeViewColumn * column UNUSED,
|
||||
GtkCellRenderer * renderer,
|
||||
GtkTreeModel * tree_model,
|
||||
GtkTreeIter * iter,
|
||||
gpointer data UNUSED )
|
||||
{
|
||||
GString * gstr = g_string_new( NULL );
|
||||
int status;
|
||||
gtk_tree_model_get( tree_model, iter, PEER_COL_STATUS, &status, -1 );
|
||||
|
||||
if( status & TR_PEER_STATUS_HANDSHAKE )
|
||||
{
|
||||
g_string_append( gstr, _("Handshaking") );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( status & TR_PEER_STATUS_CLIENT_IS_SENDING )
|
||||
g_string_append( gstr, _("Uploading to peer") );
|
||||
else if( status & TR_PEER_STATUS_PEER_IS_INTERESTED )
|
||||
g_string_append( gstr, _("Peer wants our data") );
|
||||
else if( status & TR_PEER_STATUS_PEER_IS_CHOKED )
|
||||
g_string_append( gstr, _("Refusing to send data to peer") );
|
||||
|
||||
g_string_append( gstr, " - " );
|
||||
|
||||
if( status & TR_PEER_STATUS_PEER_IS_SENDING )
|
||||
g_string_append( gstr, _("Downloading from peer") );
|
||||
else if( status & TR_PEER_STATUS_CLIENT_SENT_REQUEST )
|
||||
g_string_append( gstr, _("Requesting data from peer") );
|
||||
else if( status & TR_PEER_STATUS_CLIENT_IS_INTERESTED )
|
||||
g_string_append( gstr, _("Waiting to request data from peer") );
|
||||
else if( status & TR_PEER_STATUS_CLIENT_IS_CHOKED )
|
||||
g_string_append( gstr, _("Peer will not send us data") );
|
||||
}
|
||||
|
||||
g_object_set( renderer, "text", gstr->str, NULL );
|
||||
g_string_free( gstr, TRUE );
|
||||
}
|
||||
|
||||
static void
|
||||
render_ul_rate (GtkTreeViewColumn * column UNUSED,
|
||||
GtkCellRenderer * renderer,
|
||||
|
@ -539,11 +499,8 @@ static GtkWidget* peer_page_new ( TrTorrent * gtor )
|
|||
PEER_COL_PROGRESS,
|
||||
PEER_COL_IS_ENCRYPTED,
|
||||
PEER_COL_UPLOAD_RATE,
|
||||
PEER_COL_DOWNLOAD_RATE
|
||||
#if 0
|
||||
, PEER_COL_STATUS
|
||||
#endif
|
||||
};
|
||||
PEER_COL_DOWNLOAD_RATE,
|
||||
PEER_COL_STATUS };
|
||||
|
||||
m = peer_model_new (tor);
|
||||
v = gtk_tree_view_new_with_model (m);
|
||||
|
@ -620,9 +577,7 @@ static GtkWidget* peer_page_new ( TrTorrent * gtor )
|
|||
case PEER_COL_STATUS:
|
||||
r = gtk_cell_renderer_text_new( );
|
||||
c = gtk_tree_view_column_new_with_attributes (t, r, "text", col, NULL);
|
||||
gtk_tree_view_column_set_cell_data_func (c, r, render_status, NULL, NULL);
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
abort ();
|
||||
|
|
|
@ -57,8 +57,6 @@ typedef struct tr_peer
|
|||
unsigned int clientIsInterested : 1;
|
||||
unsigned int doPurge : 1;
|
||||
|
||||
tr_peer_status status;
|
||||
|
||||
/* number of bad pieces they've contributed to */
|
||||
uint8_t strikes;
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ static const int SWIFT_PERIOD_MSEC = 5000;
|
|||
enum
|
||||
{
|
||||
/* how frequently to change which peers are choked */
|
||||
RECHOKE_PERIOD_MSEC = (1000),
|
||||
RECHOKE_PERIOD_MSEC = (10 * 1000),
|
||||
|
||||
/* how frequently to decide which peers live and die */
|
||||
RECONNECT_PERIOD_MSEC = (2 * 1000),
|
||||
|
@ -80,9 +80,6 @@ enum
|
|||
/* how frequently to refill peers' request lists */
|
||||
REFILL_PERIOD_MSEC = 666,
|
||||
|
||||
/* don't change a peer's choke status more often than this */
|
||||
MIN_CHOKE_PERIOD_SEC = 10,
|
||||
|
||||
/* following the BT spec, we consider ourselves `snubbed' if
|
||||
* we're we don't get piece data from a peer in this long */
|
||||
SNUBBED_SEC = 60,
|
||||
|
@ -1462,21 +1459,38 @@ tr_peerMgrPeerStats( const tr_peerMgr * manager,
|
|||
|
||||
for( i=0; i<size; ++i )
|
||||
{
|
||||
char * pch;
|
||||
const tr_peer * peer = peers[i];
|
||||
const struct peer_atom * atom = getExistingAtom( t, &peer->in_addr );
|
||||
tr_peer_stat * stat = ret + i;
|
||||
|
||||
tr_netNtop( &peer->in_addr, stat->addr, sizeof(stat->addr) );
|
||||
strlcpy( stat->client, (peer->client ? peer->client : ""), sizeof(stat->client) );
|
||||
stat->port = peer->port;
|
||||
stat->from = atom->from;
|
||||
stat->progress = peer->progress;
|
||||
stat->isEncrypted = tr_peerIoIsEncrypted( peer->io ) ? 1 : 0;
|
||||
stat->uploadToRate = peer->rateToPeer;
|
||||
stat->downloadFromRate = peer->rateToClient;
|
||||
stat->isDownloading = stat->uploadToRate > 0.01;
|
||||
stat->isUploading = stat->downloadFromRate > 0.01;
|
||||
stat->status = peer->status;
|
||||
stat->port = peer->port;
|
||||
stat->from = atom->from;
|
||||
stat->progress = peer->progress;
|
||||
stat->isEncrypted = tr_peerIoIsEncrypted( peer->io ) ? 1 : 0;
|
||||
stat->uploadToRate = peer->rateToPeer;
|
||||
stat->downloadFromRate = peer->rateToClient;
|
||||
stat->peerIsChoked = peer->peerIsChoked;
|
||||
stat->peerIsInterested = peer->peerIsInterested;
|
||||
stat->clientIsChoked = peer->clientIsChoked;
|
||||
stat->clientIsInterested = peer->clientIsInterested;
|
||||
stat->isIncoming = tr_peerIoIsIncoming( peer->io );
|
||||
stat->isDownloading = stat->clientIsInterested && !stat->clientIsChoked;
|
||||
stat->isUploading = stat->peerIsInterested && !stat->peerIsChoked;
|
||||
|
||||
pch = stat->flagStr;
|
||||
if( stat->isDownloading ) *pch++ = 'D';
|
||||
else if( stat->clientIsInterested ) *pch++ = 'd';
|
||||
if( stat->isUploading ) *pch++ = 'U';
|
||||
else if( stat->peerIsInterested ) *pch++ = 'u';
|
||||
if( !stat->clientIsChoked && !stat->clientIsInterested ) *pch++ = 'K';
|
||||
if( !stat->peerIsChoked && !stat->peerIsInterested ) *pch++ = '?';
|
||||
if( stat->isEncrypted ) *pch++ = 'E';
|
||||
if( stat->from == TR_PEER_FROM_PEX ) *pch++ = 'X';
|
||||
if( stat->isIncoming ) *pch++ = 'I';
|
||||
*pch = '\0';
|
||||
}
|
||||
|
||||
*setmeCount = size;
|
||||
|
@ -1492,11 +1506,10 @@ tr_peerMgrPeerStats( const tr_peerMgr * manager,
|
|||
|
||||
struct ChokeData
|
||||
{
|
||||
tr_peer * peer;
|
||||
int rate;
|
||||
uint8_t preferred;
|
||||
uint8_t preferred_t;
|
||||
uint8_t doUnchoke;
|
||||
uint8_t isInterested;
|
||||
uint32_t rate;
|
||||
tr_peer * peer;
|
||||
};
|
||||
|
||||
static int
|
||||
|
@ -1504,20 +1517,7 @@ compareChoke( const void * va, const void * vb )
|
|||
{
|
||||
const struct ChokeData * a = va;
|
||||
const struct ChokeData * b = vb;
|
||||
if( a->rate > b->rate ) return -1;
|
||||
if( a->rate < b->rate ) return 1;
|
||||
if( a->preferred_t != b->preferred_t ) return a->preferred_t ? -1 : 1;
|
||||
if( a->preferred != b->preferred ) return a->preferred ? -1 : 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
clientIsSnubbedBy( const tr_peer * peer, int clientIsSeed )
|
||||
{
|
||||
assert( peer != NULL );
|
||||
|
||||
return !clientIsSeed
|
||||
&& ( peer->peerSentPieceDataAt < (time(NULL) - SNUBBED_SEC ) );
|
||||
return -tr_compareUint32( a->rate, b->rate );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1525,7 +1525,7 @@ clientIsSnubbedBy( const tr_peer * peer, int clientIsSeed )
|
|||
**/
|
||||
|
||||
static double
|
||||
getWeightedThroughput( const tr_peer * peer, int clientIsSeed )
|
||||
getWeightedRate( const tr_peer * peer, int clientIsSeed )
|
||||
{
|
||||
return (int)( 10.0 * ( clientIsSeed ? peer->rateToPeer
|
||||
: peer->rateToClient ) );
|
||||
|
@ -1534,8 +1534,7 @@ getWeightedThroughput( const tr_peer * peer, int clientIsSeed )
|
|||
static void
|
||||
rechoke( Torrent * t )
|
||||
{
|
||||
int i, peerCount, size=0, unchoked=0;
|
||||
const time_t fibrillationTime = time(NULL) - MIN_CHOKE_PERIOD_SEC;
|
||||
int i, n, peerCount, size, unchokedInterested;
|
||||
tr_peer ** peers = getConnectedPeers( t, &peerCount );
|
||||
struct ChokeData * choke = tr_new0( struct ChokeData, peerCount );
|
||||
const int clientIsSeed = tr_torrentIsSeed( t->tor );
|
||||
|
@ -1543,35 +1542,48 @@ rechoke( Torrent * t )
|
|||
assert( torrentIsLocked( t ) );
|
||||
|
||||
/* sort the peers by preference and rate */
|
||||
for( i=0; i<peerCount; ++i )
|
||||
for( i=0, size=0; i<peerCount; ++i )
|
||||
{
|
||||
tr_peer * peer = peers[i];
|
||||
struct ChokeData * node;
|
||||
if( peer->chokeChangedAt > fibrillationTime ) {
|
||||
if( !peer->peerIsChoked )
|
||||
++unchoked;
|
||||
continue;
|
||||
if( peer->progress >= 1.0 ) /* choke all seeds */
|
||||
tr_peerMsgsSetChoke( peer->msgs, TRUE );
|
||||
else {
|
||||
struct ChokeData * node = &choke[size++];
|
||||
node->peer = peer;
|
||||
node->isInterested = peer->peerIsInterested;
|
||||
node->rate = getWeightedRate( peer, clientIsSeed );
|
||||
}
|
||||
|
||||
node = &choke[size++];
|
||||
node->peer = peer;
|
||||
node->preferred = peer->peerIsInterested && !clientIsSnubbedBy( peer, clientIsSeed );
|
||||
node->preferred_t = node->preferred && peer->client && strstr( peer->client, "Transmission" );
|
||||
node->rate = getWeightedThroughput( peer, clientIsSeed );
|
||||
}
|
||||
|
||||
qsort( choke, size, sizeof(struct ChokeData), compareChoke );
|
||||
|
||||
for( i=0; i<size && unchoked<t->tor->maxUnchokedPeers; ++i ) {
|
||||
/**
|
||||
* Reciprocation and number of uploads capping is managed by unchoking
|
||||
* the N peers which have the best upload rate and are interested.
|
||||
* This maximizes the client's download rate. These N peers are
|
||||
* referred to as downloaders, because they are interested in downloading
|
||||
* from the client.
|
||||
*
|
||||
* Peers which have a better upload rate (as compared to the downloaders)
|
||||
* but aren't interested get unchoked. If they become interested, the
|
||||
* downloader with the worst upload rate gets choked. If a client has
|
||||
* a complete file, it uses its upload rate rather than its download
|
||||
* rate to decide which peers to unchoke.
|
||||
*/
|
||||
unchokedInterested = 0;
|
||||
for( i=0; i<size && unchokedInterested<t->tor->maxUnchokedPeers; ++i ) {
|
||||
choke[i].doUnchoke = 1;
|
||||
++unchoked;
|
||||
if( choke[i].isInterested )
|
||||
++unchokedInterested;
|
||||
}
|
||||
n = i;
|
||||
while( i<size )
|
||||
choke[i++].doUnchoke = 0;
|
||||
|
||||
for( ; i<size; ++i ) {
|
||||
++unchoked;
|
||||
/* optimistic unchoke */
|
||||
if( i != size ) {
|
||||
i = n + tr_rand( size - n );
|
||||
choke[i].doUnchoke = 1;
|
||||
if( choke[i].peer->peerIsInterested )
|
||||
break;
|
||||
}
|
||||
|
||||
for( i=0; i<size; ++i )
|
||||
|
@ -1718,6 +1730,9 @@ compareCandidates( const void * va, const void * vb )
|
|||
const struct peer_atom * b = * (const struct peer_atom**) vb;
|
||||
int i;
|
||||
|
||||
if( a->piece_data_time > b->piece_data_time ) return -1;
|
||||
if( a->piece_data_time < b->piece_data_time ) return 1;
|
||||
|
||||
if(( i = tr_compareUint16( a->numFails, b->numFails )))
|
||||
return i;
|
||||
|
||||
|
@ -1764,16 +1779,16 @@ getPeerCandidates( Torrent * t, int * setmeSize )
|
|||
continue;
|
||||
|
||||
/* we're wasting our time trying to connect to this bozo. */
|
||||
if( atom->numFails > 10 )
|
||||
if( atom->numFails > 5 )
|
||||
continue;
|
||||
|
||||
/* If we were connected to this peer recently and transferring
|
||||
* piece data, try to reconnect -- network troubles may have
|
||||
* disconnected us. but if we weren't sharing piece data,
|
||||
* hold off on this peer to give another one a try instead */
|
||||
if( ( now - atom->piece_data_time ) > 15 )
|
||||
if( ( now - atom->piece_data_time ) > 30 )
|
||||
{
|
||||
int minWait = 60;
|
||||
int minWait = (60 * 2); /* two minutes */
|
||||
int maxWait = (60 * 20); /* twenty minutes */
|
||||
int wait = atom->numFails * 30; /* add 30 secs to the wait interval for each consecutive failure*/
|
||||
if( wait < minWait ) wait = minWait;
|
||||
|
|
|
@ -1580,40 +1580,6 @@ popNextRequest( tr_peermsgs * msgs )
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
updatePeerStatus( tr_peermsgs * msgs )
|
||||
{
|
||||
const time_t now = time( NULL );
|
||||
tr_peer * peer = msgs->info;
|
||||
tr_peer_status status = 0;
|
||||
|
||||
if( !msgs->peerSentBitfield )
|
||||
status |= TR_PEER_STATUS_HANDSHAKE;
|
||||
|
||||
if( msgs->info->peerIsChoked )
|
||||
status |= TR_PEER_STATUS_PEER_IS_CHOKED;
|
||||
|
||||
if( msgs->info->peerIsInterested )
|
||||
status |= TR_PEER_STATUS_PEER_IS_INTERESTED;
|
||||
|
||||
if( msgs->info->clientIsChoked )
|
||||
status |= TR_PEER_STATUS_CLIENT_IS_CHOKED;
|
||||
|
||||
if( msgs->info->clientIsInterested )
|
||||
status |= TR_PEER_STATUS_CLIENT_IS_INTERESTED;
|
||||
|
||||
if( ( now - msgs->clientSentPieceDataAt ) < 3 )
|
||||
status |= TR_PEER_STATUS_CLIENT_IS_SENDING;
|
||||
|
||||
if( ( now - msgs->peerSentPieceDataAt ) < 3 )
|
||||
status |= TR_PEER_STATUS_PEER_IS_SENDING;
|
||||
|
||||
if( msgs->clientAskedFor != NULL )
|
||||
status |= TR_PEER_STATUS_CLIENT_SENT_REQUEST;
|
||||
|
||||
peer->status = status;
|
||||
}
|
||||
|
||||
static int
|
||||
pulse( void * vmsgs )
|
||||
{
|
||||
|
@ -1624,7 +1590,6 @@ pulse( void * vmsgs )
|
|||
tr_peerIoTryRead( msgs->io );
|
||||
pumpRequestQueue( msgs );
|
||||
expireOldRequests( msgs );
|
||||
updatePeerStatus( msgs );
|
||||
|
||||
if( msgs->sendingBlock )
|
||||
{
|
||||
|
@ -1884,7 +1849,6 @@ tr_peerMsgsNew( struct tr_torrent * torrent,
|
|||
m->handle = torrent->handle;
|
||||
m->torrent = torrent;
|
||||
m->io = info->io;
|
||||
m->info->status = TR_PEER_STATUS_HANDSHAKE;
|
||||
m->info->clientIsChoked = 1;
|
||||
m->info->peerIsChoked = 1;
|
||||
m->info->clientIsInterested = 0;
|
||||
|
|
|
@ -777,21 +777,6 @@ struct tr_file_stat
|
|||
float progress;
|
||||
};
|
||||
|
||||
typedef enum
|
||||
{
|
||||
TR_PEER_STATUS_HANDSHAKE = (1<<0), /* we're handshaking with peer */
|
||||
|
||||
TR_PEER_STATUS_PEER_IS_SENDING = (1<<1), /* peer is sending data to us */
|
||||
TR_PEER_STATUS_PEER_IS_INTERESTED = (1<<2), /* we have data the peer wants */
|
||||
TR_PEER_STATUS_PEER_IS_CHOKED = (1<<3), /* we refuse to send data to the peer */
|
||||
|
||||
TR_PEER_STATUS_CLIENT_IS_SENDING = (1<<4), /* we're sending data to the peer */
|
||||
TR_PEER_STATUS_CLIENT_SENT_REQUEST = (1<<5), /* we've sent the peer a request */
|
||||
TR_PEER_STATUS_CLIENT_IS_INTERESTED = (1<<6), /* peer has data that we want */
|
||||
TR_PEER_STATUS_CLIENT_IS_CHOKED = (1<<7), /* peer refuses to send data to us */
|
||||
}
|
||||
tr_peer_status;
|
||||
|
||||
struct tr_peer_stat
|
||||
{
|
||||
char addr[INET_ADDRSTRLEN];
|
||||
|
@ -801,7 +786,13 @@ struct tr_peer_stat
|
|||
unsigned int isDownloading : 1;
|
||||
unsigned int isUploading : 1;
|
||||
|
||||
tr_peer_status status;
|
||||
unsigned int peerIsChoked : 1;
|
||||
unsigned int peerIsInterested : 1;
|
||||
unsigned int clientIsChoked : 1;
|
||||
unsigned int clientIsInterested : 1;
|
||||
unsigned int isIncoming : 1;
|
||||
|
||||
char flagStr[32];
|
||||
|
||||
uint8_t from;
|
||||
uint16_t port;
|
||||
|
|
Loading…
Reference in New Issue