rubytorrent 0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|