source: trunk/libtransmission/peer.c @ 3

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

Update 2005-11-17

File size: 12.2 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. If 's' is a negative value, will use 'addr' and
127 * 'port' to connect to the peer. Otherwise, use the already connected
128 * socket 's'.
129 **********************************************************************/
130void tr_peerAddCompact( tr_torrent_t * tor, struct in_addr addr,
131                        in_port_t port, int s )
132{
133    tr_peer_t * peer;
134
135    if( s < 0 )
136    {
137        addWithAddr( tor, addr, port );
138        return;
139    }
140
141    if( !( peer = peerInit( tor ) ) )
142    {
143        tr_netClose( s );
144        tr_fdSocketClosed( tor->fdlimit, 0 );
145        return;
146    }
147
148    peer->socket = s;
149    peer->addr   = addr;
150    peer->port   = port;
151    peer->status = PEER_STATUS_CONNECTING;
152}
153
154/***********************************************************************
155 * tr_peerRem
156 ***********************************************************************
157 * Frees and closes everything related to the peer at index 'i', and
158 * removes it from the peers list.
159 **********************************************************************/
160void tr_peerRem( tr_torrent_t * tor, int i )
161{
162    tr_peer_t * peer = tor->peers[i];
163    int j;
164
165    for( j = 0; j < peer->inRequestCount; j++ )
166    {
167        tr_request_t * r;
168        int            block;
169
170        r     = &peer->inRequests[j];
171        block = tr_block( r->index,r->begin );
172        tr_cpDownloaderRem( tor->completion, block );
173    }
174    if( !peer->amChoking )
175    {
176        tr_uploadChoked( tor->upload );
177    }
178    if( peer->bitfield )
179    {
180        free( peer->bitfield );
181    }
182    if( peer->buf )
183    {
184        free( peer->buf );
185    }
186    if( peer->outMessages )
187    {
188        free( peer->outMessages );
189    }
190    if( peer->status > PEER_STATUS_IDLE )
191    {
192        tr_netClose( peer->socket );
193        tr_fdSocketClosed( tor->fdlimit, 0 );
194    }
195    free( peer );
196    tor->peerCount--;
197    memmove( &tor->peers[i], &tor->peers[i+1],
198             ( tor->peerCount - i ) * sizeof( tr_peer_t * ) );
199}
200
201/***********************************************************************
202 * tr_peerPulse
203 ***********************************************************************
204 *
205 **********************************************************************/
206void tr_peerPulse( tr_torrent_t * tor )
207{
208    int i, ret, size;
209    uint8_t * p;
210    tr_peer_t * peer;
211
212    tor->dates[9] = tr_date();
213    if( tor->dates[9] > tor->dates[8] + 1000 )
214    {
215        memmove( &tor->downloaded[0], &tor->downloaded[1],
216                 9 * sizeof( uint64_t ) );
217        memmove( &tor->uploaded[0], &tor->uploaded[1],
218                 9 * sizeof( uint64_t ) );
219        memmove( &tor->dates[0], &tor->dates[1],
220                 9 * sizeof( uint64_t ) );
221
222        for( i = 0; i < tor->peerCount; )
223        {
224            if( checkPeer( tor, i ) )
225            {
226                tr_peerRem( tor, i );
227                continue;
228            }
229            i++;
230        }
231    }
232
233    if( tor->status & TR_STATUS_STOPPING )
234    {
235        return;
236    }
237
238    /* Check for incoming connections */
239    if( tor->bindSocket > -1 &&
240        tor->peerCount < TR_MAX_PEER_COUNT &&
241        !tr_fdSocketWillCreate( tor->fdlimit, 0 ) )
242    {
243        int            s;
244        struct in_addr addr;
245        in_port_t      port;
246        s = tr_netAccept( tor->bindSocket, &addr, &port );
247        if( s > -1 )
248        {
249            tr_peerAddCompact( tor, addr, port, s );
250        }
251        else
252        {
253            tr_fdSocketClosed( tor->fdlimit, 0 );
254        }
255    }
256   
257    /* Shuffle peers */
258    if( tor->peerCount > 1 )
259    {
260        peer = tor->peers[0];
261        memmove( &tor->peers[0], &tor->peers[1],
262                 ( tor->peerCount - 1 ) * sizeof( void * ) );
263        tor->peers[tor->peerCount - 1] = peer;
264    }
265
266    /* Handle peers */
267    for( i = 0; i < tor->peerCount; )
268    {
269        peer = tor->peers[i];
270
271        if( peer->status < PEER_STATUS_HANDSHAKE )
272        {
273            i++;
274            continue;
275        }
276
277        /* Try to read */
278        for( ;; )
279        {
280            if( peer->size < 1 )
281            {
282                peer->size = 1024;
283                peer->buf  = malloc( peer->size );
284            }
285            else if( peer->pos >= peer->size )
286            {
287                peer->size *= 2;
288                peer->buf   = realloc( peer->buf, peer->size );
289            }
290            ret = tr_netRecv( peer->socket, &peer->buf[peer->pos],
291                              peer->size - peer->pos );
292            if( ret & TR_NET_CLOSE )
293            {
294                peer_dbg( "connection closed" );
295                goto dropPeer;
296            }
297            else if( ret & TR_NET_BLOCK )
298            {
299                break;
300            }
301            peer->date  = tr_date();
302            peer->pos  += ret;
303            if( parseBuf( tor, peer, ret ) )
304            {
305                goto dropPeer;
306            }
307        }
308
309        if( peer->status < PEER_STATUS_CONNECTED )
310        {
311            i++;
312            continue;
313        }
314
315        /* Try to write */
316writeBegin:
317
318        /* Send all smaller messages regardless of the upload cap */
319        while( ( p = messagesPending( peer, &size ) ) )
320        {
321            ret = tr_netSend( peer->socket, p, size );
322            if( ret & TR_NET_CLOSE )
323            {
324                goto dropPeer;
325            }
326            else if( ret & TR_NET_BLOCK )
327            {
328                goto writeEnd;
329            }
330            messagesSent( peer, ret );
331        }
332
333        /* Send pieces if we can */
334        while( ( p = blockPending( tor, peer, &size ) ) )
335        {
336            if( !tr_uploadCanUpload( tor->upload ) )
337            {
338                break;
339            }
340
341            ret = tr_netSend( peer->socket, p, size );
342            if( ret & TR_NET_CLOSE )
343            {
344                goto dropPeer;
345            }
346            else if( ret & TR_NET_BLOCK )
347            {
348                break;
349            }
350
351            blockSent( peer, ret );
352            tr_uploadUploaded( tor->upload, ret );
353
354            tor->uploaded[9] += ret;
355            peer->outTotal   += ret;
356            peer->outDate     = tr_date();
357
358            /* In case this block is done, you may have messages
359               pending. Send them before we start the next block */
360            goto writeBegin;
361        }
362writeEnd:
363
364        /* Ask for a block whenever possible */
365        if( !tr_cpIsSeeding( tor->completion ) &&
366            !peer->amInterested && tor->peerCount > TR_MAX_PEER_COUNT - 2 )
367        {
368            /* This peer is no use to us, and it seems there are
369               more */
370            peer_dbg( "not interesting" );
371            tr_peerRem( tor, i );
372            continue;
373        }
374
375        if( peer->amInterested && !peer->peerChoking )
376        {
377            int block;
378            while( peer->inRequestCount < OUR_REQUEST_COUNT )
379            {
380                block = chooseBlock( tor, peer );
381                if( block < 0 )
382                {
383                    break;
384                }
385                sendRequest( tor, peer, block );
386            }
387        }
388       
389        i++;
390        continue;
391
392dropPeer:
393        tr_peerRem( tor, i );
394    }
395}
396
397/***********************************************************************
398 * tr_peerIsConnected
399 ***********************************************************************
400 *
401 **********************************************************************/
402int tr_peerIsConnected( tr_peer_t * peer )
403{
404    return peer->status & PEER_STATUS_CONNECTED;
405}
406
407/***********************************************************************
408 * tr_peerIsUploading
409 ***********************************************************************
410 *
411 **********************************************************************/
412int tr_peerIsUploading( tr_peer_t * peer )
413{
414    return ( peer->inRequestCount > 0 );
415}
416
417/***********************************************************************
418 * tr_peerIsDownloading
419 ***********************************************************************
420 *
421 **********************************************************************/
422int tr_peerIsDownloading( tr_peer_t * peer )
423{
424    return peer->outBlockSending;
425}
426
427/***********************************************************************
428 * tr_peerBitfield
429 ***********************************************************************
430 *
431 **********************************************************************/
432uint8_t * tr_peerBitfield( tr_peer_t * peer )
433{
434    return peer->bitfield;
435}
Note: See TracBrowser for help on using the repository browser.