p2p2 0.13.3 → 0.14.0

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