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