rubytorrent 0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +340 -0
- data/README +21 -0
- data/ReleaseNotes.txt +25 -0
- data/doc/api.txt +289 -0
- data/doc/design.txt +59 -0
- data/dump-metainfo.rb +55 -0
- data/dump-peers.rb +45 -0
- data/lib/rubytorrent.rb +94 -0
- data/lib/rubytorrent/bencoding.rb +174 -0
- data/lib/rubytorrent/controller.rb +610 -0
- data/lib/rubytorrent/message.rb +128 -0
- data/lib/rubytorrent/metainfo.rb +214 -0
- data/lib/rubytorrent/package.rb +595 -0
- data/lib/rubytorrent/peer.rb +536 -0
- data/lib/rubytorrent/server.rb +166 -0
- data/lib/rubytorrent/tracker.rb +225 -0
- data/lib/rubytorrent/typedstruct.rb +132 -0
- data/lib/rubytorrent/util.rb +186 -0
- data/make-metainfo.rb +211 -0
- data/rtpeer-ncurses.rb +340 -0
- data/rtpeer.rb +125 -0
- metadata +78 -0
@@ -0,0 +1,536 @@
|
|
1
|
+
## peer.rb -- bitttorrent peer ("wire") protocol.
|
2
|
+
## Copyright 2004 William Morgan.
|
3
|
+
##
|
4
|
+
## This file is part of RubyTorrent. RubyTorrent is free software;
|
5
|
+
## you can redistribute it and/or modify it under the terms of version
|
6
|
+
## 2 of the GNU General Public License as published by the Free
|
7
|
+
## Software Foundation.
|
8
|
+
##
|
9
|
+
## RubyTorrent is distributed in the hope that it will be useful, but
|
10
|
+
## WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
12
|
+
## General Public License (in the file COPYING) for more details.
|
13
|
+
|
14
|
+
require 'socket'
|
15
|
+
require 'thread'
|
16
|
+
require "rubytorrent/message"
|
17
|
+
|
18
|
+
module RubyTorrent
|
19
|
+
|
20
|
+
module ArrayToBitstring
|
21
|
+
def to_bitstring
|
22
|
+
ret = "\0"
|
23
|
+
bit = 7
|
24
|
+
map do |b|
|
25
|
+
if bit == -1
|
26
|
+
ret += "\0"
|
27
|
+
bit = 7
|
28
|
+
end
|
29
|
+
ret[ret.length - 1] |= (1 << bit) if b
|
30
|
+
bit -= 1
|
31
|
+
end
|
32
|
+
ret
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
module ArrayDelete2
|
37
|
+
## just like delete but returns the *array* element deleted rather
|
38
|
+
## than the argument. someone should file an rcr.
|
39
|
+
def delete2(el)
|
40
|
+
i = index el
|
41
|
+
unless i.nil?
|
42
|
+
ret = self[i]
|
43
|
+
delete_at i
|
44
|
+
ret
|
45
|
+
else
|
46
|
+
nil
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
module StringToBarray
|
52
|
+
include StringMapBytes
|
53
|
+
def to_barray
|
54
|
+
self.map_bytes do |b|
|
55
|
+
(0 .. 7).map { |i| (b & (1 << (7 - i))) != 0 }
|
56
|
+
end.flatten
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
## estimate a rate. basically copied from bram's code.
|
61
|
+
class RateMeter
|
62
|
+
attr_reader :amt
|
63
|
+
|
64
|
+
def initialize(window=20)
|
65
|
+
@window = window.to_f
|
66
|
+
@amt = 0
|
67
|
+
@rate = 0
|
68
|
+
@last = @since = Time.now - 1
|
69
|
+
@m = Mutex.new
|
70
|
+
end
|
71
|
+
|
72
|
+
def add(new_amt)
|
73
|
+
now = Time.now
|
74
|
+
@m.synchronize do
|
75
|
+
@amt += new_amt
|
76
|
+
@rate = ((@rate * (@last - @since)) + new_amt).to_f / (now - @since)
|
77
|
+
@last = now
|
78
|
+
@since = [@since, now - @window].max
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def rate
|
83
|
+
(@rate * (@last - @since)).to_f / (Time.now - @since)
|
84
|
+
end
|
85
|
+
|
86
|
+
def bytes_until(new_rate)
|
87
|
+
[(new_rate.to_f * (Time.now - @since)) - (@rate * (@last - @since)), 0].max
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class ProtocolError < StandardError; end
|
92
|
+
|
93
|
+
## The PeerConnection object deals with all the protocol issues. It
|
94
|
+
## keeps state information as to the connection and the peer. It is
|
95
|
+
## tightly integrated with the Controller object.
|
96
|
+
##
|
97
|
+
## Remember to be "strict in what you send, lenient in what you
|
98
|
+
## accept".
|
99
|
+
class PeerConnection
|
100
|
+
extend AttrReaderQ
|
101
|
+
include EventSource
|
102
|
+
|
103
|
+
attr_reader :peer_pieces, :name
|
104
|
+
attr_reader_q :running, :choking, :interested, :peer_choking,
|
105
|
+
:peer_interested, :snubbing
|
106
|
+
event :peer_has_piece, :peer_has_pieces, :received_block, :sent_block,
|
107
|
+
:requested_block
|
108
|
+
|
109
|
+
BUFSIZE = 8192
|
110
|
+
MAX_PEER_REQUESTS = 5 # how many peer requests to keep queued
|
111
|
+
MAX_REQUESTS = 5 # how many requests for blocks to keep current
|
112
|
+
MIN_REQUESTS = 1 # get more blocks from controller when this limit is reached
|
113
|
+
REQUEST_TIMEOUT = 60 # number of seconds after sending a request before we
|
114
|
+
# decide it's been forgotten
|
115
|
+
|
116
|
+
def initialize(name, controller, socket, package)
|
117
|
+
@name = name
|
118
|
+
@controller = controller
|
119
|
+
@socket = socket
|
120
|
+
@package = package
|
121
|
+
@running = false
|
122
|
+
|
123
|
+
## my state
|
124
|
+
@want_blocks = [].extend(ArrayDelete2) # blocks i want
|
125
|
+
@want_blocks_m = Mutex.new
|
126
|
+
@choking = true
|
127
|
+
@interested = false
|
128
|
+
@snubbing = false
|
129
|
+
|
130
|
+
## peer's state
|
131
|
+
@peer_want_blocks = [].extend(ArrayDelete2)
|
132
|
+
@peer_choking = true # assumption of initial condition
|
133
|
+
@peer_interested = false # ditto
|
134
|
+
@peer_pieces = Array.new(@package.num_pieces, false) # ditto
|
135
|
+
@peer_virgin = true # does the peer have any pieces at all?
|
136
|
+
|
137
|
+
## connection stats
|
138
|
+
@dlmeter = RateMeter.new
|
139
|
+
@ulmeter = RateMeter.new
|
140
|
+
|
141
|
+
@send_q = Queue.new # output thread takes messages from here and
|
142
|
+
# puts them on the wire
|
143
|
+
end
|
144
|
+
|
145
|
+
def pending_recv; @want_blocks.find_all { |b| b.requested? }.length; end
|
146
|
+
def pending_send; @peer_want_blocks.length; end
|
147
|
+
|
148
|
+
def start
|
149
|
+
@running = true
|
150
|
+
@time = {:start => Time.now}
|
151
|
+
|
152
|
+
Thread.new do # start input thread
|
153
|
+
begin
|
154
|
+
while @running; input_thread_step; end
|
155
|
+
rescue SystemCallError, IOError, ProtocolError => e
|
156
|
+
rt_debug "#{self} (input): #{e.message}, releasing #{@want_blocks.length} claimed blocks and dying"
|
157
|
+
# rt_debug e.backtrace.join("\n")
|
158
|
+
@running = false
|
159
|
+
@controller.forget_blocks @want_blocks
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
Thread.new do # start output thread
|
164
|
+
begin
|
165
|
+
while @running; output_thread_step; end
|
166
|
+
rescue SystemCallError, IOError, ProtocolError => e
|
167
|
+
rt_debug "#{self} (output): #{e.message}, releasing #{@want_blocks.length} claimed blocks and dying"
|
168
|
+
# rt_debug e.backtrace.join("\n")
|
169
|
+
@running = false
|
170
|
+
@controller.forget_blocks @want_blocks
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
## queue the initial messages
|
175
|
+
queue_message(:bitfield, {:bitfield => @package.pieces.map { |p| p.complete? }.extend(ArrayToBitstring).to_bitstring})
|
176
|
+
|
177
|
+
## and that's it. if peer sends a bitfield, we'll send an
|
178
|
+
## interested and start requesting blocks at that point. if they
|
179
|
+
## don't, it means they don't have any pieces, so we can just sit
|
180
|
+
## tight.
|
181
|
+
self
|
182
|
+
end
|
183
|
+
|
184
|
+
## the Controller calls this from heartbeat thread to tell us
|
185
|
+
## whether to choke or not.
|
186
|
+
def choke=(now_choke)
|
187
|
+
queue_message(now_choke ? :choke : :unchoke) unless @choking == now_choke
|
188
|
+
@choking = now_choke
|
189
|
+
end
|
190
|
+
|
191
|
+
## the Controller calls this from heartbeat thread to tell us
|
192
|
+
## whether to snub or not.
|
193
|
+
def snub=(now_snub)
|
194
|
+
unless @snubbing = now_snub
|
195
|
+
@snubbing = now_snub
|
196
|
+
choke = true if @snubbing
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def peer_complete?; @peer_pieces.all?; end
|
201
|
+
def last_send_time; @time[:send]; end
|
202
|
+
def last_recv_time; @time[:recv]; end
|
203
|
+
def last_send_block_time; @time[:send_block]; end
|
204
|
+
def last_recv_block_time; @time[:recv_block]; end
|
205
|
+
def start_time; @time[:start]; end
|
206
|
+
def dlrate; @dlmeter.rate; end
|
207
|
+
def ulrate; @ulmeter.rate; end
|
208
|
+
def dlamt; @dlmeter.amt; end
|
209
|
+
def ulamt; @ulmeter.amt; end
|
210
|
+
def piece_available?(index); @peer_pieces[index]; end
|
211
|
+
def to_s; "<peer: #@name>"; end
|
212
|
+
|
213
|
+
## called by Controller in the event that a request needs to be
|
214
|
+
## rescinded.
|
215
|
+
def cancel(block)
|
216
|
+
wblock = @want_blocks_m.synchronize { @want_blocks.delete2 block }
|
217
|
+
unless wblock.nil? || !wblock.requested?
|
218
|
+
rt_debug "#{self}: sending cancel for #{wblock}"
|
219
|
+
queue_message(:cancel, {:index => wblock.pindex, :begin => wblock.begin,
|
220
|
+
:length => wblock.length})
|
221
|
+
end
|
222
|
+
get_want_blocks unless wblock.nil?
|
223
|
+
end
|
224
|
+
|
225
|
+
def shutdown
|
226
|
+
rt_debug "#{self.to_s}: shutting down"
|
227
|
+
@running = false
|
228
|
+
@socket.close rescue nil
|
229
|
+
end
|
230
|
+
|
231
|
+
## Controller calls this to tell us that a complete piece has been
|
232
|
+
## received.
|
233
|
+
def have_piece(piece)
|
234
|
+
queue_message(:have, {:index => piece.index})
|
235
|
+
end
|
236
|
+
|
237
|
+
## Controller calls this to tell us to send a keepalive
|
238
|
+
def send_keepalive
|
239
|
+
# rt_debug "* sending keepalive!"
|
240
|
+
queue_message(:keepalive)
|
241
|
+
end
|
242
|
+
|
243
|
+
## this is called both by input_thread_step and by the controller's
|
244
|
+
## heartbeat thread. it sends as many pending blocks as it can while
|
245
|
+
## keeping the amount below 'ullim', and sends as many requests as
|
246
|
+
## it can while keeping the amount below 'dllim'.
|
247
|
+
##
|
248
|
+
## returns the number of bytes requested and sent
|
249
|
+
def send_blocks_and_reqs(dllim=nil, ullim=nil)
|
250
|
+
sent_bytes = 0
|
251
|
+
reqd_bytes = 0
|
252
|
+
|
253
|
+
@want_blocks_m.synchronize do
|
254
|
+
@want_blocks.each do |b|
|
255
|
+
# puts "[][] #{self}: #{b} is #{b.requested? ? 'requested' : 'NOT requested'} and has time_elapsed of #{b.requested? ? b.time_elapsed.round : 'n/a'}s"
|
256
|
+
if b.requested? && (b.time_elapsed > REQUEST_TIMEOUT)
|
257
|
+
rt_warning "#{self}: for block #{b}, time elapsed since request is #{b.time_elapsed} > #{REQUEST_TIMEOUT}, assuming peer forgot about it"
|
258
|
+
@want_blocks.delete b
|
259
|
+
@controller.forget_blocks [b]
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
## send :requests
|
265
|
+
unless @peer_choking || !@interested
|
266
|
+
@want_blocks_m.synchronize do
|
267
|
+
@want_blocks.each do |b|
|
268
|
+
break if dllim && (reqd_bytes >= dllim)
|
269
|
+
next if b.requested?
|
270
|
+
|
271
|
+
if @package.pieces[b.pindex].complete?
|
272
|
+
# not sure that this will ever happen, but...
|
273
|
+
rt_warning "#{self}: deleting scheduled block for already-complete piece #{b}"
|
274
|
+
@want_blocks.delete b
|
275
|
+
next
|
276
|
+
end
|
277
|
+
|
278
|
+
queue_message(:request, {:index => b.pindex, :begin => b.begin,
|
279
|
+
:length => b.length})
|
280
|
+
reqd_bytes += b.length
|
281
|
+
b.requested = true
|
282
|
+
b.mark_time
|
283
|
+
send_event(:requested_block, b)
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
## send blocks
|
289
|
+
# rt_debug "sending blocks. choking? #@choking, choked? #@peer_choking, ul rate #{ulrate}b/s, limit #@ulmeterlim" unless @peer_want_blocks.empty?
|
290
|
+
unless @choking || !@peer_interested
|
291
|
+
while !@peer_want_blocks.empty?
|
292
|
+
break if ullim && (sent_bytes >= ullim)
|
293
|
+
if (b = @peer_want_blocks.shift)
|
294
|
+
sent_bytes += b.length
|
295
|
+
@send_q.push b
|
296
|
+
@time[:send_block] = Time.now
|
297
|
+
send_event(:sent_block, b)
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
get_want_blocks
|
303
|
+
|
304
|
+
[reqd_bytes, sent_bytes]
|
305
|
+
end
|
306
|
+
|
307
|
+
private
|
308
|
+
|
309
|
+
## re-calculate whether we're interested or not. triggered by
|
310
|
+
## received :have and :bitfield messages.
|
311
|
+
def recalc_interested
|
312
|
+
show_interest = !@peer_virgin || (@package.pieces.detect do |p|
|
313
|
+
!p.complete? && @peer_pieces[p.index]
|
314
|
+
end) != nil
|
315
|
+
|
316
|
+
queue_message(show_interest ? :interested : :uninterested) unless show_interest == @interested
|
317
|
+
if ((@interested = show_interest) == false)
|
318
|
+
@want_blocks_m.synchronize do
|
319
|
+
@controller.forget_blocks @want_blocks
|
320
|
+
@want_blocks.clear
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
## take a message/block from the send_q and place it on the wire. blocking.
|
326
|
+
def output_thread_step
|
327
|
+
obj = @send_q.deq
|
328
|
+
case obj
|
329
|
+
when Message
|
330
|
+
# rt_debug "output: sending message #{obj}" + (obj.id == :request ? " (request queue size #{@want_blocks.length})" : "")
|
331
|
+
send_bytes obj.to_wire_form
|
332
|
+
@time[:send] = Time.now
|
333
|
+
when Block
|
334
|
+
# rt_debug "output: sending block #{obj}"
|
335
|
+
send_bytes Message.new(:piece, {:length => obj.length, :index => obj.pindex, :begin => obj.begin}).to_wire_form
|
336
|
+
obj.each_chunk(BUFSIZE) { |c| send_bytes c }
|
337
|
+
@time[:send] = Time.now
|
338
|
+
@ulmeter.add obj.length
|
339
|
+
# rt_debug "sent block #{obj} ul rate now #{(ulrate / 1024.0).round}kb/s"
|
340
|
+
else
|
341
|
+
raise "don't know what to do with #{obj}"
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
## take bits from the wire and respond to them. blocking.
|
346
|
+
def input_thread_step
|
347
|
+
case (obj = read_from_wire)
|
348
|
+
when Block
|
349
|
+
handle_block obj
|
350
|
+
when Message
|
351
|
+
handle_message obj
|
352
|
+
else
|
353
|
+
raise "don't know what to do with #{obj.inspect}"
|
354
|
+
end
|
355
|
+
|
356
|
+
## to enable immediate response, if there are no rate limits,
|
357
|
+
## we'll send the blocks and reqs right here. otherwise, the
|
358
|
+
## controller will call this at intervals.
|
359
|
+
send_blocks_and_reqs if @controller.dlratelim.nil? && @controller.ulratelim.nil?
|
360
|
+
end
|
361
|
+
|
362
|
+
## take bits from the wire and make a message/block out of them. blocking.
|
363
|
+
def read_from_wire
|
364
|
+
len = nil
|
365
|
+
while (len = recv_bytes(4).from_fbbe) == 0
|
366
|
+
@time[:recv] = Time.now
|
367
|
+
# rt_debug "* hey, a keepalive!"
|
368
|
+
end
|
369
|
+
|
370
|
+
id = recv_bytes(1)[0]
|
371
|
+
|
372
|
+
if Message::WIRE_IDS[id] == :piece # add a block
|
373
|
+
len -= 9
|
374
|
+
m = Message.from_wire_form(id, recv_bytes(8))
|
375
|
+
b = Block.new(m.index, m.begin, len)
|
376
|
+
while len > 0
|
377
|
+
thislen = [BUFSIZE, len].min
|
378
|
+
b.add_chunk recv_bytes(thislen)
|
379
|
+
len -= thislen
|
380
|
+
end
|
381
|
+
@time[:recv] = @time[:recv_block] = Time.now
|
382
|
+
b
|
383
|
+
else # add a message
|
384
|
+
m = Message.from_wire_form(id, recv_bytes(len - 1))
|
385
|
+
# rt_debug "input: read message #{m}"
|
386
|
+
@time[:recv] = Time.now
|
387
|
+
m
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
def handle_block(block)
|
392
|
+
wblock = @want_blocks_m.synchronize { @want_blocks.delete2 block }
|
393
|
+
|
394
|
+
return rt_warning("#{self}: peer sent unrequested (possibly cancelled) block #{block}") if wblock.nil? || !wblock.requested?
|
395
|
+
|
396
|
+
@dlmeter.add block.have_length
|
397
|
+
# rt_debug "received block #{block}, dl rate now #{(dlrate / 1024.0).round}kb/s"
|
398
|
+
|
399
|
+
piece = @package.pieces[block.pindex] # find corresponding piece
|
400
|
+
piece.add_block block
|
401
|
+
send_event(:received_block, block)
|
402
|
+
get_want_blocks
|
403
|
+
end
|
404
|
+
|
405
|
+
def send_bytes(s)
|
406
|
+
if s.nil?
|
407
|
+
raise "can't send nil"
|
408
|
+
elsif s.length > 0
|
409
|
+
@socket.send(s, 0)
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
413
|
+
def recv_bytes(len)
|
414
|
+
if len < 0
|
415
|
+
raise "can't recv negative bytes"
|
416
|
+
elsif len == 0
|
417
|
+
""
|
418
|
+
elsif len > 512 * 1024 # 512k
|
419
|
+
raise ProtocolError, "read size too big."
|
420
|
+
else
|
421
|
+
r = ""
|
422
|
+
zeros = 0
|
423
|
+
while r.length < len
|
424
|
+
x = @socket.recv(len - r.length)
|
425
|
+
raise IOError, "zero bytes received" if x.length == 0
|
426
|
+
r += x
|
427
|
+
end
|
428
|
+
r
|
429
|
+
end
|
430
|
+
end
|
431
|
+
|
432
|
+
def handle_message(m)
|
433
|
+
case m.id
|
434
|
+
when :choke
|
435
|
+
# rt_debug "#{self}: peer choking (was #{@peer_choking})"
|
436
|
+
@peer_choking = true
|
437
|
+
@want_blocks_m.synchronize do
|
438
|
+
@controller.forget_blocks @want_blocks
|
439
|
+
@want_blocks.clear
|
440
|
+
end
|
441
|
+
|
442
|
+
when :unchoke
|
443
|
+
# rt_debug "#{self}: peer not choking (was #{@peer_choking})"
|
444
|
+
@peer_choking = false
|
445
|
+
|
446
|
+
when :interested
|
447
|
+
# rt_debug "peer interested (was #{@peer_interested})"
|
448
|
+
@peer_interested = true
|
449
|
+
|
450
|
+
when :uninterested
|
451
|
+
# rt_debug "peer not interested (was #{@peer_interested})"
|
452
|
+
@peer_interested = false
|
453
|
+
|
454
|
+
when :have
|
455
|
+
# rt_debug "peer has piece #{m.index}"
|
456
|
+
rt_warning "#{self}: peer already has piece #{m.index}" if @peer_pieces[m.index]
|
457
|
+
@peer_pieces[m.index] = true
|
458
|
+
@peer_virgin = false
|
459
|
+
send_event(:peer_has_piece, m)
|
460
|
+
recalc_interested
|
461
|
+
|
462
|
+
when :bitfield
|
463
|
+
# rt_debug "peer reports bitfield #{m.bitfield.inspect}"
|
464
|
+
barray = m.bitfield.extend(StringToBarray).to_barray
|
465
|
+
|
466
|
+
expected_pieces = @package.num_pieces - (@package.num_pieces % 8) + ((@package.num_pieces % 8) == 0 ? 0 : 8)
|
467
|
+
raise ProtocolError, "invalid length in bitfield message (package has #{@package.num_pieces} pieces; bitfield should be size #{expected_pieces} but is #{barray.length} pieces)" unless barray.length == expected_pieces
|
468
|
+
|
469
|
+
@peer_pieces.each_index { |i| @peer_pieces[i] = barray[i] }
|
470
|
+
@peer_virgin = false
|
471
|
+
send_event(:peer_has_pieces, barray)
|
472
|
+
recalc_interested
|
473
|
+
get_want_blocks
|
474
|
+
|
475
|
+
when :request
|
476
|
+
return rt_warning("#{self}: peer requests invalid piece #{m.index}") unless m.index < @package.num_pieces
|
477
|
+
return rt_warning("#{self}: peer requests a block but we're choking") if @choking
|
478
|
+
return rt_warning("#{self}: peer requests a block but isn't interested") unless @peer_interested
|
479
|
+
return rt_warning("#{self}: peer requested too many blocks, ignoring") if @peer_want_blocks.length > MAX_PEER_REQUESTS
|
480
|
+
|
481
|
+
piece = @package.pieces[m.index]
|
482
|
+
return rt_warning("#{self}: peer requests unavailable block from piece #{piece}") unless piece.complete?
|
483
|
+
|
484
|
+
@peer_want_blocks.push piece.get_complete_block(m.begin, m.length)
|
485
|
+
|
486
|
+
when :piece
|
487
|
+
raise "can't handle piece here"
|
488
|
+
|
489
|
+
when :cancel
|
490
|
+
b = Block.new(m.index, m.begin, m.length)
|
491
|
+
# rt_debug "peer cancels #{b}"
|
492
|
+
if @peer_want_blocks.delete2(b) == nil
|
493
|
+
rt_warning "#{self}: peer wants to cancel unrequested block #{b}"
|
494
|
+
end
|
495
|
+
|
496
|
+
else
|
497
|
+
raise "unknown message #{type}"
|
498
|
+
end
|
499
|
+
end
|
500
|
+
|
501
|
+
## queues a message for delivery. (for :piece messages, this
|
502
|
+
## transmits everything but the piece itself)
|
503
|
+
def queue_message(id, args=nil)
|
504
|
+
@send_q.push Message.new(id, args)
|
505
|
+
end
|
506
|
+
|
507
|
+
## talks to Controller and get some new blocks to request. could be
|
508
|
+
## slow. this is presumably called whenever the queue of requests is
|
509
|
+
## too small.
|
510
|
+
def get_want_blocks
|
511
|
+
return if (@want_blocks.length >= MIN_REQUESTS) || @peer_virgin || @peer_choking || !@interested
|
512
|
+
|
513
|
+
rej_count = 0
|
514
|
+
acc_count = 0
|
515
|
+
@controller.claim_blocks do |b|
|
516
|
+
break if @want_blocks.length >= MAX_REQUESTS
|
517
|
+
if @peer_pieces[b.pindex] && !@want_blocks.member?(b)
|
518
|
+
rt_debug "! #{self}: starting new piece #{@package.pieces[b.pindex]}" unless @package.pieces[b.pindex].started?
|
519
|
+
|
520
|
+
# rt_debug "#{self}: added to queue block #{b}"
|
521
|
+
# puts "#{self}: claimed block #{b}"
|
522
|
+
@want_blocks.push b
|
523
|
+
acc_count += 1
|
524
|
+
true
|
525
|
+
else
|
526
|
+
# puts "#{self}: cont offers block #{b} but peer has? #{@peer_pieces[b.pindex]} i already want? #{@want_blocks.member? b}" if rej_count < 10
|
527
|
+
rej_count += 1
|
528
|
+
false
|
529
|
+
end
|
530
|
+
end
|
531
|
+
# puts "#{self}: ... and #{rej_count} more (peer has #{@peer_pieces.inject(0) { |s, p| s + (p ? 1 : 0) }} pieces)... " if rej_count >= 10
|
532
|
+
# puts "#{self}: accepted #{acc_count} blocks, rejected #{rej_count} blocks"
|
533
|
+
end
|
534
|
+
end
|
535
|
+
|
536
|
+
end
|