source: trunk/libtransmission/peer.c @ 210

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

Adds download rate limit

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