p2p2 0.13.3 → 0.14.0

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: 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