p2p2 0.11.0 → 0.12.0

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