source: trunk/libtransmission/peer.c @ 60

Last change on this file since 60 was 60, checked in by titer, 16 years ago

Rewrote rate control, background work for the download limit, the dl/ul
limits per torrent and for the choking algorithm

File size: 13.0 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
23#include "transmission.h"
24
25#define MAX_REQUEST_COUNT 32
26#define OUR_REQUEST_COUNT 8  /* TODO: we should detect if we are on a
27                                high-speed network and adapt */
28
29typedef struct tr_request_s
30{
31    int index;
32    int begin;
33    int length;
34
35} tr_request_t;
36
37struct tr_peer_s
38{
39    struct in_addr addr;
40    in_port_t      port;
41
42#define PEER_STATUS_IDLE       1 /* Need to connect */
43#define PEER_STATUS_CONNECTING 2 /* Trying to send handshake */
44#define PEER_STATUS_HANDSHAKE  4 /* Waiting for peer's handshake */
45#define PEER_STATUS_CONNECTED  8 /* Got peer's handshake */
46    int            status;
47    int            socket;
48    uint64_t       date;
49    uint64_t       keepAlive;
50
51    char           amChoking;
52    char           amInterested;
53    char           peerChoking;
54    char           peerInterested;
55
56    uint8_t        id[20];
57
58    uint8_t      * bitfield;
59
60    uint8_t      * buf;
61    int            size;
62    int            pos;
63
64    uint8_t      * outMessages;
65    int            outMessagesSize;
66    int            outMessagesPos;
67    uint8_t        outBlock[13+16384];
68    int            outBlockSize;
69    int            outBlockLoaded;
70    int            outBlockSending;
71
72    int            inRequestCount;
73    tr_request_t   inRequests[OUR_REQUEST_COUNT];
74    int            inIndex;
75    int            inBegin;
76    int            inLength;
77    uint64_t       inTotal;
78
79    int            outRequestCount;
80    tr_request_t   outRequests[MAX_REQUEST_COUNT];
81    uint64_t       outTotal;
82    uint64_t       outDate;
83    int            outSlow;
84};
85
86#define peer_dbg( a... ) __peer_dbg( peer, ## a )
87static void __peer_dbg( tr_peer_t * peer, char * msg, ... )
88{
89    char    string[256];
90    va_list args;
91
92    va_start( args, msg );
93    sprintf( string, "%08x:%04x ",
94             (uint32_t) peer->addr.s_addr, peer->port );
95    vsnprintf( &string[14], sizeof( string ) - 14, msg, args );
96    va_end( args ); 
97
98    tr_dbg( "%s", string );
99}
100
101#include "peermessages.h"
102#include "peerutils.h"
103#include "peerparse.h"
104
105/***********************************************************************
106 * tr_peerAddOld
107 ***********************************************************************
108 * Tries to add a peer given its IP and port (received from a tracker
109 * which doesn't support the "compact" extension).
110 **********************************************************************/
111void tr_peerAddOld( tr_torrent_t * tor, char * ip, int port )
112{
113    struct in_addr addr;
114
115    if( tr_netResolve( ip, &addr ) )
116    {
117        return;
118    }
119
120    addWithAddr( tor, addr, htons( port ) );
121}
122
123/***********************************************************************
124 * tr_peerAddCompact
125 ***********************************************************************
126 * Tries to add a peer, using 'addr' and 'port' to connect to the peer.
127 **********************************************************************/
128void tr_peerAddCompact( tr_torrent_t * tor, struct in_addr addr,
129                        in_port_t port )
130{
131    addWithAddr( tor, addr, port );
132}
133
134/***********************************************************************
135 * tr_peerInit
136 ***********************************************************************
137 * Initializes a new peer.
138 **********************************************************************/
139tr_peer_t * tr_peerInit( struct in_addr addr, in_port_t port, int s )
140{
141    tr_peer_t * peer = peerInit();
142
143    peer->socket = s;
144    peer->addr   = addr;
145    peer->port   = port;
146    peer->status = PEER_STATUS_CONNECTING;
147
148    return peer;
149}
150
151void tr_peerAttach( tr_torrent_t * tor, tr_peer_t * peer )
152{
153    peerAttach( tor, peer );
154}
155
156void tr_peerDestroy( tr_fd_t * fdlimit, tr_peer_t * peer )
157{
158    if( peer->bitfield )
159    {
160        free( peer->bitfield );
161    }
162    if( peer->buf )
163    {
164        free( peer->buf );
165    }
166    if( peer->outMessages )
167    {
168        free( peer->outMessages );
169    }
170    if( peer->status > PEER_STATUS_IDLE )
171    {
172        tr_netClose( peer->socket );
173        tr_fdSocketClosed( fdlimit, 0 );
174    }
175    free( peer );
176}
177
178/***********************************************************************
179 * tr_peerRem
180 ***********************************************************************
181 * Frees and closes everything related to the peer at index 'i', and
182 * removes it from the peers list.
183 **********************************************************************/
184void tr_peerRem( tr_torrent_t * tor, int i )
185{
186    tr_peer_t * peer = tor->peers[i];
187    int j;
188
189    for( j = 0; j < peer->inRequestCount; j++ )
190    {
191        tr_request_t * r;
192        int            block;
193
194        r     = &peer->inRequests[j];
195        block = tr_block( r->index,r->begin );
196        tr_cpDownloaderRem( tor->completion, block );
197    }
198    if( !peer->amChoking )
199    {
200        //tr_uploadChoked( tor->upload );
201    }
202    tr_peerDestroy( tor->fdlimit, peer );
203    tor->peerCount--;
204    memmove( &tor->peers[i], &tor->peers[i+1],
205             ( tor->peerCount - i ) * sizeof( tr_peer_t * ) );
206}
207
208/***********************************************************************
209 * tr_peerRead
210 ***********************************************************************
211 *
212 **********************************************************************/
213int tr_peerRead( tr_torrent_t * tor, tr_peer_t * peer )
214{
215    int ret;
216
217    /* Try to read */
218    for( ;; )
219    {
220        if( peer->size < 1 )
221        {
222            peer->size = 1024;
223            peer->buf  = malloc( peer->size );
224        }
225        else if( peer->pos >= peer->size )
226        {
227            peer->size *= 2;
228            peer->buf   = realloc( peer->buf, peer->size );
229        }
230        ret = tr_netRecv( peer->socket, &peer->buf[peer->pos],
231                          peer->size - peer->pos );
232        if( ret & TR_NET_CLOSE )
233        {
234            peer_dbg( "connection closed" );
235            return 1;
236        }
237        else if( ret & TR_NET_BLOCK )
238        {
239            break;
240        }
241        peer->date  = tr_date();
242        peer->pos  += ret;
243        if( NULL != tor )
244        {
245            tr_rcTransferred( tor->download, ret );
246            tr_rcTransferred( tor->globalDownload, ret );
247            if( parseBuf( tor, peer, ret ) )
248            {
249                return 1;
250            }
251        }
252        else
253        {
254            if( parseBufHeader( peer ) )
255            {
256                return 1;
257            }
258        }
259    }
260
261    return 0;
262}
263
264/***********************************************************************
265 * tr_peerHash
266 ***********************************************************************
267 *
268 **********************************************************************/
269uint8_t * tr_peerHash( tr_peer_t * peer )
270{
271    return parseBufHash( peer );
272}
273
274/***********************************************************************
275 * tr_peerPulse
276 ***********************************************************************
277 *
278 **********************************************************************/
279void tr_peerPulse( tr_torrent_t * tor )
280{
281    int i, ret, size;
282    uint8_t * p;
283    tr_peer_t * peer;
284
285    tor->dates[9] = tr_date();
286    if( tor->dates[9] > tor->dates[8] + 1000 )
287    {
288        memmove( &tor->downloaded[0], &tor->downloaded[1],
289                 9 * sizeof( uint64_t ) );
290        memmove( &tor->uploaded[0], &tor->uploaded[1],
291                 9 * sizeof( uint64_t ) );
292        memmove( &tor->dates[0], &tor->dates[1],
293                 9 * sizeof( uint64_t ) );
294
295        for( i = 0; i < tor->peerCount; )
296        {
297            if( checkPeer( tor, i ) )
298            {
299                tr_peerRem( tor, i );
300                continue;
301            }
302            i++;
303        }
304    }
305
306    if( tor->status & TR_STATUS_STOPPING )
307    {
308        return;
309    }
310   
311    /* Shuffle peers */
312    if( tor->peerCount > 1 )
313    {
314        peer = tor->peers[0];
315        memmove( &tor->peers[0], &tor->peers[1],
316                 ( tor->peerCount - 1 ) * sizeof( void * ) );
317        tor->peers[tor->peerCount - 1] = peer;
318    }
319
320    /* Handle peers */
321    for( i = 0; i < tor->peerCount; )
322    {
323        peer = tor->peers[i];
324
325        if( peer->status < PEER_STATUS_HANDSHAKE )
326        {
327            i++;
328            continue;
329        }
330
331        if( tr_peerRead( tor, tor->peers[i] ) )
332        {
333            goto dropPeer;
334        }
335
336        if( peer->status < PEER_STATUS_CONNECTED )
337        {
338            i++;
339            continue;
340        }
341
342        /* Try to write */
343writeBegin:
344
345        /* Send all smaller messages regardless of the upload cap */
346        while( ( p = messagesPending( peer, &size ) ) )
347        {
348            ret = tr_netSend( peer->socket, p, size );
349            if( ret & TR_NET_CLOSE )
350            {
351                goto dropPeer;
352            }
353            else if( ret & TR_NET_BLOCK )
354            {
355                goto writeEnd;
356            }
357            messagesSent( peer, ret );
358        }
359
360        /* Send pieces if we can */
361        while( ( p = blockPending( tor, peer, &size ) ) )
362        {
363            if( !tr_rcCanTransfer( tor->globalUpload ) )
364            {
365                break;
366            }
367
368            ret = tr_netSend( peer->socket, p, size );
369            if( ret & TR_NET_CLOSE )
370            {
371                goto dropPeer;
372            }
373            else if( ret & TR_NET_BLOCK )
374            {
375                break;
376            }
377
378            blockSent( peer, ret );
379            tr_rcTransferred( tor->upload, ret );
380            tr_rcTransferred( tor->globalUpload, ret );
381
382            tor->uploaded[9] += ret;
383            peer->outTotal   += ret;
384            peer->outDate     = tr_date();
385
386            /* In case this block is done, you may have messages
387               pending. Send them before we start the next block */
388            goto writeBegin;
389        }
390writeEnd:
391
392        /* Ask for a block whenever possible */
393        if( !tr_cpIsSeeding( tor->completion ) &&
394            !peer->amInterested && tor->peerCount > TR_MAX_PEER_COUNT - 2 )
395        {
396            /* This peer is no use to us, and it seems there are
397               more */
398            peer_dbg( "not interesting" );
399            tr_peerRem( tor, i );
400            continue;
401        }
402
403        if( peer->amInterested && !peer->peerChoking )
404        {
405            int block;
406            while( peer->inRequestCount < OUR_REQUEST_COUNT )
407            {
408                block = chooseBlock( tor, peer );
409                if( block < 0 )
410                {
411                    break;
412                }
413                sendRequest( tor, peer, block );
414            }
415        }
416       
417        i++;
418        continue;
419
420dropPeer:
421        tr_peerRem( tor, i );
422    }
423}
424
425/***********************************************************************
426 * tr_peerIsConnected
427 ***********************************************************************
428 *
429 **********************************************************************/
430int tr_peerIsConnected( tr_peer_t * peer )
431{
432    return peer->status & PEER_STATUS_CONNECTED;
433}
434
435/***********************************************************************
436 * tr_peerIsUploading
437 ***********************************************************************
438 *
439 **********************************************************************/
440int tr_peerIsUploading( tr_peer_t * peer )
441{
442    return ( peer->inRequestCount > 0 );
443}
444
445/***********************************************************************
446 * tr_peerIsDownloading
447 ***********************************************************************
448 *
449 **********************************************************************/
450int tr_peerIsDownloading( tr_peer_t * peer )
451{
452    return peer->outBlockSending;
453}
454
455/***********************************************************************
456 * tr_peerBitfield
457 ***********************************************************************
458 *
459 **********************************************************************/
460uint8_t * tr_peerBitfield( tr_peer_t * peer )
461{
462    return peer->bitfield;
463}
Note: See TracBrowser for help on using the repository browser.