p2p2 0.8.0 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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