p2p2 0.15.0 → 0.15.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ca925ece6d1579aa867c70dbc92911eb3c0046e855c2579115738f2688dd08c5
4
- data.tar.gz: 33c156afa25319ed03758e530aab81ae1ddc2e27059ea7bc5dae463a1a14761b
3
+ metadata.gz: 6bff8fc4cd9d6dcad000d50c1e9052651daba7ded7cc263fa3f858e9a0577351
4
+ data.tar.gz: 836cd80f2d75042e18525db9e663096c52661fafe2066a77fbcdce3124cdc14f
5
5
  SHA512:
6
- metadata.gz: 8bf48c99e8242a66ec4f9774dd494ed57c9720c404e3a8cb0481c74dc3bcbd3fbf97c6cf2cfece23fdb7ad1bc5aa13cbf109baf929ef038ab756c2a3bb28a6ba
7
- data.tar.gz: 3cb4955440287396afb177f13a800a67eca199531fc1956d8004a44372e8fc3a0ebd446b5ad959c271470fabda7ebbfe6358cc2dba1f6421bf55dd6caf7a2e4f
6
+ metadata.gz: 39772ef8a4d49803851f84a17d2104a1276c236459a10e8984736ebaa19108f54b60f0734a5eb52b84e899572a7c75cc91f585b8d376c30da51b759894a6c17d
7
+ data.tar.gz: 409f469b109180e78172a36d6c56b1693217a9da61f84edeae43ae34e59172145596cb2c56691b521cb6a6238887fa52d39195d6b2323fd59b85b6873bd8d825
data/lib/p2p2.rb CHANGED
@@ -1,3 +1,3 @@
1
- require 'p2p2/p1'
2
- require 'p2p2/p2'
3
- require 'p2p2/p2pd'
1
+ require 'p2p2/p1'
2
+ require 'p2p2/p2'
3
+ require 'p2p2/p2pd'
data/lib/p2p2/head.rb CHANGED
@@ -1,27 +1,27 @@
1
- module P2p2
2
- PACK_SIZE = 1328 # 包大小 1400(console MTU) - 8(PPPoE header) - 40(IPv6 header) - 8(UDP header) - 8(app/shadow id) - 8(pack id) = 1328
3
- CHUNK_SIZE = PACK_SIZE * 1000 # 块大小
4
- WBUFFS_LIMIT = 1000 # 写前上限,超过上限结一个块
5
- WMEMS_LIMIT = 100_000 # 写后上限,达到上限暂停写
6
- RESUME_BELOW = 50_000 # 降到多少以下恢复写
7
- EXPIRE_AFTER = 1800 # 多久过期
8
- HEARTBEAT_INTERVAL = 3 # 心跳间隔
9
- STATUS_INTERVAL = 0.3 # 发送状态间隔
10
- SEND_STATUS_UNTIL = 10 # 持续的告之对面状态,直到没有流量往来,持续多少秒
11
- BREAK_SEND_MISS = 10_000 # miss包个数上限,达到上限忽略要后面的段,可控碎片缓存
12
- PEER_ADDR = 1
13
- HEARTBEAT = 2
14
- A_NEW_APP = 3
15
- PAIRED = 4
16
- SHADOW_STATUS = 5
17
- APP_STATUS = 6
18
- MISS = 7
19
- FIN1 = 8
20
- GOT_FIN1 = 9
21
- FIN2 = 10
22
- GOT_FIN2 = 11
23
- P1_FIN = 12
24
- P2_FIN = 13
25
- CTL_CLOSE = 1
26
- CTL_RESUME = 2
27
- end
1
+ module P2p2
2
+ PACK_SIZE = 1328 # 包大小 1400(console MTU) - 8(PPPoE header) - 40(IPv6 header) - 8(UDP header) - 8(app/shadow id) - 8(pack id) = 1328
3
+ CHUNK_SIZE = PACK_SIZE * 1000 # 块大小
4
+ WBUFFS_LIMIT = 1000 # 写前上限,超过上限结一个块
5
+ WMEMS_LIMIT = 100_000 # 写后上限,达到上限暂停写
6
+ RESUME_BELOW = 50_000 # 降到多少以下恢复写
7
+ EXPIRE_AFTER = 1800 # 多久过期
8
+ HEARTBEAT_INTERVAL = 3 # 心跳间隔
9
+ STATUS_INTERVAL = 0.3 # 发送状态间隔
10
+ SEND_STATUS_UNTIL = 10 # 持续的告之对面状态,直到没有流量往来,持续多少秒
11
+ BREAK_SEND_MISS = 10_000 # miss包个数上限,达到上限忽略要后面的段,可控碎片缓存
12
+ PEER_ADDR = 1
13
+ HEARTBEAT = 2
14
+ A_NEW_APP = 3
15
+ PAIRED = 4
16
+ SHADOW_STATUS = 5
17
+ APP_STATUS = 6
18
+ MISS = 7
19
+ FIN1 = 8
20
+ GOT_FIN1 = 9
21
+ FIN2 = 10
22
+ GOT_FIN2 = 11
23
+ P1_FIN = 12
24
+ P2_FIN = 13
25
+ CTL_CLOSE = 1
26
+ CTL_RESUME = 2
27
+ end
data/lib/p2p2/p1.rb CHANGED
@@ -1,952 +1,952 @@
1
- require 'p2p2/head'
2
- require 'p2p2/hex'
3
- require 'p2p2/version'
4
- require 'socket'
5
-
6
- ##
7
- # P2p2::P1 - 内网里的任意应用,访问另一个内网里的应用服务端。p1端。
8
- #
9
- # 两套关闭
10
- # ========
11
- #
12
- # 1-1. shadow.close -> ext.is_app_closed ? no -> send fin1 loop
13
- # 1-2. recv got_fin1 -> break loop
14
- # 1-3. recv fin2 -> send got_fin2 -> del ext
15
- #
16
- # 2-1. recv fin1 -> send got_fin1 -> ext.is_app_closed = true
17
- # 2-2. all sent && ext.biggest_app_pack_id == ext.continue_app_pack_id -> add closing shadow
18
- # 2-3. shadow.close -> ext.is_app_closed ? yes -> del ext -> loop send fin2
19
- # 2-4. recv got_fin2 -> break loop
20
- #
21
- module P2p2
22
- class P1
23
-
24
- ##
25
- # p2pd_host 配对服务器ip
26
- # p2pd_port 配对服务器端口
27
- # appd_host 任意的一个应用的ip
28
- # appd_port 应用端口
29
- # title 约定的房间名
30
- # shadow_chunk_dir 文件缓存目录,缓存shadow来不及写的流量
31
- # p1_chunk_dir 文件缓存目录,缓存p1来不及写的流量
32
- #
33
- def initialize( p2pd_host, p2pd_port, appd_host, appd_port, title, shadow_chunk_dir = '/tmp', p1_chunk_dir = '/tmp' )
34
- @p2pd_sockaddr = Socket.sockaddr_in( p2pd_port, p2pd_host )
35
- @appd_sockaddr = Socket.sockaddr_in( appd_port, appd_host )
36
- @title = title
37
- @shadow_chunk_dir = shadow_chunk_dir
38
- @p1_chunk_dir = p1_chunk_dir
39
- @hex = P2p2::Hex.new
40
- @mutex = Mutex.new
41
- @reads = []
42
- @writes = []
43
- @closings = []
44
- @roles = {} # sock => :ctlr / :shadow / :p1
45
- @infos = {} # sock => {}
46
- @socks = {} # sock => sock_id
47
- @sock_ids = {} # sock_id => sock
48
-
49
- ctlr, ctlw = IO.pipe
50
- @ctlw = ctlw
51
- @roles[ ctlr ] = :ctlr
52
- @reads << ctlr
53
- end
54
-
55
- def looping
56
- puts 'looping'
57
-
58
- new_p1
59
-
60
- loop do
61
- rs, ws = IO.select( @reads, @writes )
62
-
63
- @mutex.synchronize do
64
- rs.each do | sock |
65
- case @roles[ sock ]
66
- when :ctlr
67
- read_ctlr( sock )
68
- when :shadow
69
- read_shadow( sock )
70
- when :p1
71
- read_p1( sock )
72
- end
73
- end
74
-
75
- ws.each do | sock |
76
- case @roles[ sock ]
77
- when :shadow
78
- write_shadow( sock )
79
- when :p1
80
- write_p1( sock )
81
- end
82
- end
83
- end
84
- end
85
- rescue Interrupt => e
86
- puts e.class
87
- quit!
88
- end
89
-
90
- def quit!
91
- if @p1 && !@p1.closed? && @p1_info[ :p2_addr ]
92
- ctlmsg = [ 0, P1_FIN ].pack( 'Q>C' )
93
- send_pack( @p1, ctlmsg, @p1_info[ :p2_addr ] )
94
- end
95
-
96
- exit
97
- end
98
-
99
- private
100
-
101
- ##
102
- # read ctlr
103
- #
104
- def read_ctlr( ctlr )
105
- case ctlr.read( 1 ).unpack( 'C' ).first
106
- when CTL_CLOSE
107
- sock_id = ctlr.read( 8 ).unpack( 'Q>' ).first
108
- sock = @sock_ids[ sock_id ]
109
-
110
- if sock
111
- add_closing( sock )
112
- end
113
- when CTL_RESUME
114
- sock_id = ctlr.read( 8 ).unpack( 'Q>' ).first
115
- sock = @sock_ids[ sock_id ]
116
-
117
- if sock
118
- add_write( sock )
119
- end
120
- end
121
- end
122
-
123
- ##
124
- # read shadow
125
- #
126
- def read_shadow( shadow )
127
- begin
128
- data = shadow.read_nonblock( PACK_SIZE )
129
- rescue IO::WaitReadable, Errno::EINTR
130
- return
131
- rescue Exception => e
132
- add_closing( shadow )
133
- return
134
- end
135
-
136
- info = @infos[ shadow ]
137
- p1 = info[ :p1 ]
138
-
139
- if p1.closed?
140
- add_closing( shadow )
141
- return
142
- end
143
-
144
- p1_info = @infos[ p1 ]
145
- shadow_id = @socks[ shadow ]
146
- p1_info[ :wbuffs ] << [ shadow_id, data ]
147
-
148
- if p1_info[ :wbuffs ].size >= WBUFFS_LIMIT
149
- p1_id = @socks[ p1 ]
150
- spring = p1_info[ :chunks ].size > 0 ? ( p1_info[ :spring ] + 1 ) : 0
151
- filename = "#{ p1_id }.#{ spring }"
152
- chunk_path = File.join( @p1_chunk_dir, filename )
153
- IO.binwrite( chunk_path, p1_info[ :wbuffs ].map{ | shadow_id, data | "#{ [ shadow_id, data.bytesize ].pack( 'Q>n' ) }#{ data }" }.join )
154
- p1_info[ :chunks ] << filename
155
- p1_info[ :spring ] = spring
156
- p1_info[ :wbuffs ].clear
157
- end
158
-
159
- unless p1_info[ :paused ]
160
- add_write( p1 )
161
- end
162
- end
163
-
164
- ##
165
- # read p1
166
- #
167
- def read_p1( p1 )
168
- data, addrinfo, rflags, *controls = p1.recvmsg
169
- sockaddr = addrinfo.to_sockaddr
170
- now = Time.new
171
- info = @infos[ p1 ]
172
- app_id = data[ 0, 8 ].unpack( 'Q>' ).first
173
-
174
- if app_id == 0
175
- case data[ 8 ].unpack( 'C' ).first
176
- when PEER_ADDR
177
- return if info[ :peer_addr ] || ( sockaddr != @p2pd_sockaddr )
178
-
179
- peer_addr = data[ 9..-1 ]
180
- info[ :peer_addr ] = data[ 9..-1 ]
181
- # puts "debug peer addr #{ Addrinfo.new( info[ :peer_addr ] ).ip_unpack.inspect } #{ Time.new }"
182
- loop_punch_peer( p1 )
183
- when HEARTBEAT
184
- return if info[ :p2_addr ] || ( sockaddr != info[ :peer_addr ] )
185
-
186
- info[ :p2_addr ] = sockaddr
187
- # puts "debug p2 addr #{ Addrinfo.new( info[ :p2_addr ] ).ip_unpack.inspect } #{ Time.new }"
188
- info[ :last_traffic_at ] = now
189
- loop_send_heartbeat( p1 )
190
- loop_check_expire( p1 )
191
- loop_send_status( p1 )
192
- when A_NEW_APP
193
- return if sockaddr != info[ :p2_addr ]
194
-
195
- app_id = data[ 9, 8 ].unpack( 'Q>' ).first
196
- shadow_id = info[ :app_ids ][ app_id ]
197
-
198
- unless shadow_id
199
- shadow = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
200
- shadow.setsockopt( Socket::SOL_TCP, Socket::TCP_NODELAY, 1 )
201
-
202
- begin
203
- shadow.connect_nonblock( @appd_sockaddr )
204
- rescue IO::WaitWritable, Errno::EINTR
205
- end
206
-
207
- shadow_id = @hex.gen_random_num
208
- @roles[ shadow ] = :shadow
209
- @infos[ shadow ] = {
210
- id: shadow_id,
211
- p1: p1
212
- }
213
- @socks[ shadow ] = shadow_id
214
- @sock_ids[ shadow_id ] = shadow
215
-
216
- info[ :shadow_exts ][ shadow_id ] = {
217
- shadow: shadow,
218
- created_at: now,
219
- last_recv_at: nil, # 上一次收到流量的时间
220
- wbuff: '', # 写前缓存
221
- cache: '', # 块读出缓存
222
- chunks: [], # 块队列,写前达到块大小时结一个块 filename
223
- spring: 0, # 块后缀,结块时,如果块队列不为空,则自增,为空,则置为0
224
- wmems: {}, # 写后缓存 pack_id => data
225
- send_ats: {}, # 上一次发出时间 pack_id => send_at
226
- biggest_pack_id: 0, # 发到几
227
- continue_app_pack_id: 0, # 收到几
228
- pieces: {}, # 跳号包 app_pack_id => data
229
- is_app_closed: false, # 对面是否已关闭
230
- biggest_app_pack_id: 0, # 对面发到几
231
- completed_pack_id: 0, # 完成到几(对面收到几)
232
- last_traffic_at: nil # 收到有效流量,或者发出流量的时间戳
233
- }
234
-
235
- info[ :shadow_ids ][ shadow_id ] = app_id
236
- info[ :app_ids ][ app_id ] = shadow_id
237
- add_read( shadow )
238
- end
239
-
240
- ctlmsg = [
241
- 0,
242
- PAIRED,
243
- app_id,
244
- shadow_id
245
- ].pack( 'Q>CQ>Q>' )
246
-
247
- # puts "debug send PAIRED #{ app_id } #{ shadow_id } #{ Time.new }"
248
- send_pack( p1, ctlmsg, info[ :p2_addr ] )
249
- when APP_STATUS
250
- return if sockaddr != info[ :p2_addr ]
251
-
252
- app_id, biggest_app_pack_id, continue_shadow_pack_id = data[ 9, 24 ].unpack( 'Q>Q>Q>' )
253
- shadow_id = info[ :app_ids ][ app_id ]
254
- return unless shadow_id
255
-
256
- ext = info[ :shadow_exts ][ shadow_id ]
257
- return unless ext
258
-
259
- # 更新对面发到几
260
- if biggest_app_pack_id > ext[ :biggest_app_pack_id ]
261
- ext[ :biggest_app_pack_id ] = biggest_app_pack_id
262
- end
263
-
264
- # 更新对面收到几,释放写后
265
- if continue_shadow_pack_id > ext[ :completed_pack_id ]
266
- pack_ids = ext[ :wmems ].keys.select { | pack_id | pack_id <= continue_shadow_pack_id }
267
-
268
- pack_ids.each do | pack_id |
269
- ext[ :wmems ].delete( pack_id )
270
- ext[ :send_ats ].delete( pack_id )
271
- end
272
-
273
- ext[ :completed_pack_id ] = continue_shadow_pack_id
274
- end
275
-
276
- if ext[ :is_app_closed ] && ( ext[ :biggest_app_pack_id ] == ext[ :continue_app_pack_id ] )
277
- add_write( ext[ :shadow ] )
278
- return
279
- end
280
-
281
- # 发miss
282
- if !ext[ :shadow ].closed? && ( ext[ :continue_app_pack_id ] < ext[ :biggest_app_pack_id ] )
283
- ranges = []
284
- curr_pack_id = ext[ :continue_app_pack_id ] + 1
285
-
286
- ext[ :pieces ].keys.sort.each do | pack_id |
287
- if pack_id > curr_pack_id
288
- ranges << [ curr_pack_id, pack_id - 1 ]
289
- end
290
-
291
- curr_pack_id = pack_id + 1
292
- end
293
-
294
- if curr_pack_id <= ext[ :biggest_app_pack_id ]
295
- ranges << [ curr_pack_id, ext[ :biggest_app_pack_id ] ]
296
- end
297
-
298
- pack_count = 0
299
- # puts "debug #{ ext[ :continue_app_pack_id ] }/#{ ext[ :biggest_app_pack_id ] } send MISS #{ ranges.size }"
300
- ranges.each do | pack_id_begin, pack_id_end |
301
- if pack_count >= BREAK_SEND_MISS
302
- puts "break send miss at #{ pack_id_begin } #{ Time.new }"
303
- break
304
- end
305
-
306
- ctlmsg = [
307
- 0,
308
- MISS,
309
- app_id,
310
- pack_id_begin,
311
- pack_id_end
312
- ].pack( 'Q>CQ>Q>Q>' )
313
-
314
- send_pack( p1, ctlmsg, info[ :p2_addr ] )
315
- pack_count += ( pack_id_end - pack_id_begin + 1 )
316
- end
317
- end
318
- when MISS
319
- return if sockaddr != info[ :p2_addr ]
320
-
321
- shadow_id, pack_id_begin, pack_id_end = data[ 9, 24 ].unpack( 'Q>Q>Q>' )
322
- ext = info[ :shadow_exts ][ shadow_id ]
323
- return unless ext
324
-
325
- ( pack_id_begin..pack_id_end ).each do | pack_id |
326
- send_at = ext[ :send_ats ][ pack_id ]
327
-
328
- if send_at
329
- break if now - send_at < STATUS_INTERVAL
330
-
331
- info[ :resendings ] << [ shadow_id, pack_id ]
332
- end
333
- end
334
-
335
- add_write( p1 )
336
- when FIN1
337
- return if sockaddr != info[ :p2_addr ]
338
-
339
- app_id = data[ 9, 8 ].unpack( 'Q>' ).first
340
- ctlmsg = [
341
- 0,
342
- GOT_FIN1,
343
- app_id
344
- ].pack( 'Q>CQ>' )
345
-
346
- # puts "debug 2-1. recv fin1 -> send got_fin1 -> ext.is_app_closed = true #{ app_id } #{ Time.new }"
347
- send_pack( p1, ctlmsg, info[ :p2_addr ] )
348
-
349
- shadow_id = info[ :app_ids ][ app_id ]
350
- return unless shadow_id
351
-
352
- ext = info[ :shadow_exts ][ shadow_id ]
353
- return unless ext
354
-
355
- ext[ :is_app_closed ] = true
356
- when GOT_FIN1
357
- return if sockaddr != info[ :p2_addr ]
358
-
359
- # puts "debug 1-2. recv got_fin1 -> break loop #{ Time.new }"
360
- shadow_id = data[ 9, 8 ].unpack( 'Q>' ).first
361
- info[ :fin1s ].delete( shadow_id )
362
- when FIN2
363
- return if sockaddr != info[ :p2_addr ]
364
-
365
- # puts "debug 1-3. recv fin2 -> send got_fin2 -> del ext #{ Time.new }"
366
- app_id = data[ 9, 8 ].unpack( 'Q>' ).first
367
- ctlmsg = [
368
- 0,
369
- GOT_FIN2,
370
- app_id
371
- ].pack( 'Q>CQ>' )
372
-
373
- send_pack( p1, ctlmsg, info[ :p2_addr ] )
374
-
375
- shadow_id = info[ :app_ids ][ app_id ]
376
- return unless shadow_id
377
-
378
- del_shadow_ext( info, shadow_id )
379
- when GOT_FIN2
380
- return if sockaddr != info[ :p2_addr ]
381
-
382
- # puts "debug 2-4. recv got_fin2 -> break loop #{ Time.new }"
383
- shadow_id = data[ 9, 8 ].unpack( 'Q>' ).first
384
- info[ :fin2s ].delete( shadow_id )
385
- when P2_FIN
386
- return if sockaddr != info[ :p2_addr ]
387
-
388
- puts "recv p2 fin #{ Time.new }"
389
- add_closing( p1 )
390
- end
391
-
392
- return
393
- end
394
-
395
- return if sockaddr != info[ :p2_addr ]
396
-
397
- shadow_id = info[ :app_ids ][ app_id ]
398
- return unless shadow_id
399
-
400
- ext = info[ :shadow_exts ][ shadow_id ]
401
- return if ext.nil? || ext[ :shadow ].closed?
402
-
403
- pack_id = data[ 8, 8 ].unpack( 'Q>' ).first
404
- return if ( pack_id <= ext[ :continue_app_pack_id ] ) || ext[ :pieces ].include?( pack_id )
405
-
406
- data = data[ 16..-1 ]
407
-
408
- # 解混淆
409
- if pack_id == 1
410
- data = @hex.decode( data )
411
- end
412
-
413
- # 放进shadow的写前缓存,跳号放碎片缓存
414
- if pack_id - ext[ :continue_app_pack_id ] == 1
415
- while ext[ :pieces ].include?( pack_id + 1 )
416
- data << ext[ :pieces ].delete( pack_id + 1 )
417
- pack_id += 1
418
- end
419
-
420
- ext[ :continue_app_pack_id ] = pack_id
421
- ext[ :wbuff ] << data
422
-
423
- if ext[ :wbuff ].bytesize >= CHUNK_SIZE
424
- spring = ext[ :chunks ].size > 0 ? ( ext[ :spring ] + 1 ) : 0
425
- filename = "#{ shadow_id }.#{ spring }"
426
- chunk_path = File.join( @shadow_chunk_dir, filename )
427
- IO.binwrite( chunk_path, ext[ :wbuff ] )
428
- ext[ :chunks ] << filename
429
- ext[ :spring ] = spring
430
- ext[ :wbuff ].clear
431
- end
432
-
433
- add_write( ext[ :shadow ] )
434
- ext[ :last_traffic_at ] = now
435
- info[ :last_traffic_at ] = now
436
- else
437
- ext[ :pieces ][ pack_id ] = data
438
- end
439
-
440
- ext[ :last_recv_at ] = now
441
- end
442
-
443
- ##
444
- # write shadow
445
- #
446
- def write_shadow( shadow )
447
- if @closings.include?( shadow )
448
- close_shadow( shadow )
449
- return
450
- end
451
-
452
- info = @infos[ shadow ]
453
- p1 = info[ :p1 ]
454
-
455
- if p1.closed?
456
- add_closing( shadow )
457
- return
458
- end
459
-
460
- p1_info = @infos[ p1 ]
461
- shadow_id = @socks[ shadow ]
462
- ext = p1_info[ :shadow_exts ][ shadow_id ]
463
-
464
- # 取写前
465
- data = ext[ :cache ]
466
- from = :cache
467
-
468
- if data.empty?
469
- if ext[ :chunks ].any?
470
- path = File.join( @shadow_chunk_dir, ext[ :chunks ].shift )
471
-
472
- begin
473
- data = IO.binread( path )
474
- File.delete( path )
475
- rescue Errno::ENOENT
476
- add_closing( shadow )
477
- return
478
- end
479
- else
480
- data = ext[ :wbuff ]
481
- from = :wbuff
482
- end
483
- end
484
-
485
- if data.empty?
486
- if ext[ :is_app_closed ] && ( ext[ :biggest_app_pack_id ] == ext[ :continue_app_pack_id ] )
487
- # puts "debug 2-2. all sent && ext.biggest_app_pack_id == ext.continue_app_pack_id -> add closing shadow #{ Time.new }"
488
- add_closing( shadow )
489
- return
490
- end
491
-
492
- @writes.delete( shadow )
493
- return
494
- end
495
-
496
- begin
497
- written = shadow.write_nonblock( data )
498
- rescue IO::WaitWritable, Errno::EINTR => e
499
- ext[ from ] = data
500
- return
501
- rescue Exception => e
502
- add_closing( shadow )
503
- return
504
- end
505
-
506
- data = data[ written..-1 ]
507
- ext[ from ] = data
508
- end
509
-
510
- ##
511
- # write p1
512
- #
513
- def write_p1( p1 )
514
- if @closings.include?( p1 )
515
- close_p1( p1 )
516
- new_p1
517
- return
518
- end
519
-
520
- now = Time.new
521
- info = @infos[ p1 ]
522
-
523
- # 重传
524
- while info[ :resendings ].any?
525
- shadow_id, pack_id = info[ :resendings ].shift
526
- ext = info[ :shadow_exts ][ shadow_id ]
527
-
528
- if ext
529
- pack = ext[ :wmems ][ pack_id ]
530
-
531
- if pack
532
- send_pack( p1, pack, info[ :p2_addr ] )
533
- ext[ :last_traffic_at ] = now
534
- info[ :last_traffic_at ] = now
535
- return
536
- end
537
- end
538
- end
539
-
540
- # 若写后达到上限,暂停取写前
541
- if info[ :shadow_exts ].map{ | _, ext | ext[ :wmems ].size }.sum >= WMEMS_LIMIT
542
- unless info[ :paused ]
543
- puts "pause #{ Time.new }"
544
- info[ :paused ] = true
545
- end
546
-
547
- @writes.delete( p1 )
548
- return
549
- end
550
-
551
- # 取写前
552
- if info[ :caches ].any?
553
- shadow_id, data = info[ :caches ].shift
554
- elsif info[ :chunks ].any?
555
- path = File.join( @p1_chunk_dir, info[ :chunks ].shift )
556
-
557
- begin
558
- data = IO.binread( path )
559
- File.delete( path )
560
- rescue Errno::ENOENT
561
- add_closing( p1 )
562
- return
563
- end
564
-
565
- caches = []
566
-
567
- until data.empty?
568
- shadow_id, pack_size = data[ 0, 10 ].unpack( 'Q>n' )
569
- caches << [ shadow_id, data[ 10, pack_size ] ]
570
- data = data[ ( 10 + pack_size )..-1 ]
571
- end
572
-
573
- shadow_id, data = caches.shift
574
- info[ :caches ] = caches
575
- elsif info[ :wbuffs ].any?
576
- shadow_id, data = info[ :wbuffs ].shift
577
- else
578
- @writes.delete( p1 )
579
- return
580
- end
581
-
582
- ext = info[ :shadow_exts ][ shadow_id ]
583
-
584
- if ext
585
- pack_id = ext[ :biggest_pack_id ] + 1
586
-
587
- if pack_id == 1
588
- data = @hex.encode( data )
589
- end
590
-
591
- pack = "#{ [ shadow_id, pack_id ].pack( 'Q>Q>' ) }#{ data }"
592
- send_pack( p1, pack, info[ :p2_addr ] )
593
- ext[ :biggest_pack_id ] = pack_id
594
- ext[ :wmems ][ pack_id ] = pack
595
- ext[ :send_ats ][ pack_id ] = now
596
- ext[ :last_traffic_at ] = now
597
- info[ :last_traffic_at ] = now
598
- end
599
- end
600
-
601
- def new_p1
602
- p1 = Socket.new( Socket::AF_INET, Socket::SOCK_DGRAM, 0 )
603
- p1.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1 )
604
- p1.bind( Socket.sockaddr_in( 0, '0.0.0.0' ) )
605
-
606
- p1_id = @hex.gen_random_num
607
- p1_info = {
608
- id: p1_id,
609
- wbuffs: [], # 写前缓存 [ shadow_id, data ]
610
- caches: [], # 块读出缓存 [ shadow_id, data ]
611
- chunks: [], # 块队列 filename
612
- spring: 0, # 块后缀,结块时,如果块队列不为空,则自增,为空,则置为0
613
- peer_addr: nil, # 对面地址,连发心跳包打洞,离p2pd近的一方通常会撞死几个直到对面出来
614
- p2_addr: nil, # p2地址
615
- shadow_exts: {}, # 长命信息 shadow_id => {}
616
- shadow_ids: {}, # shadow_id => app_id
617
- app_ids: {}, # app_id => shadow_id
618
- fin1s: [], # fin1: shadow已关闭,等待对面收完流量 shadow_id
619
- fin2s: [], # fin2: 流量已收完 shadow_id
620
- paused: false, # 是否暂停写
621
- resendings: [], # 重传队列 [ shadow_id, pack_id ]
622
- last_traffic_at: nil # 收到有效流量,或者发出流量的时间戳
623
- }
624
-
625
- @p1 = p1
626
- @p1_info = p1_info
627
- @roles[ p1 ] = :p1
628
- @infos[ p1 ] = p1_info
629
- @socks[ p1 ] = p1_id
630
- @sock_ids[ p1_id ] = p1
631
-
632
- send_pack( p1, @title, @p2pd_sockaddr )
633
- add_read( p1 )
634
- loop_send_char( p1 )
635
- end
636
-
637
- def loop_send_char( p1 )
638
- Thread.new do
639
- is_timeout = true
640
-
641
- 20.times do
642
- sleep HEARTBEAT_INTERVAL
643
-
644
- if p1.closed?
645
- is_timeout = false
646
- break
647
- end
648
-
649
- p1_info = @infos[ p1 ]
650
-
651
- if p1_info[ :peer_addr ]
652
- is_timeout = false
653
- break
654
- end
655
-
656
- @mutex.synchronize do
657
- send_pack( p1, [ rand( 128 ) ].pack( 'C' ), @p2pd_sockaddr )
658
- end
659
- end
660
-
661
- if is_timeout
662
- @mutex.synchronize do
663
- p1_id = @socks[ p1 ]
664
- @ctlw.write( [ CTL_CLOSE, p1_id ].pack( 'CQ>' ) )
665
- end
666
- end
667
- end
668
- end
669
-
670
- def loop_punch_peer( p1 )
671
- Thread.new do
672
- 20.times do
673
- break if p1.closed?
674
-
675
- p1_info = @infos[ p1 ]
676
-
677
- @mutex.synchronize do
678
- send_heartbeat( p1, p1_info[ :peer_addr ] )
679
- end
680
-
681
- sleep STATUS_INTERVAL
682
- end
683
-
684
- if !p1.closed?
685
- p1_info = @infos[ p1 ]
686
-
687
- unless p1_info[ :p2_addr ]
688
- @mutex.synchronize do
689
- p1_id = @socks[ p1 ]
690
- @ctlw.write( [ CTL_CLOSE, p1_id ].pack( 'CQ>' ) )
691
- end
692
- end
693
- end
694
- end
695
- end
696
-
697
- def loop_send_heartbeat( p1 )
698
- Thread.new do
699
- loop do
700
- sleep HEARTBEAT_INTERVAL
701
- break if p1.closed?
702
-
703
- @mutex.synchronize do
704
- p1_info = @infos[ p1 ]
705
- send_heartbeat( p1, p1_info[ :p2_addr ] )
706
- end
707
- end
708
- end
709
- end
710
-
711
- def loop_check_expire( p1 )
712
- Thread.new do
713
- loop do
714
- sleep 60
715
- break if p1.closed?
716
-
717
- now = Time.new
718
- p1_info = @infos[ p1 ]
719
-
720
- if now - p1_info[ :last_traffic_at ] > EXPIRE_AFTER
721
- @mutex.synchronize do
722
- p1_id = @socks[ p1 ]
723
- # puts "debug ctlw close p1 #{ p1_id } #{ Time.new } p#{ Process.pid }"
724
- @ctlw.write( [ CTL_CLOSE, p1_id ].pack( 'CQ>' ) )
725
- end
726
-
727
- break
728
- end
729
-
730
- exts = p1_info[ :shadow_exts ].select{ | _, ext | now - ext[ :created_at ] > 5 }
731
-
732
- if exts.any?
733
- @mutex.synchronize do
734
- exts.each do | shadow_id, ext |
735
- if ext[ :last_recv_at ].nil? || ( now - ext[ :last_recv_at ] > EXPIRE_AFTER )
736
- # puts "debug ctlw close shadow #{ shadow_id } #{ Time.new } p#{ Process.pid }"
737
- @ctlw.write( [ CTL_CLOSE, shadow_id ].pack( 'CQ>' ) )
738
- end
739
- end
740
- end
741
- end
742
- end
743
- end
744
- end
745
-
746
- def loop_send_status( p1 )
747
- Thread.new do
748
- loop do
749
- sleep STATUS_INTERVAL
750
-
751
- if p1.closed?
752
- # puts "debug p1 is closed, break send status loop #{ Time.new }"
753
- break
754
- end
755
-
756
- p1_info = @infos[ p1 ]
757
-
758
- if p1_info[ :shadow_exts ].any?
759
- @mutex.synchronize do
760
- now = Time.new
761
-
762
- p1_info[ :shadow_exts ].each do | shadow_id, ext |
763
- if ext[ :last_traffic_at ] && ( now - ext[ :last_traffic_at ] < SEND_STATUS_UNTIL )
764
- ctlmsg = [
765
- 0,
766
- SHADOW_STATUS,
767
- shadow_id,
768
- ext[ :biggest_pack_id ],
769
- ext[ :continue_app_pack_id ]
770
- ].pack( 'Q>CQ>Q>Q>' )
771
-
772
- send_pack( p1, ctlmsg, p1_info[ :p2_addr ] )
773
- end
774
- end
775
- end
776
- end
777
-
778
- if p1_info[ :paused ] && ( p1_info[ :shadow_exts ].map{ | _, ext | ext[ :wmems ].size }.sum < RESUME_BELOW )
779
- @mutex.synchronize do
780
- p1_id = @socks[ p1 ]
781
- puts "ctlw resume #{ p1_id } #{ Time.new }"
782
- @ctlw.write( [ CTL_RESUME, p1_id ].pack( 'CQ>' ) )
783
- p1_info[ :paused ] = false
784
- end
785
- end
786
- end
787
- end
788
- end
789
-
790
- def loop_send_fin1( p1, shadow_id )
791
- Thread.new do
792
- 100.times do
793
- break if p1.closed?
794
-
795
- p1_info = @infos[ p1 ]
796
- break unless p1_info[ :p2_addr ]
797
-
798
- unless p1_info[ :fin1s ].include?( shadow_id )
799
- # puts "debug break send fin1 loop #{ Time.new }"
800
- break
801
- end
802
-
803
- @mutex.synchronize do
804
- ctlmsg = [
805
- 0,
806
- FIN1,
807
- shadow_id
808
- ].pack( 'Q>CQ>' )
809
-
810
- # puts "debug send FIN1 #{ shadow_id } #{ Time.new }"
811
- send_pack( p1, ctlmsg, p1_info[ :p2_addr ] )
812
- end
813
-
814
- sleep 1
815
- end
816
- end
817
- end
818
-
819
- def loop_send_fin2( p1, shadow_id )
820
- Thread.new do
821
- 100.times do
822
- break if p1.closed?
823
-
824
- p1_info = @infos[ p1 ]
825
- break unless p1_info[ :p2_addr ]
826
-
827
- unless p1_info[ :fin2s ].include?( shadow_id )
828
- # puts "debug break send fin2 loop #{ Time.new }"
829
- break
830
- end
831
-
832
- @mutex.synchronize do
833
- ctlmsg = [
834
- 0,
835
- FIN2,
836
- shadow_id
837
- ].pack( 'Q>CQ>' )
838
-
839
- # puts "debug send FIN2 #{ shadow_id } #{ Time.new }"
840
- send_pack( p1, ctlmsg, p1_info[ :p2_addr ] )
841
- end
842
-
843
- sleep 1
844
- end
845
- end
846
- end
847
-
848
- def send_heartbeat( p1, target_addr )
849
- ctlmsg = [ 0, HEARTBEAT, rand( 128 ) ].pack( 'Q>CC' )
850
- send_pack( p1, ctlmsg, target_addr )
851
- end
852
-
853
- def send_pack( sock, data, target_sockaddr )
854
- begin
855
- sock.sendmsg( data, 0, target_sockaddr )
856
- rescue IO::WaitWritable, Errno::EINTR => e
857
- puts "sendmsg #{ e.class } #{ Time.new }"
858
- end
859
- end
860
-
861
- def add_read( sock )
862
- return if sock.closed? || @reads.include?( sock )
863
-
864
- @reads << sock
865
- end
866
-
867
- def add_write( sock, data = nil )
868
- return if sock.closed? || @writes.include?( sock )
869
-
870
- @writes << sock
871
- end
872
-
873
- def add_closing( sock )
874
- return if sock.closed? || @closings.include?( sock )
875
-
876
- @reads.delete( sock )
877
- @closings << sock
878
- add_write( sock )
879
- end
880
-
881
- def close_p1( p1 )
882
- info = close_sock( p1 )
883
-
884
- info[ :chunks ].each do | filename |
885
- begin
886
- File.delete( File.join( @p1_chunk_dir, filename ) )
887
- rescue Errno::ENOENT
888
- end
889
- end
890
-
891
- info[ :shadow_exts ].each{ | _, ext | add_closing( ext[ :shadow ] ) }
892
- end
893
-
894
- def close_shadow( shadow )
895
- info = close_sock( shadow )
896
- p1 = info[ :p1 ]
897
- return if p1.closed?
898
-
899
- shadow_id = info[ :id ]
900
- p1_info = @infos[ p1 ]
901
- ext = p1_info[ :shadow_exts ][ shadow_id ]
902
- return unless ext
903
-
904
- if ext[ :is_app_closed ]
905
- del_shadow_ext( p1_info, shadow_id )
906
-
907
- unless p1_info[ :fin2s ].include?( shadow_id )
908
- # puts "debug 2-3. shadow.close -> ext.is_app_closed ? yes -> del ext -> loop send fin2 #{ Time.new }"
909
- p1_info[ :fin2s ] << shadow_id
910
- loop_send_fin2( p1, shadow_id )
911
- end
912
- elsif !p1_info[ :fin1s ].include?( shadow_id )
913
- # puts "debug 1-1. shadow.close -> ext.is_app_closed ? no -> send fin1 loop #{ Time.new }"
914
- p1_info[ :fin1s ] << shadow_id
915
- loop_send_fin1( p1, shadow_id )
916
- end
917
- end
918
-
919
- def close_sock( sock )
920
- sock.close
921
- @reads.delete( sock )
922
- @writes.delete( sock )
923
- @closings.delete( sock )
924
- @roles.delete( sock )
925
- info = @infos.delete( sock )
926
- sock_id = @socks.delete( sock )
927
- @sock_ids.delete( sock_id )
928
-
929
- info
930
- end
931
-
932
- def del_shadow_ext( p1_info, shadow_id )
933
- ext = p1_info[ :shadow_exts ].delete( shadow_id )
934
-
935
- if ext
936
- ext[ :chunks ].each do | filename |
937
- begin
938
- File.delete( File.join( @shadow_chunk_dir, filename ) )
939
- rescue Errno::ENOENT
940
- end
941
- end
942
- end
943
-
944
- app_id = p1_info[ :shadow_ids ].delete( shadow_id )
945
-
946
- if app_id
947
- p1_info[ :app_ids ].delete( app_id )
948
- end
949
- end
950
-
951
- end
952
- end
1
+ require 'p2p2/head'
2
+ require 'p2p2/hex'
3
+ require 'p2p2/version'
4
+ require 'socket'
5
+
6
+ ##
7
+ # P2p2::P1 - 内网里的任意应用,访问另一个内网里的应用服务端。p1端。
8
+ #
9
+ # 两套关闭
10
+ # ========
11
+ #
12
+ # 1-1. shadow.close -> ext.is_app_closed ? no -> send fin1 loop
13
+ # 1-2. recv got_fin1 -> break loop
14
+ # 1-3. recv fin2 -> send got_fin2 -> del ext
15
+ #
16
+ # 2-1. recv fin1 -> send got_fin1 -> ext.is_app_closed = true
17
+ # 2-2. all sent && ext.biggest_app_pack_id == ext.continue_app_pack_id -> add closing shadow
18
+ # 2-3. shadow.close -> ext.is_app_closed ? yes -> del ext -> loop send fin2
19
+ # 2-4. recv got_fin2 -> break loop
20
+ #
21
+ module P2p2
22
+ class P1
23
+
24
+ ##
25
+ # p2pd_host 配对服务器ip
26
+ # p2pd_port 配对服务器端口
27
+ # appd_host 任意的一个应用的ip
28
+ # appd_port 应用端口
29
+ # title 约定的房间名
30
+ # shadow_chunk_dir 文件缓存目录,缓存shadow来不及写的流量
31
+ # p1_chunk_dir 文件缓存目录,缓存p1来不及写的流量
32
+ #
33
+ def initialize( p2pd_host, p2pd_port, appd_host, appd_port, title, shadow_chunk_dir = '/tmp', p1_chunk_dir = '/tmp' )
34
+ @p2pd_sockaddr = Socket.sockaddr_in( p2pd_port, p2pd_host )
35
+ @appd_sockaddr = Socket.sockaddr_in( appd_port, appd_host )
36
+ @title = title
37
+ @shadow_chunk_dir = shadow_chunk_dir
38
+ @p1_chunk_dir = p1_chunk_dir
39
+ @hex = P2p2::Hex.new
40
+ @mutex = Mutex.new
41
+ @reads = []
42
+ @writes = []
43
+ @closings = []
44
+ @roles = {} # sock => :ctlr / :shadow / :p1
45
+ @infos = {} # sock => {}
46
+ @socks = {} # sock => sock_id
47
+ @sock_ids = {} # sock_id => sock
48
+
49
+ ctlr, ctlw = IO.pipe
50
+ @ctlw = ctlw
51
+ @roles[ ctlr ] = :ctlr
52
+ @reads << ctlr
53
+ end
54
+
55
+ def looping
56
+ puts 'looping'
57
+
58
+ new_p1
59
+
60
+ loop do
61
+ rs, ws = IO.select( @reads, @writes )
62
+
63
+ @mutex.synchronize do
64
+ rs.each do | sock |
65
+ case @roles[ sock ]
66
+ when :ctlr
67
+ read_ctlr( sock )
68
+ when :shadow
69
+ read_shadow( sock )
70
+ when :p1
71
+ read_p1( sock )
72
+ end
73
+ end
74
+
75
+ ws.each do | sock |
76
+ case @roles[ sock ]
77
+ when :shadow
78
+ write_shadow( sock )
79
+ when :p1
80
+ write_p1( sock )
81
+ end
82
+ end
83
+ end
84
+ end
85
+ rescue Interrupt => e
86
+ puts e.class
87
+ quit!
88
+ end
89
+
90
+ def quit!
91
+ if @p1 && !@p1.closed? && @p1_info[ :p2_addr ]
92
+ ctlmsg = [ 0, P1_FIN ].pack( 'Q>C' )
93
+ send_pack( @p1, ctlmsg, @p1_info[ :p2_addr ] )
94
+ end
95
+
96
+ exit
97
+ end
98
+
99
+ private
100
+
101
+ ##
102
+ # read ctlr
103
+ #
104
+ def read_ctlr( ctlr )
105
+ case ctlr.read( 1 ).unpack( 'C' ).first
106
+ when CTL_CLOSE
107
+ sock_id = ctlr.read( 8 ).unpack( 'Q>' ).first
108
+ sock = @sock_ids[ sock_id ]
109
+
110
+ if sock
111
+ add_closing( sock )
112
+ end
113
+ when CTL_RESUME
114
+ sock_id = ctlr.read( 8 ).unpack( 'Q>' ).first
115
+ sock = @sock_ids[ sock_id ]
116
+
117
+ if sock
118
+ add_write( sock )
119
+ end
120
+ end
121
+ end
122
+
123
+ ##
124
+ # read shadow
125
+ #
126
+ def read_shadow( shadow )
127
+ begin
128
+ data = shadow.read_nonblock( PACK_SIZE )
129
+ rescue IO::WaitReadable, Errno::EINTR
130
+ return
131
+ rescue Exception => e
132
+ add_closing( shadow )
133
+ return
134
+ end
135
+
136
+ info = @infos[ shadow ]
137
+ p1 = info[ :p1 ]
138
+
139
+ if p1.closed?
140
+ add_closing( shadow )
141
+ return
142
+ end
143
+
144
+ p1_info = @infos[ p1 ]
145
+ shadow_id = @socks[ shadow ]
146
+ p1_info[ :wbuffs ] << [ shadow_id, data ]
147
+
148
+ if p1_info[ :wbuffs ].size >= WBUFFS_LIMIT
149
+ p1_id = @socks[ p1 ]
150
+ spring = p1_info[ :chunks ].size > 0 ? ( p1_info[ :spring ] + 1 ) : 0
151
+ filename = "#{ p1_id }.#{ spring }"
152
+ chunk_path = File.join( @p1_chunk_dir, filename )
153
+ IO.binwrite( chunk_path, p1_info[ :wbuffs ].map{ | shadow_id, data | "#{ [ shadow_id, data.bytesize ].pack( 'Q>n' ) }#{ data }" }.join )
154
+ p1_info[ :chunks ] << filename
155
+ p1_info[ :spring ] = spring
156
+ p1_info[ :wbuffs ].clear
157
+ end
158
+
159
+ unless p1_info[ :paused ]
160
+ add_write( p1 )
161
+ end
162
+ end
163
+
164
+ ##
165
+ # read p1
166
+ #
167
+ def read_p1( p1 )
168
+ data, addrinfo, rflags, *controls = p1.recvmsg
169
+ sockaddr = addrinfo.to_sockaddr
170
+ now = Time.new
171
+ info = @infos[ p1 ]
172
+ app_id = data[ 0, 8 ].unpack( 'Q>' ).first
173
+
174
+ if app_id == 0
175
+ case data[ 8 ].unpack( 'C' ).first
176
+ when PEER_ADDR
177
+ return if info[ :peer_addr ] || ( sockaddr != @p2pd_sockaddr )
178
+
179
+ peer_addr = data[ 9..-1 ]
180
+ info[ :peer_addr ] = data[ 9..-1 ]
181
+ # puts "debug peer addr #{ Addrinfo.new( info[ :peer_addr ] ).ip_unpack.inspect } #{ Time.new }"
182
+ loop_punch_peer( p1 )
183
+ when HEARTBEAT
184
+ return if info[ :p2_addr ] || ( sockaddr != info[ :peer_addr ] )
185
+
186
+ info[ :p2_addr ] = sockaddr
187
+ # puts "debug p2 addr #{ Addrinfo.new( info[ :p2_addr ] ).ip_unpack.inspect } #{ Time.new }"
188
+ info[ :last_traffic_at ] = now
189
+ loop_send_heartbeat( p1 )
190
+ loop_check_expire( p1 )
191
+ loop_send_status( p1 )
192
+ when A_NEW_APP
193
+ return if sockaddr != info[ :p2_addr ]
194
+
195
+ app_id = data[ 9, 8 ].unpack( 'Q>' ).first
196
+ shadow_id = info[ :app_ids ][ app_id ]
197
+
198
+ unless shadow_id
199
+ shadow = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
200
+ shadow.setsockopt( Socket::SOL_TCP, Socket::TCP_NODELAY, 1 )
201
+
202
+ begin
203
+ shadow.connect_nonblock( @appd_sockaddr )
204
+ rescue IO::WaitWritable, Errno::EINTR
205
+ end
206
+
207
+ shadow_id = @hex.gen_random_num
208
+ @roles[ shadow ] = :shadow
209
+ @infos[ shadow ] = {
210
+ id: shadow_id,
211
+ p1: p1
212
+ }
213
+ @socks[ shadow ] = shadow_id
214
+ @sock_ids[ shadow_id ] = shadow
215
+
216
+ info[ :shadow_exts ][ shadow_id ] = {
217
+ shadow: shadow,
218
+ created_at: now,
219
+ last_recv_at: nil, # 上一次收到流量的时间
220
+ wbuff: '', # 写前缓存
221
+ cache: '', # 块读出缓存
222
+ chunks: [], # 块队列,写前达到块大小时结一个块 filename
223
+ spring: 0, # 块后缀,结块时,如果块队列不为空,则自增,为空,则置为0
224
+ wmems: {}, # 写后缓存 pack_id => data
225
+ send_ats: {}, # 上一次发出时间 pack_id => send_at
226
+ biggest_pack_id: 0, # 发到几
227
+ continue_app_pack_id: 0, # 收到几
228
+ pieces: {}, # 跳号包 app_pack_id => data
229
+ is_app_closed: false, # 对面是否已关闭
230
+ biggest_app_pack_id: 0, # 对面发到几
231
+ completed_pack_id: 0, # 完成到几(对面收到几)
232
+ last_traffic_at: nil # 收到有效流量,或者发出流量的时间戳
233
+ }
234
+
235
+ info[ :shadow_ids ][ shadow_id ] = app_id
236
+ info[ :app_ids ][ app_id ] = shadow_id
237
+ add_read( shadow )
238
+ end
239
+
240
+ ctlmsg = [
241
+ 0,
242
+ PAIRED,
243
+ app_id,
244
+ shadow_id
245
+ ].pack( 'Q>CQ>Q>' )
246
+
247
+ # puts "debug send PAIRED #{ app_id } #{ shadow_id } #{ Time.new }"
248
+ send_pack( p1, ctlmsg, info[ :p2_addr ] )
249
+ when APP_STATUS
250
+ return if sockaddr != info[ :p2_addr ]
251
+
252
+ app_id, biggest_app_pack_id, continue_shadow_pack_id = data[ 9, 24 ].unpack( 'Q>Q>Q>' )
253
+ shadow_id = info[ :app_ids ][ app_id ]
254
+ return unless shadow_id
255
+
256
+ ext = info[ :shadow_exts ][ shadow_id ]
257
+ return unless ext
258
+
259
+ # 更新对面发到几
260
+ if biggest_app_pack_id > ext[ :biggest_app_pack_id ]
261
+ ext[ :biggest_app_pack_id ] = biggest_app_pack_id
262
+ end
263
+
264
+ # 更新对面收到几,释放写后
265
+ if continue_shadow_pack_id > ext[ :completed_pack_id ]
266
+ pack_ids = ext[ :wmems ].keys.select { | pack_id | pack_id <= continue_shadow_pack_id }
267
+
268
+ pack_ids.each do | pack_id |
269
+ ext[ :wmems ].delete( pack_id )
270
+ ext[ :send_ats ].delete( pack_id )
271
+ end
272
+
273
+ ext[ :completed_pack_id ] = continue_shadow_pack_id
274
+ end
275
+
276
+ if ext[ :is_app_closed ] && ( ext[ :biggest_app_pack_id ] == ext[ :continue_app_pack_id ] )
277
+ add_write( ext[ :shadow ] )
278
+ return
279
+ end
280
+
281
+ # 发miss
282
+ if !ext[ :shadow ].closed? && ( ext[ :continue_app_pack_id ] < ext[ :biggest_app_pack_id ] )
283
+ ranges = []
284
+ curr_pack_id = ext[ :continue_app_pack_id ] + 1
285
+
286
+ ext[ :pieces ].keys.sort.each do | pack_id |
287
+ if pack_id > curr_pack_id
288
+ ranges << [ curr_pack_id, pack_id - 1 ]
289
+ end
290
+
291
+ curr_pack_id = pack_id + 1
292
+ end
293
+
294
+ if curr_pack_id <= ext[ :biggest_app_pack_id ]
295
+ ranges << [ curr_pack_id, ext[ :biggest_app_pack_id ] ]
296
+ end
297
+
298
+ pack_count = 0
299
+ # puts "debug #{ ext[ :continue_app_pack_id ] }/#{ ext[ :biggest_app_pack_id ] } send MISS #{ ranges.size }"
300
+ ranges.each do | pack_id_begin, pack_id_end |
301
+ if pack_count >= BREAK_SEND_MISS
302
+ puts "break send miss at #{ pack_id_begin } #{ Time.new }"
303
+ break
304
+ end
305
+
306
+ ctlmsg = [
307
+ 0,
308
+ MISS,
309
+ app_id,
310
+ pack_id_begin,
311
+ pack_id_end
312
+ ].pack( 'Q>CQ>Q>Q>' )
313
+
314
+ send_pack( p1, ctlmsg, info[ :p2_addr ] )
315
+ pack_count += ( pack_id_end - pack_id_begin + 1 )
316
+ end
317
+ end
318
+ when MISS
319
+ return if sockaddr != info[ :p2_addr ]
320
+
321
+ shadow_id, pack_id_begin, pack_id_end = data[ 9, 24 ].unpack( 'Q>Q>Q>' )
322
+ ext = info[ :shadow_exts ][ shadow_id ]
323
+ return unless ext
324
+
325
+ ( pack_id_begin..pack_id_end ).each do | pack_id |
326
+ send_at = ext[ :send_ats ][ pack_id ]
327
+
328
+ if send_at
329
+ break if now - send_at < STATUS_INTERVAL
330
+
331
+ info[ :resendings ] << [ shadow_id, pack_id ]
332
+ end
333
+ end
334
+
335
+ add_write( p1 )
336
+ when FIN1
337
+ return if sockaddr != info[ :p2_addr ]
338
+
339
+ app_id = data[ 9, 8 ].unpack( 'Q>' ).first
340
+ ctlmsg = [
341
+ 0,
342
+ GOT_FIN1,
343
+ app_id
344
+ ].pack( 'Q>CQ>' )
345
+
346
+ # puts "debug 2-1. recv fin1 -> send got_fin1 -> ext.is_app_closed = true #{ app_id } #{ Time.new }"
347
+ send_pack( p1, ctlmsg, info[ :p2_addr ] )
348
+
349
+ shadow_id = info[ :app_ids ][ app_id ]
350
+ return unless shadow_id
351
+
352
+ ext = info[ :shadow_exts ][ shadow_id ]
353
+ return unless ext
354
+
355
+ ext[ :is_app_closed ] = true
356
+ when GOT_FIN1
357
+ return if sockaddr != info[ :p2_addr ]
358
+
359
+ # puts "debug 1-2. recv got_fin1 -> break loop #{ Time.new }"
360
+ shadow_id = data[ 9, 8 ].unpack( 'Q>' ).first
361
+ info[ :fin1s ].delete( shadow_id )
362
+ when FIN2
363
+ return if sockaddr != info[ :p2_addr ]
364
+
365
+ # puts "debug 1-3. recv fin2 -> send got_fin2 -> del ext #{ Time.new }"
366
+ app_id = data[ 9, 8 ].unpack( 'Q>' ).first
367
+ ctlmsg = [
368
+ 0,
369
+ GOT_FIN2,
370
+ app_id
371
+ ].pack( 'Q>CQ>' )
372
+
373
+ send_pack( p1, ctlmsg, info[ :p2_addr ] )
374
+
375
+ shadow_id = info[ :app_ids ][ app_id ]
376
+ return unless shadow_id
377
+
378
+ del_shadow_ext( info, shadow_id )
379
+ when GOT_FIN2
380
+ return if sockaddr != info[ :p2_addr ]
381
+
382
+ # puts "debug 2-4. recv got_fin2 -> break loop #{ Time.new }"
383
+ shadow_id = data[ 9, 8 ].unpack( 'Q>' ).first
384
+ info[ :fin2s ].delete( shadow_id )
385
+ when P2_FIN
386
+ return if sockaddr != info[ :p2_addr ]
387
+
388
+ puts "recv p2 fin #{ Time.new }"
389
+ add_closing( p1 )
390
+ end
391
+
392
+ return
393
+ end
394
+
395
+ return if sockaddr != info[ :p2_addr ]
396
+
397
+ shadow_id = info[ :app_ids ][ app_id ]
398
+ return unless shadow_id
399
+
400
+ ext = info[ :shadow_exts ][ shadow_id ]
401
+ return if ext.nil? || ext[ :shadow ].closed?
402
+
403
+ pack_id = data[ 8, 8 ].unpack( 'Q>' ).first
404
+ return if ( pack_id <= ext[ :continue_app_pack_id ] ) || ext[ :pieces ].include?( pack_id )
405
+
406
+ data = data[ 16..-1 ]
407
+
408
+ # 解混淆
409
+ if pack_id == 1
410
+ data = @hex.decode( data )
411
+ end
412
+
413
+ # 放进shadow的写前缓存,跳号放碎片缓存
414
+ if pack_id - ext[ :continue_app_pack_id ] == 1
415
+ while ext[ :pieces ].include?( pack_id + 1 )
416
+ data << ext[ :pieces ].delete( pack_id + 1 )
417
+ pack_id += 1
418
+ end
419
+
420
+ ext[ :continue_app_pack_id ] = pack_id
421
+ ext[ :wbuff ] << data
422
+
423
+ if ext[ :wbuff ].bytesize >= CHUNK_SIZE
424
+ spring = ext[ :chunks ].size > 0 ? ( ext[ :spring ] + 1 ) : 0
425
+ filename = "#{ shadow_id }.#{ spring }"
426
+ chunk_path = File.join( @shadow_chunk_dir, filename )
427
+ IO.binwrite( chunk_path, ext[ :wbuff ] )
428
+ ext[ :chunks ] << filename
429
+ ext[ :spring ] = spring
430
+ ext[ :wbuff ].clear
431
+ end
432
+
433
+ add_write( ext[ :shadow ] )
434
+ ext[ :last_traffic_at ] = now
435
+ info[ :last_traffic_at ] = now
436
+ else
437
+ ext[ :pieces ][ pack_id ] = data
438
+ end
439
+
440
+ ext[ :last_recv_at ] = now
441
+ end
442
+
443
+ ##
444
+ # write shadow
445
+ #
446
+ def write_shadow( shadow )
447
+ if @closings.include?( shadow )
448
+ close_shadow( shadow )
449
+ return
450
+ end
451
+
452
+ info = @infos[ shadow ]
453
+ p1 = info[ :p1 ]
454
+
455
+ if p1.closed?
456
+ add_closing( shadow )
457
+ return
458
+ end
459
+
460
+ p1_info = @infos[ p1 ]
461
+ shadow_id = @socks[ shadow ]
462
+ ext = p1_info[ :shadow_exts ][ shadow_id ]
463
+
464
+ # 取写前
465
+ data = ext[ :cache ]
466
+ from = :cache
467
+
468
+ if data.empty?
469
+ if ext[ :chunks ].any?
470
+ path = File.join( @shadow_chunk_dir, ext[ :chunks ].shift )
471
+
472
+ begin
473
+ data = IO.binread( path )
474
+ File.delete( path )
475
+ rescue Errno::ENOENT
476
+ add_closing( shadow )
477
+ return
478
+ end
479
+ else
480
+ data = ext[ :wbuff ]
481
+ from = :wbuff
482
+ end
483
+ end
484
+
485
+ if data.empty?
486
+ if ext[ :is_app_closed ] && ( ext[ :biggest_app_pack_id ] == ext[ :continue_app_pack_id ] )
487
+ # puts "debug 2-2. all sent && ext.biggest_app_pack_id == ext.continue_app_pack_id -> add closing shadow #{ Time.new }"
488
+ add_closing( shadow )
489
+ return
490
+ end
491
+
492
+ @writes.delete( shadow )
493
+ return
494
+ end
495
+
496
+ begin
497
+ written = shadow.write_nonblock( data )
498
+ rescue IO::WaitWritable, Errno::EINTR => e
499
+ ext[ from ] = data
500
+ return
501
+ rescue Exception => e
502
+ add_closing( shadow )
503
+ return
504
+ end
505
+
506
+ data = data[ written..-1 ]
507
+ ext[ from ] = data
508
+ end
509
+
510
+ ##
511
+ # write p1
512
+ #
513
+ def write_p1( p1 )
514
+ if @closings.include?( p1 )
515
+ close_p1( p1 )
516
+ new_p1
517
+ return
518
+ end
519
+
520
+ now = Time.new
521
+ info = @infos[ p1 ]
522
+
523
+ # 重传
524
+ while info[ :resendings ].any?
525
+ shadow_id, pack_id = info[ :resendings ].shift
526
+ ext = info[ :shadow_exts ][ shadow_id ]
527
+
528
+ if ext
529
+ pack = ext[ :wmems ][ pack_id ]
530
+
531
+ if pack
532
+ send_pack( p1, pack, info[ :p2_addr ] )
533
+ ext[ :last_traffic_at ] = now
534
+ info[ :last_traffic_at ] = now
535
+ return
536
+ end
537
+ end
538
+ end
539
+
540
+ # 若写后达到上限,暂停取写前
541
+ if info[ :shadow_exts ].map{ | _, ext | ext[ :wmems ].size }.sum >= WMEMS_LIMIT
542
+ unless info[ :paused ]
543
+ puts "pause #{ Time.new }"
544
+ info[ :paused ] = true
545
+ end
546
+
547
+ @writes.delete( p1 )
548
+ return
549
+ end
550
+
551
+ # 取写前
552
+ if info[ :caches ].any?
553
+ shadow_id, data = info[ :caches ].shift
554
+ elsif info[ :chunks ].any?
555
+ path = File.join( @p1_chunk_dir, info[ :chunks ].shift )
556
+
557
+ begin
558
+ data = IO.binread( path )
559
+ File.delete( path )
560
+ rescue Errno::ENOENT
561
+ add_closing( p1 )
562
+ return
563
+ end
564
+
565
+ caches = []
566
+
567
+ until data.empty?
568
+ shadow_id, pack_size = data[ 0, 10 ].unpack( 'Q>n' )
569
+ caches << [ shadow_id, data[ 10, pack_size ] ]
570
+ data = data[ ( 10 + pack_size )..-1 ]
571
+ end
572
+
573
+ shadow_id, data = caches.shift
574
+ info[ :caches ] = caches
575
+ elsif info[ :wbuffs ].any?
576
+ shadow_id, data = info[ :wbuffs ].shift
577
+ else
578
+ @writes.delete( p1 )
579
+ return
580
+ end
581
+
582
+ ext = info[ :shadow_exts ][ shadow_id ]
583
+
584
+ if ext
585
+ pack_id = ext[ :biggest_pack_id ] + 1
586
+
587
+ if pack_id == 1
588
+ data = @hex.encode( data )
589
+ end
590
+
591
+ pack = "#{ [ shadow_id, pack_id ].pack( 'Q>Q>' ) }#{ data }"
592
+ send_pack( p1, pack, info[ :p2_addr ] )
593
+ ext[ :biggest_pack_id ] = pack_id
594
+ ext[ :wmems ][ pack_id ] = pack
595
+ ext[ :send_ats ][ pack_id ] = now
596
+ ext[ :last_traffic_at ] = now
597
+ info[ :last_traffic_at ] = now
598
+ end
599
+ end
600
+
601
+ def new_p1
602
+ p1 = Socket.new( Socket::AF_INET, Socket::SOCK_DGRAM, 0 )
603
+ p1.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1 )
604
+ p1.bind( Socket.sockaddr_in( 0, '0.0.0.0' ) )
605
+
606
+ p1_id = @hex.gen_random_num
607
+ p1_info = {
608
+ id: p1_id,
609
+ wbuffs: [], # 写前缓存 [ shadow_id, data ]
610
+ caches: [], # 块读出缓存 [ shadow_id, data ]
611
+ chunks: [], # 块队列 filename
612
+ spring: 0, # 块后缀,结块时,如果块队列不为空,则自增,为空,则置为0
613
+ peer_addr: nil, # 对面地址,连发心跳包打洞,离p2pd近的一方通常会撞死几个直到对面出来
614
+ p2_addr: nil, # p2地址
615
+ shadow_exts: {}, # 长命信息 shadow_id => {}
616
+ shadow_ids: {}, # shadow_id => app_id
617
+ app_ids: {}, # app_id => shadow_id
618
+ fin1s: [], # fin1: shadow已关闭,等待对面收完流量 shadow_id
619
+ fin2s: [], # fin2: 流量已收完 shadow_id
620
+ paused: false, # 是否暂停写
621
+ resendings: [], # 重传队列 [ shadow_id, pack_id ]
622
+ last_traffic_at: nil # 收到有效流量,或者发出流量的时间戳
623
+ }
624
+
625
+ @p1 = p1
626
+ @p1_info = p1_info
627
+ @roles[ p1 ] = :p1
628
+ @infos[ p1 ] = p1_info
629
+ @socks[ p1 ] = p1_id
630
+ @sock_ids[ p1_id ] = p1
631
+
632
+ send_pack( p1, @title, @p2pd_sockaddr )
633
+ add_read( p1 )
634
+ loop_send_char( p1 )
635
+ end
636
+
637
+ def loop_send_char( p1 )
638
+ Thread.new do
639
+ is_timeout = true
640
+
641
+ 20.times do
642
+ sleep HEARTBEAT_INTERVAL
643
+
644
+ if p1.closed?
645
+ is_timeout = false
646
+ break
647
+ end
648
+
649
+ p1_info = @infos[ p1 ]
650
+
651
+ if p1_info[ :peer_addr ]
652
+ is_timeout = false
653
+ break
654
+ end
655
+
656
+ @mutex.synchronize do
657
+ send_pack( p1, [ rand( 128 ) ].pack( 'C' ), @p2pd_sockaddr )
658
+ end
659
+ end
660
+
661
+ if is_timeout
662
+ @mutex.synchronize do
663
+ p1_id = @socks[ p1 ]
664
+ @ctlw.write( [ CTL_CLOSE, p1_id ].pack( 'CQ>' ) )
665
+ end
666
+ end
667
+ end
668
+ end
669
+
670
+ def loop_punch_peer( p1 )
671
+ Thread.new do
672
+ 20.times do
673
+ break if p1.closed?
674
+
675
+ p1_info = @infos[ p1 ]
676
+
677
+ @mutex.synchronize do
678
+ send_heartbeat( p1, p1_info[ :peer_addr ] )
679
+ end
680
+
681
+ sleep STATUS_INTERVAL
682
+ end
683
+
684
+ if !p1.closed?
685
+ p1_info = @infos[ p1 ]
686
+
687
+ unless p1_info[ :p2_addr ]
688
+ @mutex.synchronize do
689
+ p1_id = @socks[ p1 ]
690
+ @ctlw.write( [ CTL_CLOSE, p1_id ].pack( 'CQ>' ) )
691
+ end
692
+ end
693
+ end
694
+ end
695
+ end
696
+
697
+ def loop_send_heartbeat( p1 )
698
+ Thread.new do
699
+ loop do
700
+ sleep HEARTBEAT_INTERVAL
701
+ break if p1.closed?
702
+
703
+ @mutex.synchronize do
704
+ p1_info = @infos[ p1 ]
705
+ send_heartbeat( p1, p1_info[ :p2_addr ] )
706
+ end
707
+ end
708
+ end
709
+ end
710
+
711
+ def loop_check_expire( p1 )
712
+ Thread.new do
713
+ loop do
714
+ sleep 60
715
+ break if p1.closed?
716
+
717
+ now = Time.new
718
+ p1_info = @infos[ p1 ]
719
+
720
+ if now - p1_info[ :last_traffic_at ] > EXPIRE_AFTER
721
+ @mutex.synchronize do
722
+ p1_id = @socks[ p1 ]
723
+ # puts "debug ctlw close p1 #{ p1_id } #{ Time.new } p#{ Process.pid }"
724
+ @ctlw.write( [ CTL_CLOSE, p1_id ].pack( 'CQ>' ) )
725
+ end
726
+
727
+ break
728
+ end
729
+
730
+ exts = p1_info[ :shadow_exts ].select{ | _, ext | now - ext[ :created_at ] > 5 }
731
+
732
+ if exts.any?
733
+ @mutex.synchronize do
734
+ exts.each do | shadow_id, ext |
735
+ if ext[ :last_recv_at ].nil? || ( now - ext[ :last_recv_at ] > EXPIRE_AFTER )
736
+ # puts "debug ctlw close shadow #{ shadow_id } #{ Time.new } p#{ Process.pid }"
737
+ @ctlw.write( [ CTL_CLOSE, shadow_id ].pack( 'CQ>' ) )
738
+ end
739
+ end
740
+ end
741
+ end
742
+ end
743
+ end
744
+ end
745
+
746
+ def loop_send_status( p1 )
747
+ Thread.new do
748
+ loop do
749
+ sleep STATUS_INTERVAL
750
+
751
+ if p1.closed?
752
+ # puts "debug p1 is closed, break send status loop #{ Time.new }"
753
+ break
754
+ end
755
+
756
+ p1_info = @infos[ p1 ]
757
+
758
+ if p1_info[ :shadow_exts ].any?
759
+ @mutex.synchronize do
760
+ now = Time.new
761
+
762
+ p1_info[ :shadow_exts ].each do | shadow_id, ext |
763
+ if ext[ :last_traffic_at ] && ( now - ext[ :last_traffic_at ] < SEND_STATUS_UNTIL )
764
+ ctlmsg = [
765
+ 0,
766
+ SHADOW_STATUS,
767
+ shadow_id,
768
+ ext[ :biggest_pack_id ],
769
+ ext[ :continue_app_pack_id ]
770
+ ].pack( 'Q>CQ>Q>Q>' )
771
+
772
+ send_pack( p1, ctlmsg, p1_info[ :p2_addr ] )
773
+ end
774
+ end
775
+ end
776
+ end
777
+
778
+ if p1_info[ :paused ] && ( p1_info[ :shadow_exts ].map{ | _, ext | ext[ :wmems ].size }.sum < RESUME_BELOW )
779
+ @mutex.synchronize do
780
+ p1_id = @socks[ p1 ]
781
+ puts "ctlw resume #{ p1_id } #{ Time.new }"
782
+ @ctlw.write( [ CTL_RESUME, p1_id ].pack( 'CQ>' ) )
783
+ p1_info[ :paused ] = false
784
+ end
785
+ end
786
+ end
787
+ end
788
+ end
789
+
790
+ def loop_send_fin1( p1, shadow_id )
791
+ Thread.new do
792
+ 100.times do
793
+ break if p1.closed?
794
+
795
+ p1_info = @infos[ p1 ]
796
+ break unless p1_info[ :p2_addr ]
797
+
798
+ unless p1_info[ :fin1s ].include?( shadow_id )
799
+ # puts "debug break send fin1 loop #{ Time.new }"
800
+ break
801
+ end
802
+
803
+ @mutex.synchronize do
804
+ ctlmsg = [
805
+ 0,
806
+ FIN1,
807
+ shadow_id
808
+ ].pack( 'Q>CQ>' )
809
+
810
+ # puts "debug send FIN1 #{ shadow_id } #{ Time.new }"
811
+ send_pack( p1, ctlmsg, p1_info[ :p2_addr ] )
812
+ end
813
+
814
+ sleep 1
815
+ end
816
+ end
817
+ end
818
+
819
+ def loop_send_fin2( p1, shadow_id )
820
+ Thread.new do
821
+ 100.times do
822
+ break if p1.closed?
823
+
824
+ p1_info = @infos[ p1 ]
825
+ break unless p1_info[ :p2_addr ]
826
+
827
+ unless p1_info[ :fin2s ].include?( shadow_id )
828
+ # puts "debug break send fin2 loop #{ Time.new }"
829
+ break
830
+ end
831
+
832
+ @mutex.synchronize do
833
+ ctlmsg = [
834
+ 0,
835
+ FIN2,
836
+ shadow_id
837
+ ].pack( 'Q>CQ>' )
838
+
839
+ # puts "debug send FIN2 #{ shadow_id } #{ Time.new }"
840
+ send_pack( p1, ctlmsg, p1_info[ :p2_addr ] )
841
+ end
842
+
843
+ sleep 1
844
+ end
845
+ end
846
+ end
847
+
848
+ def send_heartbeat( p1, target_addr )
849
+ ctlmsg = [ 0, HEARTBEAT, rand( 128 ) ].pack( 'Q>CC' )
850
+ send_pack( p1, ctlmsg, target_addr )
851
+ end
852
+
853
+ def send_pack( sock, data, target_sockaddr )
854
+ begin
855
+ sock.sendmsg( data, 0, target_sockaddr )
856
+ rescue IO::WaitWritable, Errno::EINTR => e
857
+ puts "sendmsg #{ e.class } #{ Time.new }"
858
+ end
859
+ end
860
+
861
+ def add_read( sock )
862
+ return if sock.closed? || @reads.include?( sock )
863
+
864
+ @reads << sock
865
+ end
866
+
867
+ def add_write( sock, data = nil )
868
+ return if sock.closed? || @writes.include?( sock )
869
+
870
+ @writes << sock
871
+ end
872
+
873
+ def add_closing( sock )
874
+ return if sock.closed? || @closings.include?( sock )
875
+
876
+ @reads.delete( sock )
877
+ @closings << sock
878
+ add_write( sock )
879
+ end
880
+
881
+ def close_p1( p1 )
882
+ info = close_sock( p1 )
883
+
884
+ info[ :chunks ].each do | filename |
885
+ begin
886
+ File.delete( File.join( @p1_chunk_dir, filename ) )
887
+ rescue Errno::ENOENT
888
+ end
889
+ end
890
+
891
+ info[ :shadow_exts ].each{ | _, ext | add_closing( ext[ :shadow ] ) }
892
+ end
893
+
894
+ def close_shadow( shadow )
895
+ info = close_sock( shadow )
896
+ p1 = info[ :p1 ]
897
+ return if p1.closed?
898
+
899
+ shadow_id = info[ :id ]
900
+ p1_info = @infos[ p1 ]
901
+ ext = p1_info[ :shadow_exts ][ shadow_id ]
902
+ return unless ext
903
+
904
+ if ext[ :is_app_closed ]
905
+ del_shadow_ext( p1_info, shadow_id )
906
+
907
+ unless p1_info[ :fin2s ].include?( shadow_id )
908
+ # puts "debug 2-3. shadow.close -> ext.is_app_closed ? yes -> del ext -> loop send fin2 #{ Time.new }"
909
+ p1_info[ :fin2s ] << shadow_id
910
+ loop_send_fin2( p1, shadow_id )
911
+ end
912
+ elsif !p1_info[ :fin1s ].include?( shadow_id )
913
+ # puts "debug 1-1. shadow.close -> ext.is_app_closed ? no -> send fin1 loop #{ Time.new }"
914
+ p1_info[ :fin1s ] << shadow_id
915
+ loop_send_fin1( p1, shadow_id )
916
+ end
917
+ end
918
+
919
+ def close_sock( sock )
920
+ sock.close
921
+ @reads.delete( sock )
922
+ @writes.delete( sock )
923
+ @closings.delete( sock )
924
+ @roles.delete( sock )
925
+ info = @infos.delete( sock )
926
+ sock_id = @socks.delete( sock )
927
+ @sock_ids.delete( sock_id )
928
+
929
+ info
930
+ end
931
+
932
+ def del_shadow_ext( p1_info, shadow_id )
933
+ ext = p1_info[ :shadow_exts ].delete( shadow_id )
934
+
935
+ if ext
936
+ ext[ :chunks ].each do | filename |
937
+ begin
938
+ File.delete( File.join( @shadow_chunk_dir, filename ) )
939
+ rescue Errno::ENOENT
940
+ end
941
+ end
942
+ end
943
+
944
+ app_id = p1_info[ :shadow_ids ].delete( shadow_id )
945
+
946
+ if app_id
947
+ p1_info[ :app_ids ].delete( app_id )
948
+ end
949
+ end
950
+
951
+ end
952
+ end