source: trunk/libtransmission/peerutils.h @ 3

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

Update 2005-11-17

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