source: trunk/libtransmission/peer.c @ 26

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

Update 2006-01-11

File size: 12.8 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            if( parseBuf( tor, peer, ret ) )
246            {
247                return 1;
248            }
249        }
250        else
251        {
252            if( parseBufHeader( peer ) )
253            {
254                return 1;
255            }
256        }
257    }
258
259    return 0;
260}
261
262/***********************************************************************
263 * tr_peerHash
264 ***********************************************************************
265 *
266 **********************************************************************/
267uint8_t * tr_peerHash( tr_peer_t * peer )
268{
269    return parseBufHash( peer );
270}
271
272/***********************************************************************
273 * tr_peerPulse
274 ***********************************************************************
275 *
276 **********************************************************************/
277void tr_peerPulse( tr_torrent_t * tor )
278{
279    int i, ret, size;
280    uint8_t * p;
281    tr_peer_t * peer;
282
283    tor->dates[9] = tr_date();
284    if( tor->dates[9] > tor->dates[8] + 1000 )
285    {
286        memmove( &tor->downloaded[0], &tor->downloaded[1],
287                 9 * sizeof( uint64_t ) );
288        memmove( &tor->uploaded[0], &tor->uploaded[1],
289                 9 * sizeof( uint64_t ) );
290        memmove( &tor->dates[0], &tor->dates[1],
291                 9 * sizeof( uint64_t ) );
292
293        for( i = 0; i < tor->peerCount; )
294        {
295            if( checkPeer( tor, i ) )
296            {
297                tr_peerRem( tor, i );
298                continue;
299            }
300            i++;
301        }
302    }
303
304    if( tor->status & TR_STATUS_STOPPING )
305    {
306        return;
307    }
308   
309    /* Shuffle peers */
310    if( tor->peerCount > 1 )
311    {
312        peer = tor->peers[0];
313        memmove( &tor->peers[0], &tor->peers[1],
314                 ( tor->peerCount - 1 ) * sizeof( void * ) );
315        tor->peers[tor->peerCount - 1] = peer;
316    }
317
318    /* Handle peers */
319    for( i = 0; i < tor->peerCount; )
320    {
321        peer = tor->peers[i];
322
323        if( peer->status < PEER_STATUS_HANDSHAKE )
324        {
325            i++;
326            continue;
327        }
328
329        if( tr_peerRead( tor, tor->peers[i] ) )
330        {
331            goto dropPeer;
332        }
333
334        if( peer->status < PEER_STATUS_CONNECTED )
335        {
336            i++;
337            continue;
338        }
339
340        /* Try to write */
341writeBegin:
342
343        /* Send all smaller messages regardless of the upload cap */
344        while( ( p = messagesPending( peer, &size ) ) )
345        {
346            ret = tr_netSend( peer->socket, p, size );
347            if( ret & TR_NET_CLOSE )
348            {
349                goto dropPeer;
350            }
351            else if( ret & TR_NET_BLOCK )
352            {
353                goto writeEnd;
354            }
355            messagesSent( peer, ret );
356        }
357
358        /* Send pieces if we can */
359        while( ( p = blockPending( tor, peer, &size ) ) )
360        {
361            if( !tr_uploadCanUpload( tor->upload ) )
362            {
363                break;
364            }
365
366            ret = tr_netSend( peer->socket, p, size );
367            if( ret & TR_NET_CLOSE )
368            {
369                goto dropPeer;
370            }
371            else if( ret & TR_NET_BLOCK )
372            {
373                break;
374            }
375
376            blockSent( peer, ret );
377            tr_uploadUploaded( tor->upload, ret );
378
379            tor->uploaded[9] += 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}
Note: See TracBrowser for help on using the repository browser.