p2p2 0.8.0 → 0.8.1

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.
@@ -1,860 +1,845 @@
1
- require 'p2p2/head'
2
- require 'p2p2/hex'
3
- require 'p2p2/version'
4
- require 'socket'
5
-
6
- ##
7
- # P2p2::P2 - 内网里的任意应用,访问另一个内网里的应用服务端。p2端。
8
- #
9
- # 两套关闭
10
- # ========
11
- #
12
- # 1-1. app.close -> ext.is_shadow_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_shadow_closed = true
17
- # 2-2. all sent && ext.biggest_shadow_pack_id == ext.continue_shadow_pack_id -> add closing app
18
- # 2-3. app.close -> ext.is_shadow_closed ? yes -> del ext -> loop send fin2
19
- # 2-4. recv got_fin2 -> break loop
20
- #
21
- module P2p2
22
- class P2
23
-
24
- ##
25
- # p2pd_host 配对服务器ip
26
- # p2pd_port 配对服务器端口
27
- # appd_host 代理地址 不限制访问:'0.0.0.0',或者只允许本地访问:'127.0.0.1'
28
- # appd_port 代理端口
29
- # title 约定的房间名
30
- # app_chunk_dir 文件缓存目录,缓存app来不及写的流量
31
- # p2_chunk_dir 文件缓存目录,缓存p2来不及写的流量
32
- #
33
- def initialize( p2pd_host, p2pd_port, appd_host, appd_port, title, app_chunk_dir = '/tmp', p2_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
- @app_chunk_dir = app_chunk_dir
38
- @p2_chunk_dir = p2_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 / :appd / :app / :p2
46
- @infos = {}
47
-
48
- ctlr, ctlw = IO.pipe
49
- @ctlw = ctlw
50
- @roles[ ctlr ] = :ctlr
51
- add_read( ctlr )
52
- end
53
-
54
- def looping
55
- puts 'looping'
56
-
57
- new_appd
58
- new_p2
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 :appd
69
- read_appd( sock )
70
- when :app
71
- read_app( sock )
72
- when :p2
73
- read_p2( sock )
74
- end
75
- end
76
-
77
- ws.each do | sock |
78
- case @roles[ sock ]
79
- when :app
80
- write_app( sock )
81
- when :p2
82
- write_p2( sock )
83
- end
84
- end
85
- end
86
- end
87
- rescue Interrupt => e
88
- puts e.class
89
- quit!
90
- end
91
-
92
- def quit!
93
- if @p2 && !@p2.closed? && @p2_info[ :p1_addr ]
94
- ctlmsg = [ 0, P2_FIN ].pack( 'Q>C' )
95
- send_pack( @p2, ctlmsg, @p2_info[ :p1_addr ] )
96
- end
97
-
98
- exit
99
- end
100
-
101
- private
102
-
103
- ##
104
- # read ctlr
105
- #
106
- def read_ctlr( ctlr )
107
- case ctlr.read( 1 )
108
- when CTL_CLOSE_SOCK
109
- sock_id = ctlr.read( 8 ).unpack( 'Q>' ).first
110
- sock = @socks[ sock_id ]
111
-
112
- if sock
113
- add_closing( sock )
114
- end
115
- when CTL_RESUME
116
- p2_id = ctlr.read( 8 ).unpack( 'Q>' ).first
117
-
118
- puts "resume #{ p2_id } #{ Time.new }"
119
- p2 = @socks[ p2_id ]
120
-
121
- if p2
122
- add_write( p2 )
123
- end
124
- end
125
- end
126
-
127
- ##
128
- # read appd
129
- #
130
- def read_appd( appd )
131
- begin
132
- app, _ = appd.accept_nonblock
133
- rescue IO::WaitReadable, Errno::EINTR
134
- return
135
- end
136
-
137
- app_id = app.object_id
138
-
139
- @socks[ app_id ] = app
140
- @roles[ app ] = :app
141
- @infos[ app ] = {
142
- wbuff: '', # 写前缓存
143
- cache: '', # 块读出缓存
144
- chunks: [], # 块队列,写前达到块大小时结一个块 filename
145
- spring: 0, # 块后缀,结块时,如果块队列不为空,则自增,为空,则置为0
146
- p2: @p2
147
- }
148
-
149
- @p2_info[ :apps ] << app
150
- @p2_info[ :app_exts ][ app_id ] = {
151
- app: app,
152
- wmems: {}, # 写后缓存 pack_id => data
153
- send_ats: {}, # 上一次发出时间 pack_id => send_at
154
- biggest_pack_id: 0, # 发到几
155
- continue_shadow_pack_id: 0, # 收到几
156
- pieces: {}, # 跳号包 shadow_pack_id => data
157
- shadow_id: nil, # 对面id
158
- is_shadow_closed: false, # 对面是否已关闭
159
- biggest_shadow_pack_id: 0, # 对面发到几
160
- completed_pack_id: 0, # 完成到几(对面收到几)
161
- last_traffic_at: nil # 有流量发出,或者有更新收到几,时间戳
162
- }
163
-
164
- add_read( app )
165
- loop_send_a_new_app( app )
166
- end
167
-
168
- ##
169
- # read app
170
- #
171
- def read_app( app )
172
- begin
173
- data = app.read_nonblock( PACK_SIZE )
174
- rescue IO::WaitReadable, Errno::EINTR
175
- return
176
- rescue Exception => e
177
- add_closing( app )
178
- return
179
- end
180
-
181
- info = @infos[ app ]
182
- p2 = info[ :p2 ]
183
-
184
- if p2.closed?
185
- add_closing( app )
186
- return
187
- end
188
-
189
- p2_info = @infos[ p2 ]
190
- p2_info[ :wbuffs ] << [ app.object_id, data ]
191
-
192
- if p2_info[ :wbuffs ].size >= WBUFFS_LIMIT
193
- spring = p2_info[ :chunks ].size > 0 ? ( p2_info[ :spring ] + 1 ) : 0
194
- filename = "#{ p2.object_id }.#{ spring }"
195
- chunk_path = File.join( @p2_chunk_dir, filename )
196
- IO.binwrite( chunk_path, p2_info[ :wbuffs ].map{ | app_id, data | "#{ [ app_id, data.bytesize ].pack( 'Q>n' ) }#{ data }" }.join )
197
- p2_info[ :chunks ] << filename
198
- p2_info[ :spring ] = spring
199
- p2_info[ :wbuffs ].clear
200
- end
201
-
202
- unless p2_info[ :paused ]
203
- add_write( p2 )
204
- end
205
- end
206
-
207
- ##
208
- # read p2
209
- #
210
- def read_p2( p2 )
211
- data, addrinfo, rflags, *controls = p2.recvmsg
212
- now = Time.new
213
- info = @infos[ p2 ]
214
- info[ :last_coming_at ] = now
215
- shadow_id = data[ 0, 8 ].unpack( 'Q>' ).first
216
-
217
- if shadow_id == 0
218
- case data[ 8 ].unpack( 'C' ).first
219
- when PEER_ADDR
220
- return if addrinfo.to_sockaddr != @p2pd_sockaddr
221
-
222
- unless info[ :p1_addr ]
223
- # puts "debug peer addr #{ data[ 9..-1 ].inspect } #{ Time.new }"
224
- info[ :p1_addr ] = data[ 9..-1 ]
225
- loop_send_status( p2 )
226
- end
227
-
228
- ctlmsg = [ 0, HEARTBEAT, rand( 128 ) ].pack( 'Q>CC' )
229
- send_pack( p2, ctlmsg, info[ :p1_addr ] )
230
- when PAIRED
231
- app_id, shadow_id = data[ 9, 16 ].unpack( 'Q>Q>' )
232
- # puts "debug got PAIRED #{ app_id } #{ shadow_id } #{ Time.new }"
233
-
234
- ext = info[ :app_exts ][ app_id ]
235
- return if ext.nil? || ext[ :shadow_id ]
236
-
237
- ext[ :shadow_id ] = shadow_id
238
- info[ :shadow_ids ][ shadow_id ] = app_id
239
- when SHADOW_STATUS
240
- shadow_id, biggest_shadow_pack_id, continue_app_pack_id = data[ 9, 24 ].unpack( 'Q>Q>Q>' )
241
- app_id = info[ :shadow_ids ][ shadow_id ]
242
- return unless app_id
243
-
244
- ext = info[ :app_exts ][ app_id ]
245
- return unless ext
246
-
247
- # 更新对面发到几
248
- if biggest_shadow_pack_id > ext[ :biggest_shadow_pack_id ]
249
- ext[ :biggest_shadow_pack_id ] = biggest_shadow_pack_id
250
- end
251
-
252
- # 更新对面收到几,释放写后
253
- if continue_app_pack_id > ext[ :completed_pack_id ]
254
- pack_ids = ext[ :wmems ].keys.select { | pack_id | pack_id <= continue_app_pack_id }
255
-
256
- pack_ids.each do | pack_id |
257
- ext[ :wmems ].delete( pack_id )
258
- ext[ :send_ats ].delete( pack_id )
259
- end
260
-
261
- ext[ :completed_pack_id ] = continue_app_pack_id
262
- end
263
-
264
- if ext[ :is_shadow_closed ] && ( ext[ :biggest_shadow_pack_id ] == ext[ :continue_shadow_pack_id ] )
265
- add_write( ext[ :app ] )
266
- return
267
- end
268
-
269
- # 发miss
270
- if !ext[ :app ].closed? && ( ext[ :continue_shadow_pack_id ] < ext[ :biggest_shadow_pack_id ] )
271
- ranges = []
272
- curr_pack_id = ext[ :continue_shadow_pack_id ] + 1
273
-
274
- ext[ :pieces ].keys.sort.each do | pack_id |
275
- if pack_id > curr_pack_id
276
- ranges << [ curr_pack_id, pack_id - 1 ]
277
- end
278
-
279
- curr_pack_id = pack_id + 1
280
- end
281
-
282
- if curr_pack_id <= ext[ :biggest_shadow_pack_id ]
283
- ranges << [ curr_pack_id, ext[ :biggest_shadow_pack_id ] ]
284
- end
285
-
286
- # puts "debug #{ ext[ :continue_shadow_pack_id ] }/#{ ext[ :biggest_shadow_pack_id ] } send MISS #{ ranges.size }"
287
- ranges.each do | pack_id_begin, pack_id_end |
288
- ctlmsg = [
289
- 0,
290
- MISS,
291
- shadow_id,
292
- pack_id_begin,
293
- pack_id_end
294
- ].pack( 'Q>CQ>Q>Q>' )
295
-
296
- send_pack( p2, ctlmsg, info[ :p1_addr ] )
297
- end
298
- end
299
- when MISS
300
- app_id, pack_id_begin, pack_id_end = data[ 9, 24 ].unpack( 'Q>Q>Q>' )
301
- ext = info[ :app_exts ][ app_id ]
302
- return unless ext
303
-
304
- ( pack_id_begin..pack_id_end ).each do | pack_id |
305
- send_at = ext[ :send_ats ][ pack_id ]
306
-
307
- if send_at
308
- break if now - send_at < STATUS_INTERVAL
309
-
310
- info[ :resendings ] << [ app_id, pack_id ]
311
- end
312
- end
313
-
314
- add_write( p2 )
315
- when FIN1
316
- shadow_id = data[ 9, 8 ].unpack( 'Q>' ).first
317
- ctlmsg = [
318
- 0,
319
- GOT_FIN1,
320
- shadow_id
321
- ].pack( 'Q>CQ>' )
322
-
323
- # puts "debug 2-1. recv fin1 -> send got_fin1 -> ext.is_shadow_closed = true #{ shadow_id } #{ Time.new }"
324
- send_pack( p2, ctlmsg, info[ :p1_addr ] )
325
-
326
- app_id = info[ :shadow_ids ][ shadow_id ]
327
- return unless app_id
328
-
329
- ext = info[ :app_exts ][ app_id ]
330
- return unless ext
331
-
332
- ext[ :is_shadow_closed ] = true
333
- when GOT_FIN1
334
- # puts "debug 1-2. recv got_fin1 -> break loop #{ Time.new }"
335
- app_id = data[ 9, 8 ].unpack( 'Q>' ).first
336
- info[ :fin1s ].delete( app_id )
337
- when FIN2
338
- # puts "debug 1-3. recv fin2 -> send got_fin2 -> del ext #{ Time.new }"
339
- shadow_id = data[ 9, 8 ].unpack( 'Q>' ).first
340
- ctlmsg = [
341
- 0,
342
- GOT_FIN2,
343
- shadow_id
344
- ].pack( 'Q>CQ>' )
345
-
346
- send_pack( p2, ctlmsg, info[ :p1_addr ] )
347
-
348
- app_id = info[ :shadow_ids ].delete( shadow_id )
349
- return unless app_id
350
-
351
- info[ :app_exts ].delete( app_id )
352
- when GOT_FIN2
353
- # puts "debug 2-4. recv got_fin2 -> break loop #{ Time.new }"
354
- app_id = data[ 9, 8 ].unpack( 'Q>' ).first
355
- info[ :fin2s ].delete( app_id )
356
- when P1_FIN
357
- raise "p1 fin #{ Time.new }"
358
- end
359
-
360
- return
361
- end
362
-
363
- app_id = info[ :shadow_ids ][ shadow_id ]
364
- return unless app_id
365
-
366
- ext = info[ :app_exts ][ app_id ]
367
- return if ext.nil? || ext[ :app ].closed?
368
-
369
- pack_id = data[ 8, 8 ].unpack( 'Q>' ).first
370
- return if ( pack_id <= ext[ :continue_shadow_pack_id ] ) || ext[ :pieces ].include?( pack_id )
371
-
372
- data = data[ 16..-1 ]
373
-
374
- # 解混淆
375
- if pack_id == 1
376
- data = @hex.decode( data )
377
- end
378
-
379
- # 放进shadow的写前缓存,跳号放碎片缓存
380
- if pack_id - ext[ :continue_shadow_pack_id ] == 1
381
- while ext[ :pieces ].include?( pack_id + 1 )
382
- data << ext[ :pieces ].delete( pack_id + 1 )
383
- pack_id += 1
384
- end
385
-
386
- ext[ :continue_shadow_pack_id ] = pack_id
387
- ext[ :last_traffic_at ] = now
388
-
389
- app_info = @infos[ ext[ :app ] ]
390
- app_info[ :wbuff ] << data
391
-
392
- if app_info[ :wbuff ].bytesize >= CHUNK_SIZE
393
- spring = app_info[ :chunks ].size > 0 ? ( app_info[ :spring ] + 1 ) : 0
394
- filename = "#{ app_id }.#{ spring }"
395
- chunk_path = File.join( @app_chunk_dir, filename )
396
- IO.binwrite( chunk_path, app_info[ :wbuff ] )
397
- app_info[ :chunks ] << filename
398
- app_info[ :spring ] = spring
399
- app_info[ :wbuff ].clear
400
- end
401
-
402
- add_write( ext[ :app ] )
403
- else
404
- ext[ :pieces ][ pack_id ] = data
405
- end
406
- end
407
-
408
- ##
409
- # write app
410
- #
411
- def write_app( app )
412
- if @closings.include?( app )
413
- close_app( app )
414
- return
415
- end
416
-
417
- info = @infos[ app ]
418
-
419
- # 取写前
420
- data = info[ :cache ]
421
- from = :cache
422
-
423
- if data.empty?
424
- if info[ :chunks ].any?
425
- path = File.join( @app_chunk_dir, info[ :chunks ].shift )
426
-
427
- begin
428
- data = IO.binread( path )
429
- File.delete( path )
430
- rescue Errno::ENOENT
431
- add_closing( app )
432
- return
433
- end
434
- else
435
- data = info[ :wbuff ]
436
- from = :wbuff
437
- end
438
- end
439
-
440
- if data.empty?
441
- p2 = info[ :p2 ]
442
-
443
- if p2.closed?
444
- add_closing( app )
445
- return
446
- end
447
-
448
- p2_info = @infos[ p2 ]
449
- ext = p2_info[ :app_exts ][ app.object_id ]
450
-
451
- if ext[ :is_shadow_closed ] && ( ext[ :biggest_shadow_pack_id ] == ext[ :continue_shadow_pack_id ] )
452
- # puts "debug 2-2. all sent && ext.biggest_shadow_pack_id == ext.continue_shadow_pack_id -> add closing app #{ Time.new }"
453
- add_closing( app )
454
- return
455
- end
456
-
457
- @writes.delete( app )
458
- return
459
- end
460
-
461
- begin
462
- written = app.write_nonblock( data )
463
- rescue IO::WaitWritable, Errno::EINTR => e
464
- info[ from ] = data
465
- return
466
- rescue Exception => e
467
- add_closing( app )
468
- return
469
- end
470
-
471
- data = data[ written..-1 ]
472
- info[ from ] = data
473
- end
474
-
475
- ##
476
- # write p2
477
- #
478
- def write_p2( p2 )
479
- if @closings.include?( p2 )
480
- close_p2( p2 )
481
- new_p2
482
- return
483
- end
484
-
485
- now = Time.new
486
- info = @infos[ p2 ]
487
-
488
- # 重传
489
- while info[ :resendings ].any?
490
- app_id, pack_id = info[ :resendings ].shift
491
- ext = info[ :app_exts ][ app_id ]
492
-
493
- if ext
494
- pack = ext[ :wmems ][ pack_id ]
495
-
496
- if pack
497
- send_pack( p2, pack, info[ :p1_addr ] )
498
- ext[ :last_traffic_at ] = now
499
- return
500
- end
501
- end
502
- end
503
-
504
- # 若写后到达上限,暂停取写前
505
- if info[ :app_exts ].map{ | _, ext | ext[ :wmems ].size }.sum >= WMEMS_LIMIT
506
- unless info[ :paused ]
507
- puts "pause #{ Time.new }"
508
- info[ :paused ] = true
509
- end
510
-
511
- @writes.delete( p2 )
512
- return
513
- end
514
-
515
- # 取写前
516
- if info[ :caches ].any?
517
- app_id, data = info[ :caches ].shift
518
- elsif info[ :chunks ].any?
519
- path = File.join( @p2_chunk_dir, info[ :chunks ].shift )
520
-
521
- begin
522
- data = IO.binread( path )
523
- File.delete( path )
524
- rescue Errno::ENOENT
525
- add_closing( p2 )
526
- return
527
- end
528
-
529
- caches = []
530
-
531
- until data.empty?
532
- app_id, pack_size = data[ 0, 10 ].unpack( 'Q>n' )
533
- caches << [ app_id, data[ 10, pack_size ] ]
534
- data = data[ ( 10 + pack_size )..-1 ]
535
- end
536
-
537
- app_id, data = caches.shift
538
- info[ :caches ] = caches
539
- elsif info[ :wbuffs ].any?
540
- app_id, data = info[ :wbuffs ].shift
541
- else
542
- @writes.delete( p2 )
543
- return
544
- end
545
-
546
- ext = info[ :app_exts ][ app_id ]
547
-
548
- if ext
549
- pack_id = ext[ :biggest_pack_id ] + 1
550
-
551
- if pack_id == 1
552
- data = @hex.encode( data )
553
- end
554
-
555
- pack = "#{ [ app_id, pack_id ].pack( 'Q>Q>' ) }#{ data }"
556
- send_pack( p2, pack, info[ :p1_addr ] )
557
- ext[ :biggest_pack_id ] = pack_id
558
- ext[ :wmems ][ pack_id ] = pack
559
- ext[ :send_ats ][ pack_id ] = now
560
- ext[ :last_traffic_at ] = now
561
- end
562
- end
563
-
564
- def new_appd
565
- appd = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
566
- appd.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1 )
567
- appd.setsockopt( Socket::SOL_TCP, Socket::TCP_NODELAY, 1 )
568
- appd.bind( @appd_sockaddr )
569
- appd.listen( 511 )
570
-
571
- @roles[ appd ] = :appd
572
- add_read( appd )
573
- end
574
-
575
- def new_p2
576
- p2 = Socket.new( Socket::AF_INET, Socket::SOCK_DGRAM, 0 )
577
- p2.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1 )
578
- p2.bind( Socket.sockaddr_in( 0, '0.0.0.0' ) )
579
-
580
- p2_info = {
581
- wbuffs: [], # 写前缓存 [ app_id, data ]
582
- caches: [], # 块读出缓存 [ app_id, data ]
583
- chunks: [], # 块队列 filename
584
- spring: 0, # 块后缀,结块时,如果块队列不为空,则自增,为空,则置为0
585
- p1_addr: nil, # 远端地址
586
- shadow_ids: {}, # shadow_id => app_id
587
- apps: [], # 开着的app
588
- app_exts: {}, # 传输相关 app_id => {}
589
- fin1s: [], # fin1: app已关闭,等待对面收完流量 app_id
590
- fin2s: [], # fin2: 流量已收完 app_id
591
- last_coming_at: nil, # 上一次来流量的时间
592
- paused: false, # 是否暂停写
593
- resendings: [] # 重传队列 [ app_id, pack_id ]
594
- }
595
-
596
- @p2 = p2
597
- @p2_info = p2_info
598
- @socks[ p2.object_id ] = p2
599
- @roles[ p2 ] = :p2
600
- @infos[ p2 ] = p2_info
601
-
602
- send_pack( p2, @title, @p2pd_sockaddr )
603
- add_read( p2 )
604
- check_reconn( p2 )
605
- loop_expire( p2 )
606
- end
607
-
608
- def check_reconn( p2 )
609
- Thread.new do
610
- sleep RECONN_AFTER
611
-
612
- unless p2.closed?
613
- p2_info = @infos[ p2 ]
614
-
615
- unless p2_info[ :p1_addr ]
616
- @mutex.synchronize do
617
- puts "reconn #{ Time.new }"
618
- @ctlw.write( [ CTL_CLOSE_SOCK, [ p2.object_id ].pack( 'Q>' ) ].join )
619
- end
620
- end
621
- end
622
- end
623
- end
624
-
625
- def loop_expire( p2 )
626
- Thread.new do
627
- loop do
628
- sleep CHECK_EXPIRE_INTERVAL
629
-
630
- break if p2.closed?
631
-
632
- p2_info = @infos[ p2 ]
633
-
634
- if p2_info[ :last_coming_at ] && ( Time.new - p2_info[ :last_coming_at ] > EXPIRE_AFTER )
635
- @mutex.synchronize do
636
- puts "expire p2 #{ p2.object_id } #{ Time.new }"
637
- @ctlw.write( [ CTL_CLOSE_SOCK, [ p2.object_id ].pack( 'Q>' ) ].join )
638
- end
639
- end
640
- end
641
- end
642
- end
643
-
644
- def loop_send_status( p2 )
645
- Thread.new do
646
- loop do
647
- sleep STATUS_INTERVAL
648
-
649
- if p2.closed?
650
- # puts "debug p2 is closed, break send status loop #{ Time.new }"
651
- break
652
- end
653
-
654
- p2_info = @infos[ p2 ]
655
-
656
- if p2_info[ :app_exts ].any?
657
- @mutex.synchronize do
658
- now = Time.new
659
-
660
- p2_info[ :app_exts ].each do | app_id, ext |
661
- if ext[ :last_traffic_at ] && ( now - ext[ :last_traffic_at ] < SEND_STATUS_UNTIL )
662
- ctlmsg = [
663
- 0,
664
- APP_STATUS,
665
- app_id,
666
- ext[ :biggest_pack_id ],
667
- ext[ :continue_shadow_pack_id ]
668
- ].pack( 'Q>CQ>Q>Q>' )
669
-
670
- send_pack( p2, ctlmsg, p2_info[ :p1_addr ] )
671
- end
672
- end
673
- end
674
- end
675
-
676
- if p2_info[ :paused ] && ( p2_info[ :app_exts ].map{ | _, ext | ext[ :wmems ].size }.sum < RESUME_BELOW )
677
- @mutex.synchronize do
678
- @ctlw.write( [ CTL_RESUME, [ p2.object_id ].pack( 'Q>' ) ].join )
679
- p2_info[ :paused ] = false
680
- end
681
- end
682
- end
683
- end
684
- end
685
-
686
- def loop_send_fin1( p2, app_id )
687
- Thread.new do
688
- 100.times do
689
- break if p2.closed?
690
-
691
- p2_info = @infos[ p2 ]
692
-
693
- unless p2_info[ :fin1s ].include?( app_id )
694
- # puts "debug break send fin1 loop #{ Time.new }"
695
- break
696
- end
697
-
698
- @mutex.synchronize do
699
- ctlmsg = [
700
- 0,
701
- FIN1,
702
- app_id
703
- ].pack( 'Q>CQ>' )
704
-
705
- # puts "debug send FIN1 #{ app_id } #{ Time.new }"
706
- send_pack( p2, ctlmsg, p2_info[ :p1_addr ] )
707
- end
708
-
709
- sleep 1
710
- end
711
- end
712
- end
713
-
714
- def loop_send_fin2( p2, app_id )
715
- Thread.new do
716
- 100.times do
717
- break if p2.closed?
718
-
719
- p2_info = @infos[ p2 ]
720
-
721
- unless p2_info[ :fin2s ].include?( app_id )
722
- # puts "debug break send fin2 loop #{ Time.new }"
723
- break
724
- end
725
-
726
- @mutex.synchronize do
727
- ctlmsg = [
728
- 0,
729
- FIN2,
730
- app_id
731
- ].pack( 'Q>CQ>' )
732
-
733
- # puts "debug send FIN2 #{ app_id } #{ Time.new }"
734
- send_pack( p2, ctlmsg, p2_info[ :p1_addr ] )
735
- end
736
-
737
- sleep 1
738
- end
739
- end
740
- end
741
-
742
- def loop_send_a_new_app( app )
743
- Thread.new do
744
- 100.times do
745
- break if app.closed?
746
-
747
- app_info = @infos[ app ]
748
- p2 = app_info[ :p2 ]
749
- break if p2.closed?
750
-
751
- p2_info = @infos[ p2 ]
752
-
753
- if p2_info[ :p1_addr ]
754
- ext = p2_info[ :app_exts ][ app.object_id ]
755
-
756
- if ext.nil? || ext[ :shadow_id ]
757
- # puts "debug break a new app loop #{ Time.new }"
758
- break
759
- end
760
-
761
- @mutex.synchronize do
762
- ctlmsg = [ 0, A_NEW_APP, app.object_id ].pack( 'Q>CQ>' )
763
- # puts "debug send a new app #{ Time.new }"
764
- send_pack( p2, ctlmsg, p2_info[ :p1_addr ] )
765
- end
766
- end
767
-
768
- sleep 1
769
- end
770
- end
771
- end
772
-
773
- def send_pack( sock, data, target_sockaddr )
774
- begin
775
- sock.sendmsg( data, 0, target_sockaddr )
776
- rescue IO::WaitWritable, Errno::EINTR => e
777
- puts "sendmsg #{ e.class } #{ Time.new }"
778
- end
779
- end
780
-
781
- def add_read( sock )
782
- return if sock.closed? || @reads.include?( sock )
783
-
784
- @reads << sock
785
- end
786
-
787
- def add_write( sock, data = nil )
788
- return if sock.closed? || @writes.include?( sock )
789
-
790
- @writes << sock
791
- end
792
-
793
- def add_closing( sock )
794
- return if sock.closed? || @closings.include?( sock )
795
-
796
- @reads.delete( sock )
797
- @closings << sock
798
- add_write( sock )
799
- end
800
-
801
- def close_p2( p2 )
802
- info = close_sock( p2 )
803
- info[ :apps ].each { | app | add_closing( app ) }
804
- end
805
-
806
- def close_app( app )
807
- info = close_sock( app )
808
- p2 = info[ :p2 ]
809
- return if p2.closed?
810
-
811
- p2_info = @infos[ p2 ]
812
- p2_info[ :apps ].delete( app )
813
- app_id = app.object_id
814
- ext = p2_info[ :app_exts ][ app_id ]
815
- return unless ext
816
-
817
- if ext[ :is_shadow_closed ]
818
- # puts "debug 2-3. app.close -> ext.is_shadow_closed ? yes -> del ext -> loop send fin2 #{ Time.new }"
819
- p2_info[ :shadow_ids ].delete( ext[ :shadow_id ] )
820
- p2_info[ :app_exts ].delete( app_id )
821
-
822
- unless p2_info[ :fin2s ].include?( app_id )
823
- p2_info[ :fin2s ] << app_id
824
- loop_send_fin2( p2, app_id )
825
- end
826
- else
827
- # puts "debug 1-1. app.close -> ext.is_shadow_closed ? no -> send fin1 loop #{ Time.new }"
828
-
829
- if p2_info[ :p1_addr ] && !p2_info[ :fin1s ].include?( app_id )
830
- p2_info[ :fin1s ] << app_id
831
- loop_send_fin1( p2, app_id )
832
- end
833
- end
834
- end
835
-
836
- def close_sock( sock )
837
- sock.close
838
- @reads.delete( sock )
839
- @writes.delete( sock )
840
- @closings.delete( sock )
841
- @socks.delete( sock.object_id )
842
- role = @roles.delete( sock )
843
- info = @infos.delete( sock )
844
-
845
- if info
846
- chunk_dir = ( role == :app ? @app_chunk_dir : @p2_chunk_dir )
847
-
848
- info[ :chunks ].each do | filename |
849
- begin
850
- File.delete( File.join( chunk_dir, filename ) )
851
- rescue Errno::ENOENT
852
- end
853
- end
854
- end
855
-
856
- info
857
- end
858
-
859
- end
860
- end
1
+ require 'p2p2/head'
2
+ require 'p2p2/hex'
3
+ require 'p2p2/version'
4
+ require 'socket'
5
+
6
+ ##
7
+ # P2p2::P2 - 内网里的任意应用,访问另一个内网里的应用服务端。p2端。
8
+ #
9
+ # 两套关闭
10
+ # ========
11
+ #
12
+ # 1-1. app.close -> ext.is_shadow_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_shadow_closed = true
17
+ # 2-2. all sent && ext.biggest_shadow_pack_id == ext.continue_shadow_pack_id -> add closing app
18
+ # 2-3. app.close -> ext.is_shadow_closed ? yes -> del ext -> loop send fin2
19
+ # 2-4. recv got_fin2 -> break loop
20
+ #
21
+ module P2p2
22
+ class P2
23
+
24
+ ##
25
+ # p2pd_host 配对服务器ip
26
+ # p2pd_port 配对服务器端口
27
+ # appd_host 代理地址 不限制访问:'0.0.0.0',或者只允许本地访问:'127.0.0.1'
28
+ # appd_port 代理端口
29
+ # title 约定的房间名
30
+ # app_chunk_dir 文件缓存目录,缓存app来不及写的流量
31
+ # p2_chunk_dir 文件缓存目录,缓存p2来不及写的流量
32
+ #
33
+ def initialize( p2pd_host, p2pd_port, appd_host, appd_port, title, app_chunk_dir = '/tmp', p2_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
+ @app_chunk_dir = app_chunk_dir
38
+ @p2_chunk_dir = p2_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 / :appd / :app / :p2
46
+ @infos = {}
47
+
48
+ ctlr, ctlw = IO.pipe
49
+ @ctlw = ctlw
50
+ @roles[ ctlr ] = :ctlr
51
+ add_read( ctlr )
52
+ end
53
+
54
+ def looping
55
+ puts 'looping'
56
+
57
+ new_appd
58
+ new_p2
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 :appd
69
+ read_appd( sock )
70
+ when :app
71
+ read_app( sock )
72
+ when :p2
73
+ read_p2( sock )
74
+ end
75
+ end
76
+
77
+ ws.each do | sock |
78
+ case @roles[ sock ]
79
+ when :app
80
+ write_app( sock )
81
+ when :p2
82
+ write_p2( sock )
83
+ end
84
+ end
85
+ end
86
+ end
87
+ rescue Interrupt => e
88
+ puts e.class
89
+ quit!
90
+ end
91
+
92
+ def quit!
93
+ if @p2 && !@p2.closed? && @p2_info[ :p1_addr ]
94
+ ctlmsg = [ 0, P2_FIN ].pack( 'Q>C' )
95
+ send_pack( @p2, ctlmsg, @p2_info[ :p1_addr ] )
96
+ end
97
+
98
+ exit
99
+ end
100
+
101
+ private
102
+
103
+ ##
104
+ # read ctlr
105
+ #
106
+ def read_ctlr( ctlr )
107
+ case ctlr.read( 1 )
108
+ when CTL_CLOSE_SOCK
109
+ sock_id = ctlr.read( 8 ).unpack( 'Q>' ).first
110
+ sock = @socks[ sock_id ]
111
+
112
+ if sock
113
+ add_closing( sock )
114
+ end
115
+ when CTL_RESUME
116
+ p2_id = ctlr.read( 8 ).unpack( 'Q>' ).first
117
+
118
+ puts "resume #{ p2_id } #{ Time.new }"
119
+ p2 = @socks[ p2_id ]
120
+
121
+ if p2
122
+ add_write( p2 )
123
+ end
124
+ end
125
+ end
126
+
127
+ ##
128
+ # read appd
129
+ #
130
+ def read_appd( appd )
131
+ begin
132
+ app, _ = appd.accept_nonblock
133
+ rescue IO::WaitReadable, Errno::EINTR
134
+ return
135
+ end
136
+
137
+ app_id = app.object_id
138
+
139
+ @socks[ app_id ] = app
140
+ @roles[ app ] = :app
141
+ @infos[ app ] = {
142
+ wbuff: '', # 写前缓存
143
+ cache: '', # 块读出缓存
144
+ chunks: [], # 块队列,写前达到块大小时结一个块 filename
145
+ spring: 0, # 块后缀,结块时,如果块队列不为空,则自增,为空,则置为0
146
+ p2: @p2
147
+ }
148
+
149
+ @p2_info[ :apps ] << app
150
+ @p2_info[ :app_exts ][ app_id ] = {
151
+ app: app,
152
+ wmems: {}, # 写后缓存 pack_id => data
153
+ send_ats: {}, # 上一次发出时间 pack_id => send_at
154
+ biggest_pack_id: 0, # 发到几
155
+ continue_shadow_pack_id: 0, # 收到几
156
+ pieces: {}, # 跳号包 shadow_pack_id => data
157
+ shadow_id: nil, # 对面id
158
+ is_shadow_closed: false, # 对面是否已关闭
159
+ biggest_shadow_pack_id: 0, # 对面发到几
160
+ completed_pack_id: 0, # 完成到几(对面收到几)
161
+ last_traffic_at: nil # 有流量发出,或者有更新收到几,时间戳
162
+ }
163
+
164
+ add_read( app )
165
+ loop_send_a_new_app( app )
166
+ end
167
+
168
+ ##
169
+ # read app
170
+ #
171
+ def read_app( app )
172
+ begin
173
+ data = app.read_nonblock( PACK_SIZE )
174
+ rescue IO::WaitReadable, Errno::EINTR
175
+ return
176
+ rescue Exception => e
177
+ add_closing( app )
178
+ return
179
+ end
180
+
181
+ info = @infos[ app ]
182
+ p2 = info[ :p2 ]
183
+
184
+ if p2.closed?
185
+ add_closing( app )
186
+ return
187
+ end
188
+
189
+ p2_info = @infos[ p2 ]
190
+ p2_info[ :wbuffs ] << [ app.object_id, data ]
191
+
192
+ if p2_info[ :wbuffs ].size >= WBUFFS_LIMIT
193
+ spring = p2_info[ :chunks ].size > 0 ? ( p2_info[ :spring ] + 1 ) : 0
194
+ filename = "#{ p2.object_id }.#{ spring }"
195
+ chunk_path = File.join( @p2_chunk_dir, filename )
196
+ IO.binwrite( chunk_path, p2_info[ :wbuffs ].map{ | app_id, data | "#{ [ app_id, data.bytesize ].pack( 'Q>n' ) }#{ data }" }.join )
197
+ p2_info[ :chunks ] << filename
198
+ p2_info[ :spring ] = spring
199
+ p2_info[ :wbuffs ].clear
200
+ end
201
+
202
+ unless p2_info[ :paused ]
203
+ add_write( p2 )
204
+ end
205
+ end
206
+
207
+ ##
208
+ # read p2
209
+ #
210
+ def read_p2( p2 )
211
+ data, addrinfo, rflags, *controls = p2.recvmsg
212
+ now = Time.new
213
+ info = @infos[ p2 ]
214
+ info[ :last_coming_at ] = now
215
+ shadow_id = data[ 0, 8 ].unpack( 'Q>' ).first
216
+
217
+ if shadow_id == 0
218
+ case data[ 8 ].unpack( 'C' ).first
219
+ when PEER_ADDR
220
+ return if addrinfo.to_sockaddr != @p2pd_sockaddr
221
+
222
+ unless info[ :p1_addr ]
223
+ # puts "debug peer addr #{ data[ 9..-1 ].inspect } #{ Time.new }"
224
+ info[ :p1_addr ] = data[ 9..-1 ]
225
+ loop_send_status( p2 )
226
+ end
227
+
228
+ ctlmsg = [ 0, HEARTBEAT, rand( 128 ) ].pack( 'Q>CC' )
229
+ send_pack( p2, ctlmsg, info[ :p1_addr ] )
230
+ when PAIRED
231
+ app_id, shadow_id = data[ 9, 16 ].unpack( 'Q>Q>' )
232
+ # puts "debug got PAIRED #{ app_id } #{ shadow_id } #{ Time.new }"
233
+
234
+ ext = info[ :app_exts ][ app_id ]
235
+ return if ext.nil? || ext[ :shadow_id ]
236
+
237
+ ext[ :shadow_id ] = shadow_id
238
+ info[ :shadow_ids ][ shadow_id ] = app_id
239
+ when SHADOW_STATUS
240
+ shadow_id, biggest_shadow_pack_id, continue_app_pack_id = data[ 9, 24 ].unpack( 'Q>Q>Q>' )
241
+ app_id = info[ :shadow_ids ][ shadow_id ]
242
+ return unless app_id
243
+
244
+ ext = info[ :app_exts ][ app_id ]
245
+ return unless ext
246
+
247
+ # 更新对面发到几
248
+ if biggest_shadow_pack_id > ext[ :biggest_shadow_pack_id ]
249
+ ext[ :biggest_shadow_pack_id ] = biggest_shadow_pack_id
250
+ end
251
+
252
+ # 更新对面收到几,释放写后
253
+ if continue_app_pack_id > ext[ :completed_pack_id ]
254
+ pack_ids = ext[ :wmems ].keys.select { | pack_id | pack_id <= continue_app_pack_id }
255
+
256
+ pack_ids.each do | pack_id |
257
+ ext[ :wmems ].delete( pack_id )
258
+ ext[ :send_ats ].delete( pack_id )
259
+ end
260
+
261
+ ext[ :completed_pack_id ] = continue_app_pack_id
262
+ end
263
+
264
+ if ext[ :is_shadow_closed ] && ( ext[ :biggest_shadow_pack_id ] == ext[ :continue_shadow_pack_id ] )
265
+ add_write( ext[ :app ] )
266
+ return
267
+ end
268
+
269
+ # 发miss
270
+ if !ext[ :app ].closed? && ( ext[ :continue_shadow_pack_id ] < ext[ :biggest_shadow_pack_id ] )
271
+ ranges = []
272
+ curr_pack_id = ext[ :continue_shadow_pack_id ] + 1
273
+
274
+ ext[ :pieces ].keys.sort.each do | pack_id |
275
+ if pack_id > curr_pack_id
276
+ ranges << [ curr_pack_id, pack_id - 1 ]
277
+ end
278
+
279
+ curr_pack_id = pack_id + 1
280
+ end
281
+
282
+ if curr_pack_id <= ext[ :biggest_shadow_pack_id ]
283
+ ranges << [ curr_pack_id, ext[ :biggest_shadow_pack_id ] ]
284
+ end
285
+
286
+ # puts "debug #{ ext[ :continue_shadow_pack_id ] }/#{ ext[ :biggest_shadow_pack_id ] } send MISS #{ ranges.size }"
287
+ ranges.each do | pack_id_begin, pack_id_end |
288
+ ctlmsg = [
289
+ 0,
290
+ MISS,
291
+ shadow_id,
292
+ pack_id_begin,
293
+ pack_id_end
294
+ ].pack( 'Q>CQ>Q>Q>' )
295
+
296
+ send_pack( p2, ctlmsg, info[ :p1_addr ] )
297
+ end
298
+ end
299
+ when MISS
300
+ app_id, pack_id_begin, pack_id_end = data[ 9, 24 ].unpack( 'Q>Q>Q>' )
301
+ ext = info[ :app_exts ][ app_id ]
302
+ return unless ext
303
+
304
+ ( pack_id_begin..pack_id_end ).each do | pack_id |
305
+ send_at = ext[ :send_ats ][ pack_id ]
306
+
307
+ if send_at
308
+ break if now - send_at < STATUS_INTERVAL
309
+
310
+ info[ :resendings ] << [ app_id, pack_id ]
311
+ end
312
+ end
313
+
314
+ add_write( p2 )
315
+ when FIN1
316
+ shadow_id = data[ 9, 8 ].unpack( 'Q>' ).first
317
+ ctlmsg = [
318
+ 0,
319
+ GOT_FIN1,
320
+ shadow_id
321
+ ].pack( 'Q>CQ>' )
322
+
323
+ # puts "debug 2-1. recv fin1 -> send got_fin1 -> ext.is_shadow_closed = true #{ shadow_id } #{ Time.new }"
324
+ send_pack( p2, ctlmsg, info[ :p1_addr ] )
325
+
326
+ app_id = info[ :shadow_ids ][ shadow_id ]
327
+ return unless app_id
328
+
329
+ ext = info[ :app_exts ][ app_id ]
330
+ return unless ext
331
+
332
+ ext[ :is_shadow_closed ] = true
333
+ when GOT_FIN1
334
+ # puts "debug 1-2. recv got_fin1 -> break loop #{ Time.new }"
335
+ app_id = data[ 9, 8 ].unpack( 'Q>' ).first
336
+ info[ :fin1s ].delete( app_id )
337
+ when FIN2
338
+ # puts "debug 1-3. recv fin2 -> send got_fin2 -> del ext #{ Time.new }"
339
+ shadow_id = data[ 9, 8 ].unpack( 'Q>' ).first
340
+ ctlmsg = [
341
+ 0,
342
+ GOT_FIN2,
343
+ shadow_id
344
+ ].pack( 'Q>CQ>' )
345
+
346
+ send_pack( p2, ctlmsg, info[ :p1_addr ] )
347
+
348
+ app_id = info[ :shadow_ids ].delete( shadow_id )
349
+ return unless app_id
350
+
351
+ info[ :app_exts ].delete( app_id )
352
+ when GOT_FIN2
353
+ # puts "debug 2-4. recv got_fin2 -> break loop #{ Time.new }"
354
+ app_id = data[ 9, 8 ].unpack( 'Q>' ).first
355
+ info[ :fin2s ].delete( app_id )
356
+ when P1_FIN
357
+ raise "p1 fin #{ Time.new }"
358
+ end
359
+
360
+ return
361
+ end
362
+
363
+ app_id = info[ :shadow_ids ][ shadow_id ]
364
+ return unless app_id
365
+
366
+ ext = info[ :app_exts ][ app_id ]
367
+ return if ext.nil? || ext[ :app ].closed?
368
+
369
+ pack_id = data[ 8, 8 ].unpack( 'Q>' ).first
370
+ return if ( pack_id <= ext[ :continue_shadow_pack_id ] ) || ext[ :pieces ].include?( pack_id )
371
+
372
+ data = data[ 16..-1 ]
373
+
374
+ # 解混淆
375
+ if pack_id == 1
376
+ data = @hex.decode( data )
377
+ end
378
+
379
+ # 放进shadow的写前缓存,跳号放碎片缓存
380
+ if pack_id - ext[ :continue_shadow_pack_id ] == 1
381
+ while ext[ :pieces ].include?( pack_id + 1 )
382
+ data << ext[ :pieces ].delete( pack_id + 1 )
383
+ pack_id += 1
384
+ end
385
+
386
+ ext[ :continue_shadow_pack_id ] = pack_id
387
+ ext[ :last_traffic_at ] = now
388
+
389
+ app_info = @infos[ ext[ :app ] ]
390
+ app_info[ :wbuff ] << data
391
+
392
+ if app_info[ :wbuff ].bytesize >= CHUNK_SIZE
393
+ spring = app_info[ :chunks ].size > 0 ? ( app_info[ :spring ] + 1 ) : 0
394
+ filename = "#{ app_id }.#{ spring }"
395
+ chunk_path = File.join( @app_chunk_dir, filename )
396
+ IO.binwrite( chunk_path, app_info[ :wbuff ] )
397
+ app_info[ :chunks ] << filename
398
+ app_info[ :spring ] = spring
399
+ app_info[ :wbuff ].clear
400
+ end
401
+
402
+ add_write( ext[ :app ] )
403
+ else
404
+ ext[ :pieces ][ pack_id ] = data
405
+ end
406
+ end
407
+
408
+ ##
409
+ # write app
410
+ #
411
+ def write_app( app )
412
+ if @closings.include?( app )
413
+ close_app( app )
414
+ return
415
+ end
416
+
417
+ info = @infos[ app ]
418
+
419
+ # 取写前
420
+ data = info[ :cache ]
421
+ from = :cache
422
+
423
+ if data.empty?
424
+ if info[ :chunks ].any?
425
+ path = File.join( @app_chunk_dir, info[ :chunks ].shift )
426
+
427
+ begin
428
+ data = IO.binread( path )
429
+ File.delete( path )
430
+ rescue Errno::ENOENT
431
+ add_closing( app )
432
+ return
433
+ end
434
+ else
435
+ data = info[ :wbuff ]
436
+ from = :wbuff
437
+ end
438
+ end
439
+
440
+ if data.empty?
441
+ p2 = info[ :p2 ]
442
+
443
+ if p2.closed?
444
+ add_closing( app )
445
+ return
446
+ end
447
+
448
+ p2_info = @infos[ p2 ]
449
+ ext = p2_info[ :app_exts ][ app.object_id ]
450
+
451
+ if ext[ :is_shadow_closed ] && ( ext[ :biggest_shadow_pack_id ] == ext[ :continue_shadow_pack_id ] )
452
+ # puts "debug 2-2. all sent && ext.biggest_shadow_pack_id == ext.continue_shadow_pack_id -> add closing app #{ Time.new }"
453
+ add_closing( app )
454
+ return
455
+ end
456
+
457
+ @writes.delete( app )
458
+ return
459
+ end
460
+
461
+ begin
462
+ written = app.write_nonblock( data )
463
+ rescue IO::WaitWritable, Errno::EINTR => e
464
+ info[ from ] = data
465
+ return
466
+ rescue Exception => e
467
+ add_closing( app )
468
+ return
469
+ end
470
+
471
+ data = data[ written..-1 ]
472
+ info[ from ] = data
473
+ end
474
+
475
+ ##
476
+ # write p2
477
+ #
478
+ def write_p2( p2 )
479
+ if @closings.include?( p2 )
480
+ close_p2( p2 )
481
+ new_p2
482
+ return
483
+ end
484
+
485
+ now = Time.new
486
+ info = @infos[ p2 ]
487
+
488
+ # 重传
489
+ while info[ :resendings ].any?
490
+ app_id, pack_id = info[ :resendings ].shift
491
+ ext = info[ :app_exts ][ app_id ]
492
+
493
+ if ext
494
+ pack = ext[ :wmems ][ pack_id ]
495
+
496
+ if pack
497
+ send_pack( p2, pack, info[ :p1_addr ] )
498
+ ext[ :last_traffic_at ] = now
499
+ return
500
+ end
501
+ end
502
+ end
503
+
504
+ # 若写后到达上限,暂停取写前
505
+ if info[ :app_exts ].map{ | _, ext | ext[ :wmems ].size }.sum >= WMEMS_LIMIT
506
+ unless info[ :paused ]
507
+ puts "pause #{ Time.new }"
508
+ info[ :paused ] = true
509
+ end
510
+
511
+ @writes.delete( p2 )
512
+ return
513
+ end
514
+
515
+ # 取写前
516
+ if info[ :caches ].any?
517
+ app_id, data = info[ :caches ].shift
518
+ elsif info[ :chunks ].any?
519
+ path = File.join( @p2_chunk_dir, info[ :chunks ].shift )
520
+
521
+ begin
522
+ data = IO.binread( path )
523
+ File.delete( path )
524
+ rescue Errno::ENOENT
525
+ add_closing( p2 )
526
+ return
527
+ end
528
+
529
+ caches = []
530
+
531
+ until data.empty?
532
+ app_id, pack_size = data[ 0, 10 ].unpack( 'Q>n' )
533
+ caches << [ app_id, data[ 10, pack_size ] ]
534
+ data = data[ ( 10 + pack_size )..-1 ]
535
+ end
536
+
537
+ app_id, data = caches.shift
538
+ info[ :caches ] = caches
539
+ elsif info[ :wbuffs ].any?
540
+ app_id, data = info[ :wbuffs ].shift
541
+ else
542
+ @writes.delete( p2 )
543
+ return
544
+ end
545
+
546
+ ext = info[ :app_exts ][ app_id ]
547
+
548
+ if ext
549
+ pack_id = ext[ :biggest_pack_id ] + 1
550
+
551
+ if pack_id == 1
552
+ data = @hex.encode( data )
553
+ end
554
+
555
+ pack = "#{ [ app_id, pack_id ].pack( 'Q>Q>' ) }#{ data }"
556
+ send_pack( p2, pack, info[ :p1_addr ] )
557
+ ext[ :biggest_pack_id ] = pack_id
558
+ ext[ :wmems ][ pack_id ] = pack
559
+ ext[ :send_ats ][ pack_id ] = now
560
+ ext[ :last_traffic_at ] = now
561
+ end
562
+ end
563
+
564
+ def new_appd
565
+ appd = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
566
+ appd.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1 )
567
+ appd.setsockopt( Socket::SOL_TCP, Socket::TCP_NODELAY, 1 )
568
+ appd.bind( @appd_sockaddr )
569
+ appd.listen( 511 )
570
+
571
+ @roles[ appd ] = :appd
572
+ add_read( appd )
573
+ end
574
+
575
+ def new_p2
576
+ p2 = Socket.new( Socket::AF_INET, Socket::SOCK_DGRAM, 0 )
577
+ p2.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1 )
578
+ p2.bind( Socket.sockaddr_in( 0, '0.0.0.0' ) )
579
+
580
+ p2_info = {
581
+ wbuffs: [], # 写前缓存 [ app_id, data ]
582
+ caches: [], # 块读出缓存 [ app_id, data ]
583
+ chunks: [], # 块队列 filename
584
+ spring: 0, # 块后缀,结块时,如果块队列不为空,则自增,为空,则置为0
585
+ p1_addr: nil, # 远端地址
586
+ shadow_ids: {}, # shadow_id => app_id
587
+ apps: [], # 开着的app
588
+ app_exts: {}, # 传输相关 app_id => {}
589
+ fin1s: [], # fin1: app已关闭,等待对面收完流量 app_id
590
+ fin2s: [], # fin2: 流量已收完 app_id
591
+ last_coming_at: nil, # 上一次来流量的时间
592
+ paused: false, # 是否暂停写
593
+ resendings: [] # 重传队列 [ app_id, pack_id ]
594
+ }
595
+
596
+ @p2 = p2
597
+ @p2_info = p2_info
598
+ @socks[ p2.object_id ] = p2
599
+ @roles[ p2 ] = :p2
600
+ @infos[ p2 ] = p2_info
601
+
602
+ send_pack( p2, @title, @p2pd_sockaddr )
603
+ add_read( p2 )
604
+ loop_expire( p2 )
605
+ end
606
+
607
+ def loop_expire( p2 )
608
+ Thread.new do
609
+ loop do
610
+ sleep 30
611
+
612
+ break if p2.closed?
613
+
614
+ p2_info = @infos[ p2 ]
615
+
616
+ if p2_info[ :p1_addr ].nil? || ( Time.new - p2_info[ :last_coming_at ] > EXPIRE_AFTER )
617
+ @mutex.synchronize do
618
+ puts "expire p2 #{ p2.object_id } #{ Time.new }"
619
+ @ctlw.write( [ CTL_CLOSE_SOCK, [ p2.object_id ].pack( 'Q>' ) ].join )
620
+ end
621
+ else
622
+ ctlmsg = [ 0, HEARTBEAT, rand( 128 ) ].pack( 'Q>CC' )
623
+ send_pack( p2, ctlmsg, p2_info[ :p1_addr ] )
624
+ end
625
+ end
626
+ end
627
+ end
628
+
629
+ def loop_send_status( p2 )
630
+ Thread.new do
631
+ loop do
632
+ sleep STATUS_INTERVAL
633
+
634
+ if p2.closed?
635
+ # puts "debug p2 is closed, break send status loop #{ Time.new }"
636
+ break
637
+ end
638
+
639
+ p2_info = @infos[ p2 ]
640
+
641
+ if p2_info[ :app_exts ].any?
642
+ @mutex.synchronize do
643
+ now = Time.new
644
+
645
+ p2_info[ :app_exts ].each do | app_id, ext |
646
+ if ext[ :last_traffic_at ] && ( now - ext[ :last_traffic_at ] < SEND_STATUS_UNTIL )
647
+ ctlmsg = [
648
+ 0,
649
+ APP_STATUS,
650
+ app_id,
651
+ ext[ :biggest_pack_id ],
652
+ ext[ :continue_shadow_pack_id ]
653
+ ].pack( 'Q>CQ>Q>Q>' )
654
+
655
+ send_pack( p2, ctlmsg, p2_info[ :p1_addr ] )
656
+ end
657
+ end
658
+ end
659
+ end
660
+
661
+ if p2_info[ :paused ] && ( p2_info[ :app_exts ].map{ | _, ext | ext[ :wmems ].size }.sum < RESUME_BELOW )
662
+ @mutex.synchronize do
663
+ @ctlw.write( [ CTL_RESUME, [ p2.object_id ].pack( 'Q>' ) ].join )
664
+ p2_info[ :paused ] = false
665
+ end
666
+ end
667
+ end
668
+ end
669
+ end
670
+
671
+ def loop_send_fin1( p2, app_id )
672
+ Thread.new do
673
+ 100.times do
674
+ break if p2.closed?
675
+
676
+ p2_info = @infos[ p2 ]
677
+
678
+ unless p2_info[ :fin1s ].include?( app_id )
679
+ # puts "debug break send fin1 loop #{ Time.new }"
680
+ break
681
+ end
682
+
683
+ @mutex.synchronize do
684
+ ctlmsg = [
685
+ 0,
686
+ FIN1,
687
+ app_id
688
+ ].pack( 'Q>CQ>' )
689
+
690
+ # puts "debug send FIN1 #{ app_id } #{ Time.new }"
691
+ send_pack( p2, ctlmsg, p2_info[ :p1_addr ] )
692
+ end
693
+
694
+ sleep 1
695
+ end
696
+ end
697
+ end
698
+
699
+ def loop_send_fin2( p2, app_id )
700
+ Thread.new do
701
+ 100.times do
702
+ break if p2.closed?
703
+
704
+ p2_info = @infos[ p2 ]
705
+
706
+ unless p2_info[ :fin2s ].include?( app_id )
707
+ # puts "debug break send fin2 loop #{ Time.new }"
708
+ break
709
+ end
710
+
711
+ @mutex.synchronize do
712
+ ctlmsg = [
713
+ 0,
714
+ FIN2,
715
+ app_id
716
+ ].pack( 'Q>CQ>' )
717
+
718
+ # puts "debug send FIN2 #{ app_id } #{ Time.new }"
719
+ send_pack( p2, ctlmsg, p2_info[ :p1_addr ] )
720
+ end
721
+
722
+ sleep 1
723
+ end
724
+ end
725
+ end
726
+
727
+ def loop_send_a_new_app( app )
728
+ Thread.new do
729
+ 100.times do
730
+ break if app.closed?
731
+
732
+ app_info = @infos[ app ]
733
+ p2 = app_info[ :p2 ]
734
+ break if p2.closed?
735
+
736
+ p2_info = @infos[ p2 ]
737
+
738
+ if p2_info[ :p1_addr ]
739
+ ext = p2_info[ :app_exts ][ app.object_id ]
740
+
741
+ if ext.nil? || ext[ :shadow_id ]
742
+ # puts "debug break a new app loop #{ Time.new }"
743
+ break
744
+ end
745
+
746
+ @mutex.synchronize do
747
+ ctlmsg = [ 0, A_NEW_APP, app.object_id ].pack( 'Q>CQ>' )
748
+ # puts "debug send a new app #{ Time.new }"
749
+ send_pack( p2, ctlmsg, p2_info[ :p1_addr ] )
750
+ end
751
+ end
752
+
753
+ sleep 1
754
+ end
755
+ end
756
+ end
757
+
758
+ def send_pack( sock, data, target_sockaddr )
759
+ begin
760
+ sock.sendmsg( data, 0, target_sockaddr )
761
+ rescue IO::WaitWritable, Errno::EINTR => e
762
+ puts "sendmsg #{ e.class } #{ Time.new }"
763
+ end
764
+ end
765
+
766
+ def add_read( sock )
767
+ return if sock.closed? || @reads.include?( sock )
768
+
769
+ @reads << sock
770
+ end
771
+
772
+ def add_write( sock, data = nil )
773
+ return if sock.closed? || @writes.include?( sock )
774
+
775
+ @writes << sock
776
+ end
777
+
778
+ def add_closing( sock )
779
+ return if sock.closed? || @closings.include?( sock )
780
+
781
+ @reads.delete( sock )
782
+ @closings << sock
783
+ add_write( sock )
784
+ end
785
+
786
+ def close_p2( p2 )
787
+ info = close_sock( p2 )
788
+ info[ :apps ].each { | app | add_closing( app ) }
789
+ end
790
+
791
+ def close_app( app )
792
+ info = close_sock( app )
793
+ p2 = info[ :p2 ]
794
+ return if p2.closed?
795
+
796
+ p2_info = @infos[ p2 ]
797
+ p2_info[ :apps ].delete( app )
798
+ app_id = app.object_id
799
+ ext = p2_info[ :app_exts ][ app_id ]
800
+ return unless ext
801
+
802
+ if ext[ :is_shadow_closed ]
803
+ # puts "debug 2-3. app.close -> ext.is_shadow_closed ? yes -> del ext -> loop send fin2 #{ Time.new }"
804
+ p2_info[ :shadow_ids ].delete( ext[ :shadow_id ] )
805
+ p2_info[ :app_exts ].delete( app_id )
806
+
807
+ unless p2_info[ :fin2s ].include?( app_id )
808
+ p2_info[ :fin2s ] << app_id
809
+ loop_send_fin2( p2, app_id )
810
+ end
811
+ else
812
+ # puts "debug 1-1. app.close -> ext.is_shadow_closed ? no -> send fin1 loop #{ Time.new }"
813
+
814
+ if p2_info[ :p1_addr ] && !p2_info[ :fin1s ].include?( app_id )
815
+ p2_info[ :fin1s ] << app_id
816
+ loop_send_fin1( p2, app_id )
817
+ end
818
+ end
819
+ end
820
+
821
+ def close_sock( sock )
822
+ sock.close
823
+ @reads.delete( sock )
824
+ @writes.delete( sock )
825
+ @closings.delete( sock )
826
+ @socks.delete( sock.object_id )
827
+ role = @roles.delete( sock )
828
+ info = @infos.delete( sock )
829
+
830
+ if info
831
+ chunk_dir = ( role == :app ? @app_chunk_dir : @p2_chunk_dir )
832
+
833
+ info[ :chunks ].each do | filename |
834
+ begin
835
+ File.delete( File.join( chunk_dir, filename ) )
836
+ rescue Errno::ENOENT
837
+ end
838
+ end
839
+ end
840
+
841
+ info
842
+ end
843
+
844
+ end
845
+ end