source: trunk/libtransmission/peerutils.h @ 3105

Last change on this file since 3105 was 3105, checked in by livings124, 15 years ago

merge encryption branch to trunk (xcode project is still out of date)

  • Property svn:keywords set to Date Rev Author Id
File size: 9.4 KB
Line 
1/******************************************************************************
2 * $Id: peerutils.h 3105 2007-09-20 16:32:01Z livings124 $
3 *
4 * Copyright (c) 2005-2007 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25static int peerCmp( tr_peer_t * peer1, tr_peer_t * peer2 )
26{
27    /* Wait until we got the peers' ids */
28    if( peer1->status <= PEER_STATUS_HANDSHAKE ||
29        peer2->status <= PEER_STATUS_HANDSHAKE )
30    {
31        return 1;
32    }
33
34    return memcmp( peer1->id, peer2->id, 20 );
35}
36
37static int
38checkPeer( tr_peer_t * peer )
39{
40    int ret;
41    tr_torrent * tor = peer->tor;
42    const uint64_t now = tr_date( );
43    const uint64_t idleTime = now - peer->date;
44    const uint64_t idleSecs = idleTime / 1000u;
45    uint64_t lo, hi, limit;
46    int relaxStrictnessIfFewerThanN;
47    double strictness;
48
49    /* when deciding whether or not to keep a peer, judge its responsiveness
50       on a sliding scale that's based on how many other peers are available */
51    relaxStrictnessIfFewerThanN =
52        (int)(((TR_MAX_PEER_COUNT * PERCENT_PEER_WANTED) / 100.0) + 0.5);
53
54    /* if we have >= relaxIfFewerThan, strictness is 100%.
55       if we have zero connections, strictness is 0% */
56    if( tor->peerCount >= relaxStrictnessIfFewerThanN )
57        strictness = 1.0;
58    else
59        strictness = tor->peerCount / (double)relaxStrictnessIfFewerThanN;
60
61    /* test: has it been too long since we were properly connected to them? */
62    lo = MIN_CON_TIMEOUT;
63    hi = MAX_CON_TIMEOUT;
64    limit = lo + ((hi-lo) * strictness);
65    if( peer->status < PEER_STATUS_CONNECTED && idleTime > limit ) {
66        peer_dbg( "connection timeout, idled %"PRIu64" seconds", idleSecs );
67        return TR_ERROR;
68    }
69
70    /* test: have we been waiting on a request for too long? */
71    lo = MIN_UPLOAD_IDLE; 
72    hi = MAX_UPLOAD_IDLE;
73    limit = lo + ((hi-lo) * strictness);
74    if( peer->inRequestCount && idleTime > limit ) {
75        peer_dbg( "idle uploader timeout, idled %"PRIu64" seconds", idleSecs );
76        return TR_ERROR;
77    }
78
79    /* test: has it been too long since the peer gave us any response at all? */
80    lo = MIN_KEEP_ALIVE;
81    hi = MAX_KEEP_ALIVE;
82    limit = lo + ((hi-lo) * strictness);
83    if( idleTime > limit ) {
84        peer_dbg( "peer timeout, idled %"PRIu64" seconds", idleSecs );
85        return TR_ERROR;
86    }
87
88    if( PEER_STATUS_CONNECTED == peer->status )
89    {
90        /* Send keep-alive every 1 minute and 45 seconds */
91        if( now > peer->keepAlive + 105000 )
92        {
93            sendKeepAlive( peer );
94            peer->keepAlive = now;
95        }
96
97        /* Resend extended handshake if our public port changed */
98        if( EXTENDED_HANDSHAKE == peer->extStatus && 
99            tor->publicPort != peer->advertisedPort )
100        {
101            sendExtended( tor, peer, EXTENDED_HANDSHAKE_ID );
102        }
103
104        /* Send peer list */
105        if( !peer->private && 0 < peer->pexStatus )
106        {
107            if( 0 == peer->lastPex )
108            {
109                /* randomize time when first pex message is sent */
110                peer->lastPex = now - 1000 * tr_rand( PEX_INTERVAL );
111            }
112            if( now > peer->lastPex + 1000 * PEX_INTERVAL )
113            {
114                if( EXTENDED_HANDSHAKE == peer->extStatus )
115                {
116                    ret = sendExtended( tor, peer, EXTENDED_PEX_ID );
117                }
118                else if( peer->azproto )
119                {
120                    ret = sendAZPex( tor, peer );
121                }
122                else
123                {
124                    assert( 0 );
125                    ret = TR_ERROR;
126                }
127                if( ret )
128                {
129                    return ret;
130                }
131                peer->lastPex = now + 1000 *
132                    ( PEX_INTERVAL + tr_rand( PEX_INTERVAL / 10 ) );
133            }
134        }
135    }
136
137    return TR_OK;
138}
139
140static int isPieceInteresting( const tr_torrent  * tor,
141                               const tr_peer_t   * peer,
142                               int                 piece )
143{
144    if( tor->info.pieces[piece].dnd ) /* we don't want it */
145        return 0;
146
147    if( tr_cpPieceIsComplete( tor->completion, piece ) ) /* we already have it */
148        return 0;
149
150    if( !tr_bitfieldHas( peer->bitfield, piece ) ) /* peer doesn't have it */
151        return 0;
152
153    if( tr_bitfieldHas( peer->banfield, piece ) ) /* peer is banned for it */
154        return 0;
155
156    return 1;
157}
158
159/***********************************************************************
160 * isInteresting
161 ***********************************************************************
162 * Returns 1 if 'peer' has at least one piece that we want but
163 * haven't completed, or 0 otherwise.
164 **********************************************************************/
165static int isInteresting( const tr_torrent * tor, const tr_peer_t * peer )
166{
167    int i;
168    const tr_bitfield   * bitfield = tr_cpPieceBitfield( tor->completion );
169
170    if( !peer->bitfield )
171    {
172        /* We don't know what this peer has */
173        return 0;
174    }
175
176    assert( bitfield->len == peer->bitfield->len );
177
178    for( i=0; i<tor->info.pieceCount; ++i )
179        if( isPieceInteresting( tor, peer, i ) )
180            return 1;
181
182    return 0;
183}
184
185static void
186updateInterest( tr_torrent * tor, tr_peer_t * peer )
187{
188    const int i = !!isInteresting( tor, peer );
189
190    if( i != peer->isInteresting )
191        sendInterest( peer, i );
192}
193
194/** utility structure used by getPreferredPieces() and comparePieces() */
195typedef struct
196{
197    int piece;
198    tr_priority_t priority;
199    int missingBlockCount;
200    int peerCount;
201}
202PieceCompareData;
203
204/** utility function used by getPreferredPieces */
205int comparePieces (const void * aIn, const void * bIn)
206{
207    const PieceCompareData * a = (const PieceCompareData*) aIn;
208    const PieceCompareData * b = (const PieceCompareData*) bIn;
209
210    /* if one piece has a higher priority, it goes first */
211    if (a->priority != b->priority)
212        return a->priority > b->priority ? -1 : 1;
213
214    /* otherwise if one has fewer missing blocks, it goes first */
215    if (a->missingBlockCount != b->missingBlockCount)
216        return a->missingBlockCount < b->missingBlockCount ? -1 : 1;
217
218    /* otherwise if one has fewer peers, it goes first */
219    if (a->peerCount != b->peerCount)
220        return a->peerCount < b->peerCount ? -1 : 1;
221
222    /* otherwise go with the earlier piece */
223    return a->piece - b->piece;
224}
225
226static int* getPreferredPieces( const tr_torrent  * tor,
227                                const tr_peer_t   * peer,
228                                int               * pieceCount,
229                                int               * isEndgame )
230{
231    const tr_info * inf = &tor->info;
232
233    int i;
234    int poolSize = 0;
235    int endgame = FALSE;
236    int * pool = tr_new( int, inf->pieceCount );
237
238    for( i=0; i<inf->pieceCount; ++i )
239        if( isPieceInteresting( tor, peer, i ) )
240            if( tr_cpMissingBlocksForPiece( tor->completion, i ) )
241                pool[poolSize++] = i;
242
243    if( !poolSize ) {
244        endgame = TRUE;
245        for( i=0; i<inf->pieceCount; ++i )
246            if( isPieceInteresting( tor, peer, i ) )
247                pool[poolSize++] = i;
248    }
249
250#if 0
251fprintf (stderr, "old pool: ");
252for (i=0; i<15 && i<poolSize; ++i ) fprintf (stderr, "%d, ", pool[i] );
253fprintf (stderr, "\n");
254#endif
255
256    /* sort the rest from most interesting to least...
257       but not in endgame, because it asks for pieces in a
258       scattershot manner anyway and doesn't need them sorted */
259    if( !endgame && ( poolSize > 1 ) )
260    {
261        PieceCompareData * p = tr_new( PieceCompareData, poolSize );
262
263        for( i=0; i<poolSize; ++i )
264        {
265            int j;
266            const int piece = pool[i];
267
268            p[i].piece = piece;
269            p[i].priority = inf->pieces[piece].priority;
270            p[i].missingBlockCount = tr_cpMissingBlocksForPiece( tor->completion, piece );
271            p[i].peerCount = 0;
272
273            for( j=0; j<tor->peerCount; ++j )
274                if( tr_bitfieldHas( tor->peers[j]->bitfield, piece ) )
275                    ++p[i].peerCount;
276        }
277
278        qsort (p, poolSize, sizeof(PieceCompareData), comparePieces);
279
280        for( i=0; i<poolSize; ++i )
281            pool[i] = p[i].piece;
282
283        tr_free( p );
284    }
285
286#if 0
287fprintf (stderr, "new pool: ");
288for (i=0; i<15 && i<poolSize; ++i ) fprintf (stderr, "%d, ", pool[i] );
289fprintf (stderr, "\n");
290#endif
291
292    *isEndgame = endgame;
293    *pieceCount = poolSize;
294    return pool;
295}
Note: See TracBrowser for help on using the repository browser.