source: trunk/libtransmission/peer.c @ 109

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

Added optimistic choking

File size: 13.4 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( peer->size < 1 )
223        {
224            peer->size = 1024;
225            peer->buf  = malloc( peer->size );
226        }
227        else if( peer->pos >= peer->size )
228        {
229            peer->size *= 2;
230            peer->buf   = realloc( peer->buf, peer->size );
231        }
232        ret = tr_netRecv( peer->socket, &peer->buf[peer->pos],
233                          peer->size - peer->pos );
234        if( ret & TR_NET_CLOSE )
235        {
236            peer_dbg( "connection closed" );
237            return 1;
238        }
239        else if( ret & TR_NET_BLOCK )
240        {
241            break;
242        }
243        peer->date  = tr_date();
244        peer->pos  += ret;
245        if( NULL != tor )
246        {
247            tr_rcTransferred( peer->download, ret );
248            tr_rcTransferred( tor->download, ret );
249            tr_rcTransferred( tor->globalDownload, ret );
250            if( parseBuf( tor, peer ) )
251            {
252                return 1;
253            }
254        }
255        else
256        {
257            if( parseBufHeader( peer ) )
258            {
259                return 1;
260            }
261        }
262    }
263
264    return 0;
265}
266
267/***********************************************************************
268 * tr_peerHash
269 ***********************************************************************
270 *
271 **********************************************************************/
272uint8_t * tr_peerHash( tr_peer_t * peer )
273{
274    return parseBufHash( peer );
275}
276
277/***********************************************************************
278 * tr_peerPulse
279 ***********************************************************************
280 *
281 **********************************************************************/
282void tr_peerPulse( tr_torrent_t * tor )
283{
284    int i, ret, size;
285    uint8_t * p;
286    tr_peer_t * peer;
287
288    if( tr_date() > tor->date + 1000 )
289    {
290        tor->date = tr_date();
291
292        for( i = 0; i < tor->peerCount; )
293        {
294            if( checkPeer( tor, i ) )
295            {
296                tr_peerRem( tor, i );
297                continue;
298            }
299            i++;
300        }
301    }
302
303    if( tor->status & TR_STATUS_STOPPING )
304    {
305        return;
306    }
307   
308    /* Shuffle peers */
309    if( tor->peerCount > 1 )
310    {
311        peer = tor->peers[0];
312        memmove( &tor->peers[0], &tor->peers[1],
313                 ( tor->peerCount - 1 ) * sizeof( void * ) );
314        tor->peers[tor->peerCount - 1] = peer;
315    }
316
317    /* Handle peers */
318    for( i = 0; i < tor->peerCount; )
319    {
320        peer = tor->peers[i];
321
322        if( peer->status < PEER_STATUS_HANDSHAKE )
323        {
324            i++;
325            continue;
326        }
327
328        if( tr_peerRead( tor, tor->peers[i] ) )
329        {
330            goto dropPeer;
331        }
332
333        if( peer->status < PEER_STATUS_CONNECTED )
334        {
335            i++;
336            continue;
337        }
338
339        /* Try to write */
340writeBegin:
341
342        /* Send all smaller messages regardless of the upload cap */
343        while( ( p = messagesPending( peer, &size ) ) )
344        {
345            ret = tr_netSend( peer->socket, p, size );
346            if( ret & TR_NET_CLOSE )
347            {
348                goto dropPeer;
349            }
350            else if( ret & TR_NET_BLOCK )
351            {
352                goto writeEnd;
353            }
354            messagesSent( peer, ret );
355        }
356
357        /* Send pieces if we can */
358        while( ( p = blockPending( tor, peer, &size ) ) )
359        {
360            if( !tr_rcCanTransfer( tor->globalUpload ) )
361            {
362                break;
363            }
364
365            ret = tr_netSend( peer->socket, p, size );
366            if( ret & TR_NET_CLOSE )
367            {
368                goto dropPeer;
369            }
370            else if( ret & TR_NET_BLOCK )
371            {
372                break;
373            }
374
375            blockSent( peer, ret );
376            tr_rcTransferred( tor->upload, ret );
377            tr_rcTransferred( tor->globalUpload, ret );
378
379            tor->uploaded  += ret;
380            peer->outTotal += ret;
381            peer->outDate   = tr_date();
382
383            /* In case this block is done, you may have messages
384               pending. Send them before we start the next block */
385            goto writeBegin;
386        }
387writeEnd:
388
389        /* Ask for a block whenever possible */
390        if( !tr_cpIsSeeding( tor->completion ) &&
391            !peer->amInterested && tor->peerCount > TR_MAX_PEER_COUNT - 2 )
392        {
393            /* This peer is no use to us, and it seems there are
394               more */
395            peer_dbg( "not interesting" );
396            tr_peerRem( tor, i );
397            continue;
398        }
399
400        if( peer->amInterested && !peer->peerChoking )
401        {
402            int block;
403            while( peer->inRequestCount < OUR_REQUEST_COUNT )
404            {
405                block = chooseBlock( tor, peer );
406                if( block < 0 )
407                {
408                    break;
409                }
410                sendRequest( tor, peer, block );
411            }
412        }
413       
414        i++;
415        continue;
416
417dropPeer:
418        tr_peerRem( tor, i );
419    }
420}
421
422/***********************************************************************
423 * tr_peerIsConnected
424 ***********************************************************************
425 *
426 **********************************************************************/
427int tr_peerIsConnected( tr_peer_t * peer )
428{
429    return peer->status & PEER_STATUS_CONNECTED;
430}
431
432/***********************************************************************
433 * tr_peerIsUploading
434 ***********************************************************************
435 *
436 **********************************************************************/
437int tr_peerIsUploading( tr_peer_t * peer )
438{
439    return ( peer->inRequestCount > 0 );
440}
441
442/***********************************************************************
443 * tr_peerIsDownloading
444 ***********************************************************************
445 *
446 **********************************************************************/
447int tr_peerIsDownloading( tr_peer_t * peer )
448{
449    return peer->outBlockSending;
450}
451
452/***********************************************************************
453 * tr_peerBitfield
454 ***********************************************************************
455 *
456 **********************************************************************/
457uint8_t * tr_peerBitfield( tr_peer_t * peer )
458{
459    return peer->bitfield;
460}
461
462float tr_peerDownloadRate( tr_peer_t * peer )
463{
464    return tr_rcRate( peer->download );
465}
466
467int tr_peerIsUnchoked( tr_peer_t * peer )
468{
469    return !peer->amChoking;
470}
471
472int tr_peerIsInterested  ( tr_peer_t * peer )
473{
474    return peer->peerInterested;
475}
476
477void tr_peerChoke( tr_peer_t * peer )
478{
479    sendChoke( peer, 1 );
480    peer->lastChoke = tr_date();
481}
482
483void tr_peerUnchoke( tr_peer_t * peer )
484{
485    sendChoke( peer, 0 );
486    peer->lastChoke = tr_date();
487}
488
489uint64_t tr_peerLastChoke( tr_peer_t * peer )
490{
491    return peer->lastChoke;
492}
493
494void tr_peerSetOptimistic( tr_peer_t * peer, int o )
495{
496    peer->optimistic = o;
497}
498
499int tr_peerIsOptimistic( tr_peer_t * peer )
500{
501    return peer->optimistic;
502}
Note: See TracBrowser for help on using the repository browser.