source: trunk/libtransmission/peerutils.h @ 26

Last change on this file since 26 was 26, checked in by root, 16 years ago

Update 2006-01-11

File size: 11.7 KB
Line 
1/******************************************************************************
2 * Copyright (c) 2005 Eric Petit
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 * DEALINGS IN THE SOFTWARE.
21 *****************************************************************************/
22
23static void updateInterest( tr_torrent_t * tor, tr_peer_t * peer );
24
25/***********************************************************************
26 * peerInit
27 ***********************************************************************
28 * Allocates a new tr_peer_t and returns a pointer to it.
29 **********************************************************************/
30static tr_peer_t * peerInit()
31{
32    tr_peer_t * peer;
33
34    peer              = calloc( sizeof( tr_peer_t ), 1 );
35    peer->amChoking   = 1;
36    peer->peerChoking = 1;
37    peer->date        = tr_date();
38    peer->keepAlive   = peer->date;
39
40    return peer;
41}
42
43/***********************************************************************
44 * peerAttach
45 ***********************************************************************
46 * Deallocates the tr_peer_t and returns 0 if we reached the maximum
47 * authorized number of peers. Otherwise, adds the tr_peer_t to the
48 * peers list.
49 **********************************************************************/
50static int peerAttach( tr_torrent_t * tor, tr_peer_t * peer )
51{
52    if( tor->peerCount >= TR_MAX_PEER_COUNT )
53    {
54        tr_peerDestroy( tor->fdlimit, peer );
55        return 0;
56    }
57
58    tor->peers[tor->peerCount++] = peer;
59    return 1;
60}
61
62static int peerCmp( tr_peer_t * peer1, tr_peer_t * peer2 )
63{
64    /* Wait until we got the peers' ids */
65    if( peer1->status < PEER_STATUS_CONNECTED ||
66        peer2->status < PEER_STATUS_CONNECTED )
67    {
68        return 1;
69    }
70
71    return memcmp( peer1->id, peer2->id, 20 );
72}
73
74/***********************************************************************
75 * addWithAddr
76 ***********************************************************************
77 * Does nothing if we already have a peer matching 'addr' and 'port'.
78 * Otherwise adds such a new peer.
79 **********************************************************************/
80static void addWithAddr( tr_torrent_t * tor, struct in_addr addr,
81                         in_port_t port )
82{
83    int i;
84    tr_peer_t * peer;
85
86    for( i = 0; i < tor->peerCount; i++ )
87    {
88        peer = tor->peers[i];
89        if( peer->addr.s_addr == addr.s_addr &&
90            peer->port        == port )
91        {
92            /* We are already connected to this peer */
93            return;
94        }
95    }
96
97    peer = peerInit();
98    if( !peerAttach( tor, peer ) )
99    {
100        return;
101    }
102
103    peer->addr   = addr;
104    peer->port   = port;
105    peer->status = PEER_STATUS_IDLE;
106}
107
108static int checkPeer( tr_torrent_t * tor, int i )
109{
110    tr_peer_t * peer = tor->peers[i];
111
112    if( peer->status < PEER_STATUS_CONNECTED &&
113        tr_date() > peer->date + 8000 )
114    {
115        /* If it has been too long, don't wait for the socket
116           to timeout - forget about it now */
117        peer_dbg( "connection timeout" );
118        return 1;
119    }
120
121    /* Drop peers who haven't even sent a keep-alive within the
122       last 3 minutes */
123    if( tr_date() > peer->date + 180000 )
124    {
125        peer_dbg( "read timeout" );
126        return 1;
127    }
128
129    /* Drop peers which are supposed to upload but actually
130       haven't sent anything within the last minute */
131    if( peer->inRequestCount && tr_date() > peer->date + 60000 )
132    {
133        peer_dbg( "bad uploader" );
134        return 1;
135    }
136
137    /* TODO: check for bad downloaders */
138
139#if 0
140    /* Choke unchoked peers we are not sending anything to */
141    if( !peer->amChoking && tr_date() > peer->outDate + 10000 )
142    {
143        peer_dbg( "not worth the unchoke" );
144        if( sendChoke( peer, 1 ) )
145        {
146            goto dropPeer;
147        }
148        peer->outSlow = 1;
149        tr_uploadChoked( tor->upload );
150    }
151#endif
152
153    if( peer->status & PEER_STATUS_CONNECTED )
154    {
155        /* Send keep-alive every 2 minutes */
156        if( tr_date() > peer->keepAlive + 120000 )
157        {
158            sendKeepAlive( peer );
159            peer->keepAlive = tr_date();
160        }
161
162        /* Choke or unchoke some people */
163        /* TODO: prefer people who upload to us */
164        if( !peer->amChoking && !peer->peerInterested )
165        {
166            /* He doesn't need us */
167            sendChoke( peer, 1 );
168            tr_uploadChoked( tor->upload );
169        }
170        if( peer->amChoking && peer->peerInterested &&
171            !peer->outSlow && tr_uploadCanUnchoke( tor->upload ) )
172        {
173            sendChoke( peer, 0 );
174            tr_uploadUnchoked( tor->upload );
175        }
176    }
177
178    /* Connect */
179    if( ( peer->status & PEER_STATUS_IDLE ) &&
180        !tr_fdSocketWillCreate( tor->fdlimit, 0 ) )
181    {
182        peer->socket = tr_netOpen( peer->addr, peer->port );
183        if( peer->socket < 0 )
184        {
185            peer_dbg( "connection failed" );
186            tr_fdSocketClosed( tor->fdlimit, 0 );
187            return 1;
188        }
189        peer->status = PEER_STATUS_CONNECTING;
190    }
191
192    /* Try to send handshake */
193    if( peer->status & PEER_STATUS_CONNECTING )
194    {
195        uint8_t buf[68];
196        tr_info_t * inf = &tor->info;
197        int ret;
198
199        buf[0] = 19;
200        memcpy( &buf[1], "BitTorrent protocol", 19 );
201        memset( &buf[20], 0, 8 );
202        memcpy( &buf[28], inf->hash, 20 );
203        memcpy( &buf[48], tor->id, 20 );
204
205        ret = tr_netSend( peer->socket, buf, 68 );
206        if( ret & TR_NET_CLOSE )
207        {
208            peer_dbg( "connection closed" );
209            return 1;
210        }
211        else if( !( ret & TR_NET_BLOCK ) )
212        {
213            peer_dbg( "SEND handshake" );
214            peer->status = PEER_STATUS_HANDSHAKE;
215        }
216    }
217
218    return 0;
219}
220
221/***********************************************************************
222 * isInteresting
223 ***********************************************************************
224 * Returns 1 if 'peer' has at least one piece that we haven't completed,
225 * or 0 otherwise.
226 **********************************************************************/
227static int isInteresting( tr_torrent_t * tor, tr_peer_t * peer )
228{
229    tr_info_t * inf = &tor->info;
230
231    int i;
232    int bitfieldSize = ( inf->pieceCount + 7 ) / 8;
233    uint8_t * bitfield = tr_cpPieceBitfield( tor->completion );
234
235    if( !peer->bitfield )
236    {
237        /* We don't know what this peer has */
238        return 0;
239    }
240
241    for( i = 0; i < bitfieldSize; i++ )
242    {
243        if( ( peer->bitfield[i] & ~(bitfield[i]) ) & 0xFF )
244        {
245            return 1;
246        }
247    }
248
249    return 0;
250}
251static void updateInterest( tr_torrent_t * tor, tr_peer_t * peer )
252{
253    int interested = isInteresting( tor, peer );
254
255    if( interested && !peer->amInterested )
256    {
257        sendInterest( peer, 1 );
258    }
259    if( !interested && peer->amInterested )
260    {
261        sendInterest( peer, 0 );
262    }
263}
264
265/***********************************************************************
266 * chooseBlock
267 ***********************************************************************
268 * At this point, we know the peer has at least one block we have an
269 * interest in. If he has more than one, we choose which one we are
270 * going to ask first.
271 * Our main goal is to complete pieces, so we look the pieces which are
272 * missing less blocks.
273 **********************************************************************/
274static inline int chooseBlock( tr_torrent_t * tor, tr_peer_t * peer )
275{
276    tr_info_t * inf = &tor->info;
277
278    int i;
279    int missingBlocks, minMissing;
280    int poolSize, * pool;
281    int block, minDownloading;
282
283    /* Choose a piece */
284    pool       = malloc( inf->pieceCount * sizeof( int ) );
285    poolSize   = 0;
286    minMissing = tor->blockCount + 1;
287    for( i = 0; i < inf->pieceCount; i++ )
288    {
289        missingBlocks = tr_cpMissingBlocksForPiece( tor->completion, i );
290        if( missingBlocks < 1 )
291        {
292            /* We already have or are downloading all blocks */
293            continue;
294        }
295        if( !tr_bitfieldHas( peer->bitfield, i ) )
296        {
297            /* The peer doesn't have this piece */
298            continue;
299        }
300
301        /* We are interested in this piece, remember it */
302        if( missingBlocks < minMissing )
303        {
304            minMissing = missingBlocks;
305            poolSize   = 0;
306        }
307        if( missingBlocks <= minMissing )
308        {
309            pool[poolSize++] = i;
310        }
311    }
312
313    if( poolSize )
314    {
315        /* All pieces in 'pool' have 'minMissing' missing blocks. Find
316           the rarest ones. */
317        uint8_t * bitfield;
318        int piece;
319        int min, foo, j;
320        int * pool2;
321        int   pool2Size;
322
323        pool2     = malloc( poolSize * sizeof( int ) );
324        pool2Size = 0;
325        min       = TR_MAX_PEER_COUNT + 1;
326        for( i = 0; i < poolSize; i++ )
327        {
328            foo = 0;
329            for( j = 0; j < tor->peerCount; j++ )
330            {
331                bitfield = tor->peers[j]->bitfield;
332                if( bitfield && tr_bitfieldHas( bitfield, pool[i] ) )
333                {
334                    foo++;
335                }
336            }
337            if( foo < min )
338            {
339                min       = foo;
340                pool2Size = 0;
341            }
342            if( foo <= min )
343            {
344                pool2[pool2Size++] = pool[i];
345            }
346        }
347        free( pool );
348
349        if( pool2Size < 1 )
350        {
351            /* Shouldn't happen */
352            free( pool2 );
353            return -1;
354        }
355
356        /* All pieces in pool2 have the same number of missing blocks,
357           and are availabme from the same number of peers. Pick a
358           random one */
359        piece = pool2[tr_rand(pool2Size)];
360        free( pool2 );
361
362        /* Pick a block in this piece */
363        block = tr_cpMissingBlockInPiece( tor->completion, piece );
364        goto check;
365    }
366
367    free( pool );
368
369    /* "End game" mode */
370    minDownloading = 255;
371    block = -1;
372    for( i = 0; i < inf->pieceCount; i++ )
373    {
374        int downloaders, block2;
375        if( !tr_bitfieldHas( peer->bitfield, i ) )
376        {
377            /* The peer doesn't have this piece */
378            continue;
379        }
380        if( tr_cpPieceIsComplete( tor->completion, i ) )
381        {
382            /* We already have it */
383            continue;
384        }
385        block2 = tr_cpMostMissingBlockInPiece( tor->completion, i, &downloaders );
386        if( block2 > -1 && downloaders < minDownloading )
387        {
388            block = block2;
389            minDownloading = downloaders;
390        }
391    }
392
393check:
394    if( block < 0 )
395    {
396        /* Shouldn't happen */
397        return -1;
398    }
399
400    for( i = 0; i < peer->inRequestCount; i++ )
401    {
402        tr_request_t * r;
403        r = &peer->inRequests[i];
404        if( tr_block( r->index, r->begin ) == block )
405        {
406            /* We are already asking this peer for this block */
407            return -1;
408        }
409    }
410
411    return block;
412}
Note: See TracBrowser for help on using the repository browser.