p2p2 0.14.0 → 0.18.0
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.
- checksums.yaml +4 -4
- data/lib/p2p2.rb +3 -3
- data/lib/p2p2/{hex.rb → custom.rb} +2 -5
- data/lib/p2p2/head.rb +28 -26
- data/lib/p2p2/p1.rb +114 -945
- data/lib/p2p2/p1_custom.rb +7 -0
- data/lib/p2p2/p1_worker.rb +924 -0
- data/lib/p2p2/p2.rb +114 -1013
- data/lib/p2p2/p2_custom.rb +7 -0
- data/lib/p2p2/p2_worker.rb +963 -0
- data/lib/p2p2/p2pd.rb +120 -135
- data/lib/p2p2/p2pd_worker.rb +78 -0
- data/lib/p2p2/version.rb +3 -3
- data/p2p2.gemspec +34 -29
- metadata +10 -4
@@ -0,0 +1,924 @@
|
|
1
|
+
module P2p2
|
2
|
+
class P1Worker
|
3
|
+
|
4
|
+
##
|
5
|
+
# initialize
|
6
|
+
#
|
7
|
+
def initialize( p2pd_host, p2pd_port, room, appd_host, appd_port, dst_chunk_dir, tund_chunk_dir )
|
8
|
+
@p2pd_addr = Socket.sockaddr_in( p2pd_port, p2pd_host )
|
9
|
+
@room = room
|
10
|
+
@appd_addr = Socket.sockaddr_in( appd_port, appd_host )
|
11
|
+
@dst_chunk_dir = dst_chunk_dir
|
12
|
+
@tund_chunk_dir = tund_chunk_dir
|
13
|
+
@custom = P2p2::P1Custom.new
|
14
|
+
@mutex = Mutex.new
|
15
|
+
@reads = []
|
16
|
+
@writes = []
|
17
|
+
@roles = {} # sock => :dotr / :dst / :tund
|
18
|
+
@dst_infos = {} # dst => {}
|
19
|
+
|
20
|
+
dotr, dotw = IO.pipe
|
21
|
+
@dotw = dotw
|
22
|
+
add_read( dotr, :dotr )
|
23
|
+
new_a_tund
|
24
|
+
end
|
25
|
+
|
26
|
+
##
|
27
|
+
# looping
|
28
|
+
#
|
29
|
+
def looping
|
30
|
+
puts "#{ Time.new } looping"
|
31
|
+
loop_update_room
|
32
|
+
loop_check_expire
|
33
|
+
loop_check_status
|
34
|
+
|
35
|
+
loop do
|
36
|
+
rs, ws = IO.select( @reads, @writes )
|
37
|
+
|
38
|
+
@mutex.synchronize do
|
39
|
+
# 先写,再读
|
40
|
+
ws.each do | sock |
|
41
|
+
case @roles[ sock ]
|
42
|
+
when :dst
|
43
|
+
write_dst( sock )
|
44
|
+
when :tund
|
45
|
+
write_tund( sock )
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
rs.each do | sock |
|
50
|
+
case @roles[ sock ]
|
51
|
+
when :dotr
|
52
|
+
read_dotr( sock )
|
53
|
+
when :dst
|
54
|
+
read_dst( sock )
|
55
|
+
when :tund
|
56
|
+
read_tund( sock )
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
rescue Interrupt => e
|
62
|
+
puts e.class
|
63
|
+
quit!
|
64
|
+
end
|
65
|
+
|
66
|
+
##
|
67
|
+
# quit!
|
68
|
+
#
|
69
|
+
def quit!
|
70
|
+
if !@tund.closed? && @tund_info[ :tun_addr ]
|
71
|
+
# puts "debug1 send tund fin"
|
72
|
+
data = [ 0, TUND_FIN ].pack( 'Q>C' )
|
73
|
+
@tund.sendmsg( data, 0, @tund_info[ :tun_addr ] )
|
74
|
+
end
|
75
|
+
|
76
|
+
# puts "debug1 exit"
|
77
|
+
exit
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
##
|
83
|
+
# loop update room
|
84
|
+
#
|
85
|
+
def loop_update_room( update_at = Time.new )
|
86
|
+
Thread.new do
|
87
|
+
loop do
|
88
|
+
sleep UPDATE_ROOM_INTERVAL
|
89
|
+
|
90
|
+
@mutex.synchronize do
|
91
|
+
if !@tund.closed? && @tund_info[ :peer_addr ].nil?
|
92
|
+
now = Time.new
|
93
|
+
|
94
|
+
if now - update_at >= 60
|
95
|
+
data = @room
|
96
|
+
update_at = now
|
97
|
+
else
|
98
|
+
data = [ rand( 128 ) ].pack( 'C' )
|
99
|
+
end
|
100
|
+
|
101
|
+
add_tund_ctlmsg( data, @p2pd_addr )
|
102
|
+
next_tick
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
##
|
110
|
+
# loop check expire
|
111
|
+
#
|
112
|
+
def loop_check_expire
|
113
|
+
Thread.new do
|
114
|
+
loop do
|
115
|
+
sleep CHECK_EXPIRE_INTERVAL
|
116
|
+
|
117
|
+
@mutex.synchronize do
|
118
|
+
need_trigger = false
|
119
|
+
now = Time.new
|
120
|
+
|
121
|
+
if !@tund.closed? && @tund_info[ :tun_addr ]
|
122
|
+
if now - @tund_info[ :last_recv_at ] > EXPIRE_AFTER
|
123
|
+
puts "#{ Time.new } expire tund"
|
124
|
+
set_is_closing( @tund )
|
125
|
+
else
|
126
|
+
# puts "debug1 #{ Time.new } heartbeat"
|
127
|
+
add_tund_ctlmsg( pack_a_heartbeat )
|
128
|
+
|
129
|
+
@tund_info[ :dst_exts ].each do | dst_local_port, dst_ext |
|
130
|
+
if dst_ext[ :dst ].closed? && ( now - dst_ext[ :last_continue_at ] > EXPIRE_AFTER )
|
131
|
+
puts "#{ Time.new } expire dst ext #{ dst_local_port }"
|
132
|
+
del_dst_ext( dst_local_port )
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
need_trigger = true
|
138
|
+
end
|
139
|
+
|
140
|
+
@dst_infos.each do | dst, dst_info |
|
141
|
+
is_expired = dst_info[ :last_recv_at ].nil? && ( now - dst_info[ :created_at ] > EXPIRE_NEW )
|
142
|
+
|
143
|
+
if is_expired
|
144
|
+
puts "#{ Time.new } expire dst"
|
145
|
+
set_is_closing( dst )
|
146
|
+
need_trigger = true
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
if need_trigger
|
151
|
+
next_tick
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
##
|
159
|
+
# loop check status
|
160
|
+
#
|
161
|
+
def loop_check_status
|
162
|
+
Thread.new do
|
163
|
+
loop do
|
164
|
+
sleep STATUS_INTERVAL
|
165
|
+
|
166
|
+
@mutex.synchronize do
|
167
|
+
if !@tund.closed? && @tund_info[ :tun_addr ]
|
168
|
+
need_trigger = false
|
169
|
+
|
170
|
+
if @tund_info[ :dst_exts ].any?
|
171
|
+
now = Time.new
|
172
|
+
|
173
|
+
@tund_info[ :dst_exts ].each do | dst_local_port, dst_ext |
|
174
|
+
if now - dst_ext[ :last_continue_at ] < SEND_STATUS_UNTIL
|
175
|
+
data = [ 0, DEST_STATUS, dst_local_port, dst_ext[ :relay_pack_id ], dst_ext[ :continue_src_pack_id ] ].pack( 'Q>CnQ>Q>' )
|
176
|
+
add_tund_ctlmsg( data )
|
177
|
+
need_trigger = true
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
if @tund_info[ :paused ] && ( @tund_info[ :dst_exts ].map{ | _, dst_ext | dst_ext[ :wmems ].size }.sum < RESUME_BELOW )
|
183
|
+
puts "#{ Time.new } resume tund"
|
184
|
+
@tund_info[ :paused ] = false
|
185
|
+
add_write( @tund )
|
186
|
+
need_trigger = true
|
187
|
+
end
|
188
|
+
|
189
|
+
if need_trigger
|
190
|
+
next_tick
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
##
|
199
|
+
# loop punch peer
|
200
|
+
#
|
201
|
+
def loop_punch_peer
|
202
|
+
Thread.new do
|
203
|
+
EXPIRE_NEW.times do
|
204
|
+
if @tund.closed?
|
205
|
+
# puts "debug1 break loop punch peer"
|
206
|
+
break
|
207
|
+
end
|
208
|
+
|
209
|
+
@mutex.synchronize do
|
210
|
+
# puts "debug1 punch peer"
|
211
|
+
add_tund_ctlmsg( pack_a_heartbeat, @tund_info[ :peer_addr ] )
|
212
|
+
next_tick
|
213
|
+
end
|
214
|
+
|
215
|
+
sleep 1
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
##
|
221
|
+
# new a tund
|
222
|
+
#
|
223
|
+
def new_a_tund
|
224
|
+
tund = Socket.new( Socket::AF_INET, Socket::SOCK_DGRAM, 0 )
|
225
|
+
tund.bind( Socket.sockaddr_in( 0, '0.0.0.0' ) )
|
226
|
+
port = tund.local_address.ip_port
|
227
|
+
puts "#{ Time.new } tund bind on #{ port }"
|
228
|
+
|
229
|
+
tund_info = {
|
230
|
+
port: port, # 端口
|
231
|
+
ctlmsgs: [], # [ to_addr, data ]
|
232
|
+
wbuffs: [], # 写前缓存 [ dst_local_port, pack_id, data ]
|
233
|
+
caches: [], # 块读出缓存 [ dst_local_port, pack_id, data ]
|
234
|
+
chunks: [], # 块队列 filename
|
235
|
+
spring: 0, # 块后缀,结块时,如果块队列不为空,则自增,为空,则置为0
|
236
|
+
peer_addr: nil, # 对面地址
|
237
|
+
tun_addr: nil, # 连通后的tun地址
|
238
|
+
dst_exts: {}, # dst额外信息 dst_local_port => {}
|
239
|
+
dst_local_ports: {}, # src_id => dst_local_port
|
240
|
+
paused: false, # 是否暂停写
|
241
|
+
resendings: [], # 重传队列 [ dst_local_port, pack_id ]
|
242
|
+
created_at: Time.new, # 创建时间
|
243
|
+
last_recv_at: nil, # 上一次收到流量的时间,过期关闭
|
244
|
+
is_closing: false # 是否准备关闭
|
245
|
+
}
|
246
|
+
|
247
|
+
@tund = tund
|
248
|
+
@tund_info = tund_info
|
249
|
+
add_read( tund, :tund )
|
250
|
+
add_tund_ctlmsg( @room, @p2pd_addr )
|
251
|
+
end
|
252
|
+
|
253
|
+
##
|
254
|
+
# pack a heartbeat
|
255
|
+
#
|
256
|
+
def pack_a_heartbeat
|
257
|
+
[ 0, HEARTBEAT, rand( 128 ) ].pack( 'Q>CC' )
|
258
|
+
end
|
259
|
+
|
260
|
+
##
|
261
|
+
# is match tun addr
|
262
|
+
#
|
263
|
+
def is_match_tun_addr( addrinfo )
|
264
|
+
from_addr = addrinfo.to_sockaddr
|
265
|
+
|
266
|
+
if from_addr != @tund_info[ :tun_addr ]
|
267
|
+
puts "#{ Time.new } #{ addrinfo.inspect } not match #{ Addrinfo.new( @tund_info[ :tun_addr ] ).inspect }"
|
268
|
+
return false
|
269
|
+
end
|
270
|
+
|
271
|
+
@tund_info[ :last_recv_at ] = Time.new
|
272
|
+
true
|
273
|
+
end
|
274
|
+
|
275
|
+
##
|
276
|
+
# add tund ctlmsg
|
277
|
+
#
|
278
|
+
def add_tund_ctlmsg( data, to_addr = nil )
|
279
|
+
unless to_addr
|
280
|
+
to_addr = @tund_info[ :tun_addr ]
|
281
|
+
end
|
282
|
+
|
283
|
+
if to_addr
|
284
|
+
@tund_info[ :ctlmsgs ] << [ to_addr, data ]
|
285
|
+
add_write( @tund )
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
##
|
290
|
+
# add tund wbuff
|
291
|
+
#
|
292
|
+
def add_tund_wbuff( dst_local_port, pack_id, data )
|
293
|
+
@tund_info[ :wbuffs ] << [ dst_local_port, pack_id, data ]
|
294
|
+
|
295
|
+
if @tund_info[ :wbuffs ].size >= WBUFFS_LIMIT
|
296
|
+
spring = @tund_info[ :chunks ].size > 0 ? ( @tund_info[ :spring ] + 1 ) : 0
|
297
|
+
filename = "#{ Process.pid }-#{ @tund_info[ :port ] }.#{ spring }"
|
298
|
+
chunk_path = File.join( @tund_chunk_dir, filename )
|
299
|
+
datas = @tund_info[ :wbuffs ].map{ | _dst_local_port, _pack_id, _data | [ [ _dst_local_port, _pack_id, _data.bytesize ].pack( 'nQ>n' ), _data ].join }
|
300
|
+
|
301
|
+
begin
|
302
|
+
IO.binwrite( chunk_path, datas.join )
|
303
|
+
rescue Errno::ENOSPC => e
|
304
|
+
puts "#{ Time.new } #{ e.class }, close tund"
|
305
|
+
set_is_closing( @tund )
|
306
|
+
return
|
307
|
+
end
|
308
|
+
|
309
|
+
@tund_info[ :chunks ] << filename
|
310
|
+
@tund_info[ :spring ] = spring
|
311
|
+
@tund_info[ :wbuffs ].clear
|
312
|
+
end
|
313
|
+
|
314
|
+
add_write( @tund )
|
315
|
+
end
|
316
|
+
|
317
|
+
##
|
318
|
+
# add dst wbuff
|
319
|
+
#
|
320
|
+
def add_dst_wbuff( dst, data )
|
321
|
+
dst_info = @dst_infos[ dst ]
|
322
|
+
dst_info[ :wbuff ] << data
|
323
|
+
|
324
|
+
if dst_info[ :wbuff ].bytesize >= CHUNK_SIZE
|
325
|
+
spring = dst_info[ :chunks ].size > 0 ? ( dst_info[ :spring ] + 1 ) : 0
|
326
|
+
filename = "#{ Process.pid }-#{ dst_info[ :local_port ] }.#{ spring }"
|
327
|
+
chunk_path = File.join( @dst_chunk_dir, filename )
|
328
|
+
|
329
|
+
begin
|
330
|
+
IO.binwrite( chunk_path, dst_info[ :wbuff ] )
|
331
|
+
rescue Errno::ENOSPC => e
|
332
|
+
puts "#{ Time.new } #{ e.class }, close dst"
|
333
|
+
set_is_closing( dst )
|
334
|
+
return
|
335
|
+
end
|
336
|
+
|
337
|
+
dst_info[ :chunks ] << filename
|
338
|
+
dst_info[ :spring ] = spring
|
339
|
+
dst_info[ :wbuff ].clear
|
340
|
+
end
|
341
|
+
|
342
|
+
add_write( dst )
|
343
|
+
end
|
344
|
+
|
345
|
+
##
|
346
|
+
# add read
|
347
|
+
#
|
348
|
+
def add_read( sock, role )
|
349
|
+
unless @reads.include?( sock )
|
350
|
+
@reads << sock
|
351
|
+
end
|
352
|
+
|
353
|
+
@roles[ sock ] = role
|
354
|
+
end
|
355
|
+
|
356
|
+
##
|
357
|
+
# add write
|
358
|
+
#
|
359
|
+
def add_write( sock )
|
360
|
+
if sock && !sock.closed? && !@writes.include?( sock )
|
361
|
+
@writes << sock
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
##
|
366
|
+
# set is closing
|
367
|
+
#
|
368
|
+
def set_is_closing( sock )
|
369
|
+
if sock && !sock.closed?
|
370
|
+
role = @roles[ sock ]
|
371
|
+
# puts "debug1 set #{ role.to_s } is closing"
|
372
|
+
|
373
|
+
case role
|
374
|
+
when :dst
|
375
|
+
dst_info = @dst_infos[ sock ]
|
376
|
+
dst_info[ :is_closing ] = true
|
377
|
+
when :tund
|
378
|
+
@tund_info[ :is_closing ] = true
|
379
|
+
end
|
380
|
+
|
381
|
+
@reads.delete( sock )
|
382
|
+
add_write( sock )
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
##
|
387
|
+
# close dst
|
388
|
+
#
|
389
|
+
def close_dst( dst )
|
390
|
+
# puts "debug1 close dst"
|
391
|
+
close_sock( dst )
|
392
|
+
dst_info = @dst_infos.delete( dst )
|
393
|
+
|
394
|
+
dst_info[ :chunks ].each do | filename |
|
395
|
+
begin
|
396
|
+
File.delete( File.join( @dst_chunk_dir, filename ) )
|
397
|
+
rescue Errno::ENOENT
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
return if @tund.closed?
|
402
|
+
|
403
|
+
local_port = dst_info[ :local_port ]
|
404
|
+
dst_ext = @tund_info[ :dst_exts ][ local_port ]
|
405
|
+
return unless dst_ext
|
406
|
+
|
407
|
+
if dst_ext[ :is_src_closed ]
|
408
|
+
# puts "debug1 4-3. after close dst -> src closed ? yes -> del dst ext -> send fin2"
|
409
|
+
del_dst_ext( local_port )
|
410
|
+
data = [ 0, FIN2, local_port ].pack( 'Q>Cn' )
|
411
|
+
add_tund_ctlmsg( data )
|
412
|
+
else
|
413
|
+
# puts "debug1 3-1. after close dst -> src closed ? no -> send fin1"
|
414
|
+
data = [ 0, FIN1, local_port, dst_info[ :biggest_pack_id ], dst_ext[ :continue_src_pack_id ] ].pack( 'Q>CnQ>Q>' )
|
415
|
+
add_tund_ctlmsg( data )
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
##
|
420
|
+
# close tund
|
421
|
+
#
|
422
|
+
def close_tund( tund )
|
423
|
+
# puts "debug1 close tund"
|
424
|
+
close_sock( tund )
|
425
|
+
|
426
|
+
@tund_info[ :chunks ].each do | filename |
|
427
|
+
begin
|
428
|
+
File.delete( File.join( @tund_chunk_dir, filename ) )
|
429
|
+
rescue Errno::ENOENT
|
430
|
+
end
|
431
|
+
end
|
432
|
+
|
433
|
+
@tund_info[ :dst_exts ].each{ | _, dst_ext | set_is_closing( dst_ext[ :dst ] ) }
|
434
|
+
end
|
435
|
+
|
436
|
+
##
|
437
|
+
# close sock
|
438
|
+
#
|
439
|
+
def close_sock( sock )
|
440
|
+
sock.close
|
441
|
+
@reads.delete( sock )
|
442
|
+
@writes.delete( sock )
|
443
|
+
@roles.delete( sock )
|
444
|
+
end
|
445
|
+
|
446
|
+
##
|
447
|
+
# del dst ext
|
448
|
+
#
|
449
|
+
def del_dst_ext( dst_local_port )
|
450
|
+
dst_ext = @tund_info[ :dst_exts ].delete( dst_local_port )
|
451
|
+
|
452
|
+
if dst_ext
|
453
|
+
@tund_info[ :dst_local_ports ].delete( dst_ext[ :src_id ] )
|
454
|
+
end
|
455
|
+
end
|
456
|
+
|
457
|
+
##
|
458
|
+
# release wmems
|
459
|
+
#
|
460
|
+
def release_wmems( dst_ext, completed_pack_id )
|
461
|
+
if completed_pack_id > dst_ext[ :completed_pack_id ]
|
462
|
+
# puts "debug2 update completed pack #{ completed_pack_id }"
|
463
|
+
|
464
|
+
pack_ids = dst_ext[ :wmems ].keys.select { | pack_id | pack_id <= completed_pack_id }
|
465
|
+
|
466
|
+
pack_ids.each do | pack_id |
|
467
|
+
dst_ext[ :wmems ].delete( pack_id )
|
468
|
+
dst_ext[ :send_ats ].delete( pack_id )
|
469
|
+
end
|
470
|
+
|
471
|
+
dst_ext[ :completed_pack_id ] = completed_pack_id
|
472
|
+
end
|
473
|
+
end
|
474
|
+
|
475
|
+
##
|
476
|
+
# next tick
|
477
|
+
#
|
478
|
+
def next_tick
|
479
|
+
@dotw.write( '.' )
|
480
|
+
end
|
481
|
+
|
482
|
+
##
|
483
|
+
# write dst
|
484
|
+
#
|
485
|
+
def write_dst( dst )
|
486
|
+
dst_info = @dst_infos[ dst ]
|
487
|
+
data = dst_info[ :cache ]
|
488
|
+
from = :cache
|
489
|
+
|
490
|
+
if data.empty?
|
491
|
+
if dst_info[ :chunks ].any?
|
492
|
+
path = File.join( @dst_chunk_dir, dst_info[ :chunks ].shift )
|
493
|
+
|
494
|
+
begin
|
495
|
+
dst_info[ :cache ] = data = IO.binread( path )
|
496
|
+
File.delete( path )
|
497
|
+
rescue Errno::ENOENT => e
|
498
|
+
puts "#{ Time.new } read #{ path } #{ e.class }"
|
499
|
+
close_dst( dst )
|
500
|
+
return
|
501
|
+
end
|
502
|
+
else
|
503
|
+
data = dst_info[ :wbuff ]
|
504
|
+
from = :wbuff
|
505
|
+
end
|
506
|
+
end
|
507
|
+
|
508
|
+
if data.empty?
|
509
|
+
if dst_info[ :is_closing ]
|
510
|
+
close_dst( dst )
|
511
|
+
else
|
512
|
+
@writes.delete( dst )
|
513
|
+
end
|
514
|
+
|
515
|
+
return
|
516
|
+
end
|
517
|
+
|
518
|
+
begin
|
519
|
+
written = dst.write_nonblock( data )
|
520
|
+
rescue IO::WaitWritable, Errno::EINTR
|
521
|
+
return
|
522
|
+
rescue Exception => e
|
523
|
+
# puts "debug1 write dst #{ e.class }"
|
524
|
+
close_dst( dst )
|
525
|
+
return
|
526
|
+
end
|
527
|
+
|
528
|
+
# puts "debug2 write dst #{ written }"
|
529
|
+
data = data[ written..-1 ]
|
530
|
+
dst_info[ from ] = data
|
531
|
+
end
|
532
|
+
|
533
|
+
##
|
534
|
+
# write tund
|
535
|
+
#
|
536
|
+
def write_tund( tund )
|
537
|
+
if @tund_info[ :is_closing ]
|
538
|
+
close_tund( tund )
|
539
|
+
new_a_tund
|
540
|
+
return
|
541
|
+
end
|
542
|
+
|
543
|
+
# 传ctlmsg
|
544
|
+
while @tund_info[ :ctlmsgs ].any?
|
545
|
+
to_addr, data = @tund_info[ :ctlmsgs ].first
|
546
|
+
|
547
|
+
begin
|
548
|
+
tund.sendmsg( data, 0, to_addr )
|
549
|
+
rescue IO::WaitWritable, Errno::EINTR
|
550
|
+
return
|
551
|
+
end
|
552
|
+
|
553
|
+
@tund_info[ :ctlmsgs ].shift
|
554
|
+
end
|
555
|
+
|
556
|
+
# 重传
|
557
|
+
while @tund_info[ :resendings ].any?
|
558
|
+
dst_local_port, pack_id = @tund_info[ :resendings ].first
|
559
|
+
dst_ext = @tund_info[ :dst_exts ][ dst_local_port ]
|
560
|
+
|
561
|
+
if dst_ext
|
562
|
+
data = dst_ext[ :wmems ][ pack_id ]
|
563
|
+
|
564
|
+
if data
|
565
|
+
begin
|
566
|
+
tund.sendmsg( data, 0, @tund_info[ :tun_addr ] )
|
567
|
+
rescue IO::WaitWritable, Errno::EINTR
|
568
|
+
return
|
569
|
+
end
|
570
|
+
end
|
571
|
+
end
|
572
|
+
|
573
|
+
@tund_info[ :resendings ].shift
|
574
|
+
return
|
575
|
+
end
|
576
|
+
|
577
|
+
# 若写后达到上限,暂停取写前
|
578
|
+
if @tund_info[ :dst_exts ].map{ | _, dst_ext | dst_ext[ :wmems ].size }.sum >= WMEMS_LIMIT
|
579
|
+
unless @tund_info[ :paused ]
|
580
|
+
puts "#{ Time.new } pause tund #{ @tund_info[ :port ] }"
|
581
|
+
@tund_info[ :paused ] = true
|
582
|
+
end
|
583
|
+
|
584
|
+
@writes.delete( tund )
|
585
|
+
return
|
586
|
+
end
|
587
|
+
|
588
|
+
# 取写前
|
589
|
+
if @tund_info[ :caches ].any?
|
590
|
+
dst_local_port, pack_id, data = @tund_info[ :caches ].first
|
591
|
+
from = :caches
|
592
|
+
elsif @tund_info[ :chunks ].any?
|
593
|
+
path = File.join( @tund_chunk_dir, @tund_info[ :chunks ].shift )
|
594
|
+
|
595
|
+
begin
|
596
|
+
data = IO.binread( path )
|
597
|
+
File.delete( path )
|
598
|
+
rescue Errno::ENOENT => e
|
599
|
+
puts "#{ Time.new } read #{ path } #{ e.class }"
|
600
|
+
close_tund( tund )
|
601
|
+
return
|
602
|
+
end
|
603
|
+
|
604
|
+
caches = []
|
605
|
+
|
606
|
+
until data.empty?
|
607
|
+
_dst_local_port, _pack_id, pack_size = data[ 0, 12 ].unpack( 'nQ>n' )
|
608
|
+
caches << [ _dst_local_port, _pack_id, data[ 12, pack_size ] ]
|
609
|
+
data = data[ ( 12 + pack_size )..-1 ]
|
610
|
+
end
|
611
|
+
|
612
|
+
@tund_info[ :caches ] = caches
|
613
|
+
dst_local_port, pack_id, data = caches.first
|
614
|
+
from = :caches
|
615
|
+
elsif @tund_info[ :wbuffs ].any?
|
616
|
+
dst_local_port, pack_id, data = @tund_info[ :wbuffs ].first
|
617
|
+
from = :wbuffs
|
618
|
+
else
|
619
|
+
@writes.delete( tund )
|
620
|
+
return
|
621
|
+
end
|
622
|
+
|
623
|
+
dst_ext = @tund_info[ :dst_exts ][ dst_local_port ]
|
624
|
+
|
625
|
+
if dst_ext
|
626
|
+
if pack_id <= CONFUSE_UNTIL
|
627
|
+
data = @custom.encode( data )
|
628
|
+
# puts "debug1 encoded pack #{ pack_id }"
|
629
|
+
end
|
630
|
+
|
631
|
+
data = [ [ pack_id, dst_local_port ].pack( 'Q>n' ), data ].join
|
632
|
+
|
633
|
+
begin
|
634
|
+
tund.sendmsg( data, 0, @tund_info[ :tun_addr ] )
|
635
|
+
rescue IO::WaitWritable, Errno::EINTR
|
636
|
+
return
|
637
|
+
end
|
638
|
+
|
639
|
+
# puts "debug2 written pack #{ pack_id }"
|
640
|
+
now = Time.new
|
641
|
+
dst_ext[ :relay_pack_id ] = pack_id
|
642
|
+
dst_ext[ :wmems ][ pack_id ] = data
|
643
|
+
dst_ext[ :send_ats ][ pack_id ] = now
|
644
|
+
dst_ext[ :last_continue_at ] = now
|
645
|
+
end
|
646
|
+
|
647
|
+
@tund_info[ from ].shift
|
648
|
+
end
|
649
|
+
|
650
|
+
##
|
651
|
+
# read dotr
|
652
|
+
#
|
653
|
+
def read_dotr( dotr )
|
654
|
+
dotr.read( 1 )
|
655
|
+
end
|
656
|
+
|
657
|
+
##
|
658
|
+
# read dst
|
659
|
+
#
|
660
|
+
def read_dst( dst )
|
661
|
+
begin
|
662
|
+
data = dst.read_nonblock( PACK_SIZE )
|
663
|
+
rescue IO::WaitReadable, Errno::EINTR
|
664
|
+
return
|
665
|
+
rescue Exception => e
|
666
|
+
# puts "debug1 read dst #{ e.class }"
|
667
|
+
set_is_closing( dst )
|
668
|
+
return
|
669
|
+
end
|
670
|
+
|
671
|
+
# puts "debug2 read dst #{ data.inspect }"
|
672
|
+
dst_info = @dst_infos[ dst ]
|
673
|
+
dst_info[ :last_recv_at ] = Time.new
|
674
|
+
|
675
|
+
if @tund.closed?
|
676
|
+
puts "#{ Time.new } tund closed, close dst"
|
677
|
+
set_is_closing( dst )
|
678
|
+
return
|
679
|
+
end
|
680
|
+
|
681
|
+
pack_id = dst_info[ :biggest_pack_id ] + 1
|
682
|
+
dst_info[ :biggest_pack_id ] = pack_id
|
683
|
+
add_tund_wbuff( dst_info[ :local_port ], pack_id, data )
|
684
|
+
end
|
685
|
+
|
686
|
+
##
|
687
|
+
# read tund
|
688
|
+
#
|
689
|
+
def read_tund( tund )
|
690
|
+
data, addrinfo, rflags, *controls = tund.recvmsg
|
691
|
+
now = Time.new
|
692
|
+
pack_id = data[ 0, 8 ].unpack( 'Q>' ).first
|
693
|
+
|
694
|
+
if pack_id == 0
|
695
|
+
ctl_num = data[ 8 ].unpack( 'C' ).first
|
696
|
+
|
697
|
+
case ctl_num
|
698
|
+
when PEER_ADDR
|
699
|
+
return if @tund_info[ :peer_addr ] || ( addrinfo.to_sockaddr != @p2pd_addr )
|
700
|
+
|
701
|
+
peer_addr = data[ 9..-1 ]
|
702
|
+
puts "#{ Time.new } got peer addr #{ Addrinfo.new( peer_addr ).inspect }"
|
703
|
+
|
704
|
+
@tund_info[ :peer_addr ] = peer_addr
|
705
|
+
loop_punch_peer
|
706
|
+
when HEARTBEAT
|
707
|
+
from_addr = addrinfo.to_sockaddr
|
708
|
+
return if from_addr != @tund_info[ :peer_addr ]
|
709
|
+
|
710
|
+
# puts "debug1 set tun addr #{ Addrinfo.new( from_addr ).inspect }"
|
711
|
+
@tund_info[ :tun_addr ] = from_addr
|
712
|
+
@tund_info[ :last_recv_at ] = now
|
713
|
+
when A_NEW_SOURCE
|
714
|
+
return unless is_match_tun_addr( addrinfo )
|
715
|
+
|
716
|
+
src_id = data[ 9, 8 ].unpack( 'Q>' ).first
|
717
|
+
dst_local_port = @tund_info[ :dst_local_ports ][ src_id ]
|
718
|
+
# puts "debug1 got a new source #{ src_id }"
|
719
|
+
|
720
|
+
if dst_local_port
|
721
|
+
dst_ext = @tund_info[ :dst_exts ][ dst_local_port ]
|
722
|
+
return unless dst_ext
|
723
|
+
|
724
|
+
if dst_ext[ :dst ].closed?
|
725
|
+
dst_local_port = 0
|
726
|
+
end
|
727
|
+
else
|
728
|
+
dst = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
|
729
|
+
|
730
|
+
if RUBY_PLATFORM.include?( 'linux' )
|
731
|
+
dst.setsockopt( Socket::SOL_TCP, Socket::TCP_NODELAY, 1 )
|
732
|
+
end
|
733
|
+
|
734
|
+
begin
|
735
|
+
dst.connect_nonblock( @appd_addr )
|
736
|
+
rescue IO::WaitWritable
|
737
|
+
rescue Exception => e
|
738
|
+
puts "#{ Time.new } connect appd #{ e.class }"
|
739
|
+
return
|
740
|
+
end
|
741
|
+
|
742
|
+
dst_local_port = dst.local_address.ip_port
|
743
|
+
|
744
|
+
@dst_infos[ dst ] = {
|
745
|
+
local_port: dst_local_port, # 本地端口
|
746
|
+
biggest_pack_id: 0, # 最大包号码
|
747
|
+
wbuff: '', # 写前
|
748
|
+
cache: '', # 块读出缓存
|
749
|
+
chunks: [], # 块队列,写前达到块大小时结一个块 filename
|
750
|
+
spring: 0, # 块后缀,结块时,如果块队列不为空,则自增,为空,则置为0
|
751
|
+
created_at: Time.new, # 创建时间
|
752
|
+
last_recv_at: nil, # 上一次收到流量的时间,过期关闭
|
753
|
+
is_closing: false # 是否准备关闭
|
754
|
+
}
|
755
|
+
add_read( dst, :dst )
|
756
|
+
|
757
|
+
@tund_info[ :dst_local_ports ][ src_id ] = dst_local_port
|
758
|
+
@tund_info[ :dst_exts ][ dst_local_port ] = {
|
759
|
+
dst: dst, # dst
|
760
|
+
src_id: src_id, # 近端src id
|
761
|
+
wmems: {}, # 写后 pack_id => data
|
762
|
+
send_ats: {}, # 上一次发出时间 pack_id => send_at
|
763
|
+
relay_pack_id: 0, # 转发到几
|
764
|
+
continue_src_pack_id: 0, # 收到几
|
765
|
+
pieces: {}, # 跳号包 src_pack_id => data
|
766
|
+
is_src_closed: false, # src是否已关闭
|
767
|
+
biggest_src_pack_id: 0, # src最大包号码
|
768
|
+
completed_pack_id: 0, # 完成到几(对面收到几)
|
769
|
+
last_continue_at: Time.new # 创建,或者上一次收到连续流量,或者发出新包的时间
|
770
|
+
}
|
771
|
+
end
|
772
|
+
|
773
|
+
data2 = [ 0, PAIRED, src_id, dst_local_port ].pack( 'Q>CQ>n' )
|
774
|
+
# puts "debug1 add ctlmsg paired #{ data2.inspect }"
|
775
|
+
add_tund_ctlmsg( data2 )
|
776
|
+
when SOURCE_STATUS
|
777
|
+
return unless is_match_tun_addr( addrinfo )
|
778
|
+
|
779
|
+
src_id, relay_src_pack_id, continue_dst_pack_id = data[ 9, 24 ].unpack( 'Q>Q>Q>' )
|
780
|
+
|
781
|
+
dst_local_port = @tund_info[ :dst_local_ports ][ src_id ]
|
782
|
+
return unless dst_local_port
|
783
|
+
|
784
|
+
dst_ext = @tund_info[ :dst_exts ][ dst_local_port ]
|
785
|
+
return unless dst_ext
|
786
|
+
|
787
|
+
# puts "debug2 got source status"
|
788
|
+
|
789
|
+
release_wmems( dst_ext, continue_dst_pack_id )
|
790
|
+
|
791
|
+
# 发miss
|
792
|
+
if !dst_ext[ :dst ].closed? && ( dst_ext[ :continue_src_pack_id ] < relay_src_pack_id )
|
793
|
+
ranges = []
|
794
|
+
curr_pack_id = dst_ext[ :continue_src_pack_id ] + 1
|
795
|
+
|
796
|
+
dst_ext[ :pieces ].keys.sort.each do | pack_id |
|
797
|
+
if pack_id > curr_pack_id
|
798
|
+
ranges << [ curr_pack_id, pack_id - 1 ]
|
799
|
+
end
|
800
|
+
|
801
|
+
curr_pack_id = pack_id + 1
|
802
|
+
end
|
803
|
+
|
804
|
+
if curr_pack_id <= relay_src_pack_id
|
805
|
+
ranges << [ curr_pack_id, relay_src_pack_id ]
|
806
|
+
end
|
807
|
+
|
808
|
+
pack_count = 0
|
809
|
+
# puts "debug1 continue/relay #{ dst_ext[ :continue_src_pack_id ] }/#{ relay_src_pack_id } send MISS #{ ranges.size }"
|
810
|
+
|
811
|
+
ranges.each do | pack_id_begin, pack_id_end |
|
812
|
+
if pack_count >= BREAK_SEND_MISS
|
813
|
+
puts "#{ Time.new } break send miss at #{ pack_id_begin }"
|
814
|
+
break
|
815
|
+
end
|
816
|
+
|
817
|
+
data2 = [ 0, MISS, src_id, pack_id_begin, pack_id_end ].pack( 'Q>CQ>Q>Q>' )
|
818
|
+
add_tund_ctlmsg( data2 )
|
819
|
+
pack_count += ( pack_id_end - pack_id_begin + 1 )
|
820
|
+
end
|
821
|
+
end
|
822
|
+
when MISS
|
823
|
+
return unless is_match_tun_addr( addrinfo )
|
824
|
+
|
825
|
+
dst_local_port, pack_id_begin, pack_id_end = data[ 9, 18 ].unpack( 'nQ>Q>' )
|
826
|
+
|
827
|
+
dst_ext = @tund_info[ :dst_exts ][ dst_local_port ]
|
828
|
+
return unless dst_ext
|
829
|
+
|
830
|
+
( pack_id_begin..pack_id_end ).each do | pack_id |
|
831
|
+
send_at = dst_ext[ :send_ats ][ pack_id ]
|
832
|
+
|
833
|
+
if send_at
|
834
|
+
break if now - send_at < STATUS_INTERVAL
|
835
|
+
@tund_info[ :resendings ] << [ dst_local_port, pack_id ]
|
836
|
+
end
|
837
|
+
end
|
838
|
+
|
839
|
+
add_write( tund )
|
840
|
+
when FIN1
|
841
|
+
return unless is_match_tun_addr( addrinfo )
|
842
|
+
|
843
|
+
src_id, biggest_src_pack_id, continue_dst_pack_id = data[ 9, 24 ].unpack( 'Q>Q>Q>' )
|
844
|
+
|
845
|
+
dst_local_port = @tund_info[ :dst_local_ports ][ src_id ]
|
846
|
+
return unless dst_local_port
|
847
|
+
|
848
|
+
dst_ext = @tund_info[ :dst_exts ][ dst_local_port ]
|
849
|
+
return unless dst_ext
|
850
|
+
|
851
|
+
# puts "debug1 got fin1 #{ src_id } biggest src pack #{ biggest_src_pack_id } completed dst pack #{ continue_dst_pack_id }"
|
852
|
+
dst_ext[ :is_src_closed ] = true
|
853
|
+
dst_ext[ :biggest_src_pack_id ] = biggest_src_pack_id
|
854
|
+
release_wmems( dst_ext, continue_dst_pack_id )
|
855
|
+
|
856
|
+
if biggest_src_pack_id == dst_ext[ :continue_src_pack_id ]
|
857
|
+
# puts "debug1 4-1. tund recv fin1 -> all traffic received ? -> close dst after write"
|
858
|
+
set_is_closing( dst_ext[ :dst ] )
|
859
|
+
end
|
860
|
+
when FIN2
|
861
|
+
return unless is_match_tun_addr( addrinfo )
|
862
|
+
|
863
|
+
src_id = data[ 9, 8 ].unpack( 'Q>' ).first
|
864
|
+
|
865
|
+
dst_local_port = @tund_info[ :dst_local_ports ][ src_id ]
|
866
|
+
return unless dst_local_port
|
867
|
+
|
868
|
+
# puts "debug1 3-2. tund recv fin2 -> del dst ext"
|
869
|
+
del_dst_ext( dst_local_port )
|
870
|
+
when TUN_FIN
|
871
|
+
return unless is_match_tun_addr( addrinfo )
|
872
|
+
|
873
|
+
puts "#{ Time.new } recv tun fin"
|
874
|
+
set_is_closing( tund )
|
875
|
+
end
|
876
|
+
|
877
|
+
return
|
878
|
+
end
|
879
|
+
|
880
|
+
return unless is_match_tun_addr( addrinfo )
|
881
|
+
|
882
|
+
src_id = data[ 8, 8 ].unpack( 'Q>' ).first
|
883
|
+
|
884
|
+
dst_local_port = @tund_info[ :dst_local_ports ][ src_id ]
|
885
|
+
return unless dst_local_port
|
886
|
+
|
887
|
+
dst_ext = @tund_info[ :dst_exts ][ dst_local_port ]
|
888
|
+
return if dst_ext.nil? || dst_ext[ :dst ].closed?
|
889
|
+
return if ( pack_id <= dst_ext[ :continue_src_pack_id ] ) || dst_ext[ :pieces ].include?( pack_id )
|
890
|
+
|
891
|
+
data = data[ 16..-1 ]
|
892
|
+
# puts "debug2 got pack #{ pack_id }"
|
893
|
+
|
894
|
+
if pack_id <= CONFUSE_UNTIL
|
895
|
+
# puts "debug2 #{ data.inspect }"
|
896
|
+
data = @custom.decode( data )
|
897
|
+
# puts "debug1 decoded pack #{ pack_id }"
|
898
|
+
end
|
899
|
+
|
900
|
+
# 放进写前,跳号放碎片缓存
|
901
|
+
if pack_id - dst_ext[ :continue_src_pack_id ] == 1
|
902
|
+
while dst_ext[ :pieces ].include?( pack_id + 1 )
|
903
|
+
data << dst_ext[ :pieces ].delete( pack_id + 1 )
|
904
|
+
pack_id += 1
|
905
|
+
end
|
906
|
+
|
907
|
+
dst_ext[ :continue_src_pack_id ] = pack_id
|
908
|
+
dst_ext[ :last_continue_at ] = now
|
909
|
+
add_dst_wbuff( dst_ext[ :dst ], data )
|
910
|
+
# puts "debug2 update continue src pack #{ pack_id }"
|
911
|
+
|
912
|
+
# 接到流量,若对面已关闭,且流量正好收全,关闭dst
|
913
|
+
if dst_ext[ :is_src_closed ] && ( pack_id == dst_ext[ :biggest_src_pack_id ] )
|
914
|
+
# puts "debug1 4-2. tund recv traffic -> src closed and all traffic received ? -> close dst after write"
|
915
|
+
set_is_closing( dst_ext[ :dst ] )
|
916
|
+
return
|
917
|
+
end
|
918
|
+
else
|
919
|
+
dst_ext[ :pieces ][ pack_id ] = data
|
920
|
+
end
|
921
|
+
end
|
922
|
+
|
923
|
+
end
|
924
|
+
end
|