p2p2 0.15.1 → 0.16.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.
@@ -0,0 +1,7 @@
1
+ require 'p2p2/custom'
2
+
3
+ module P2p2
4
+ class P1Custom
5
+ include Custom
6
+ end
7
+ end
@@ -0,0 +1,915 @@
1
+ module P2p2
2
+ class P1Worker
3
+
4
+ ##
5
+ # initialize
6
+ #
7
+ def initialize( p2pd_host, p2pd_port, room, appd_host, appd_port, dst_chunk_dir, tund_chunk_dir )
8
+ @p2pd_addr = Socket.sockaddr_in( p2pd_port, p2pd_host )
9
+ @room = room
10
+ @appd_addr = Socket.sockaddr_in( appd_port, appd_host )
11
+ @dst_chunk_dir = dst_chunk_dir
12
+ @tund_chunk_dir = tund_chunk_dir
13
+ @custom = P2p2::P1Custom.new
14
+ @mutex = Mutex.new
15
+ @reads = []
16
+ @writes = []
17
+ @roles = {} # sock => :dotr / :dst / :tund
18
+ @dst_infos = {} # dst => {}
19
+
20
+ dotr, dotw = IO.pipe
21
+ @dotw = dotw
22
+ add_read( dotr, :dotr )
23
+ new_a_tund
24
+ end
25
+
26
+ ##
27
+ # looping
28
+ #
29
+ def looping
30
+ puts "#{ Time.new } looping"
31
+ loop_update_room
32
+ loop_check_expire
33
+ loop_check_status
34
+
35
+ loop do
36
+ rs, ws = IO.select( @reads, @writes )
37
+
38
+ @mutex.synchronize do
39
+ # 先写,再读
40
+ ws.each do | sock |
41
+ case @roles[ sock ]
42
+ when :dst
43
+ write_dst( sock )
44
+ when :tund
45
+ write_tund( sock )
46
+ end
47
+ end
48
+
49
+ rs.each do | sock |
50
+ case @roles[ sock ]
51
+ when :dotr
52
+ read_dotr( sock )
53
+ when :dst
54
+ read_dst( sock )
55
+ when :tund
56
+ read_tund( sock )
57
+ end
58
+ end
59
+ end
60
+ end
61
+ rescue Interrupt => e
62
+ puts e.class
63
+ quit!
64
+ end
65
+
66
+ ##
67
+ # quit!
68
+ #
69
+ def quit!
70
+ if !@tund.closed? && @tund_info[ :tun_addr ]
71
+ # puts "debug1 send tund fin"
72
+ data = [ 0, TUND_FIN ].pack( 'Q>C' )
73
+ @tund.sendmsg( data, 0, @tund_info[ :tun_addr ] )
74
+ end
75
+
76
+ # puts "debug1 exit"
77
+ exit
78
+ end
79
+
80
+ private
81
+
82
+ ##
83
+ # loop update room
84
+ #
85
+ def loop_update_room
86
+ Thread.new do
87
+ loop do
88
+ sleep UPDATE_ROOM_INTERVAL
89
+
90
+ @mutex.synchronize do
91
+ if !@tund.closed? && @tund_info[ :peer_addr ].nil?
92
+ add_tund_ctlmsg( @room, @p2pd_addr )
93
+ next_tick
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ ##
101
+ # loop check expire
102
+ #
103
+ def loop_check_expire
104
+ Thread.new do
105
+ loop do
106
+ sleep CHECK_EXPIRE_INTERVAL
107
+
108
+ @mutex.synchronize do
109
+ need_trigger = false
110
+ now = Time.new
111
+
112
+ if !@tund.closed? && @tund_info[ :tun_addr ]
113
+ if now - @tund_info[ :last_recv_at ] > EXPIRE_AFTER
114
+ puts "#{ Time.new } expire tund"
115
+ set_is_closing( @tund )
116
+ else
117
+ # puts "debug1 #{ Time.new } heartbeat"
118
+ add_tund_ctlmsg( pack_a_heartbeat )
119
+
120
+ @tund_info[ :dst_exts ].each do | dst_local_port, dst_ext |
121
+ if dst_ext[ :dst ].closed? && ( now - dst_ext[ :last_continue_at ] > EXPIRE_AFTER )
122
+ puts "#{ Time.new } expire dst ext #{ dst_local_port }"
123
+ del_dst_ext( dst_local_port )
124
+ end
125
+ end
126
+ end
127
+
128
+ need_trigger = true
129
+ end
130
+
131
+ @dst_infos.each do | dst, dst_info |
132
+ is_expired = dst_info[ :last_recv_at ].nil? && ( now - dst_info[ :created_at ] > EXPIRE_NEW )
133
+
134
+ if is_expired
135
+ puts "#{ Time.new } expire dst"
136
+ set_is_closing( dst )
137
+ need_trigger = true
138
+ end
139
+ end
140
+
141
+ if need_trigger
142
+ next_tick
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end
148
+
149
+ ##
150
+ # loop check status
151
+ #
152
+ def loop_check_status
153
+ Thread.new do
154
+ loop do
155
+ sleep STATUS_INTERVAL
156
+
157
+ @mutex.synchronize do
158
+ if !@tund.closed? && @tund_info[ :tun_addr ]
159
+ need_trigger = false
160
+
161
+ if @tund_info[ :dst_exts ].any?
162
+ now = Time.new
163
+
164
+ @tund_info[ :dst_exts ].each do | dst_local_port, dst_ext |
165
+ if now - dst_ext[ :last_continue_at ] < SEND_STATUS_UNTIL
166
+ data = [ 0, DEST_STATUS, dst_local_port, dst_ext[ :relay_pack_id ], dst_ext[ :continue_src_pack_id ] ].pack( 'Q>CnQ>Q>' )
167
+ add_tund_ctlmsg( data )
168
+ need_trigger = true
169
+ end
170
+ end
171
+ end
172
+
173
+ if @tund_info[ :paused ] && ( @tund_info[ :dst_exts ].map{ | _, dst_ext | dst_ext[ :wmems ].size }.sum < RESUME_BELOW )
174
+ puts "#{ Time.new } resume tund"
175
+ @tund_info[ :paused ] = false
176
+ add_write( @tund )
177
+ need_trigger = true
178
+ end
179
+
180
+ if need_trigger
181
+ next_tick
182
+ end
183
+ end
184
+ end
185
+ end
186
+ end
187
+ end
188
+
189
+ ##
190
+ # loop punch peer
191
+ #
192
+ def loop_punch_peer
193
+ Thread.new do
194
+ EXPIRE_NEW.times do
195
+ if @tund.closed?
196
+ # puts "debug1 break loop punch peer"
197
+ break
198
+ end
199
+
200
+ @mutex.synchronize do
201
+ # puts "debug1 punch peer"
202
+ add_tund_ctlmsg( pack_a_heartbeat, @tund_info[ :peer_addr ] )
203
+ next_tick
204
+ end
205
+
206
+ sleep 1
207
+ end
208
+ end
209
+ end
210
+
211
+ ##
212
+ # new a tund
213
+ #
214
+ def new_a_tund
215
+ tund = Socket.new( Socket::AF_INET, Socket::SOCK_DGRAM, 0 )
216
+ tund.bind( Socket.sockaddr_in( 0, '0.0.0.0' ) )
217
+ port = tund.local_address.ip_port
218
+ puts "#{ Time.new } tund bind on #{ port }"
219
+
220
+ tund_info = {
221
+ port: port, # 端口
222
+ ctlmsgs: [], # [ to_addr, data ]
223
+ wbuffs: [], # 写前缓存 [ dst_local_port, pack_id, data ]
224
+ caches: [], # 块读出缓存 [ dst_local_port, pack_id, data ]
225
+ chunks: [], # 块队列 filename
226
+ spring: 0, # 块后缀,结块时,如果块队列不为空,则自增,为空,则置为0
227
+ peer_addr: nil, # 对面地址
228
+ tun_addr: nil, # 连通后的tun地址
229
+ dst_exts: {}, # dst额外信息 dst_local_port => {}
230
+ dst_local_ports: {}, # src_id => dst_local_port
231
+ paused: false, # 是否暂停写
232
+ resendings: [], # 重传队列 [ dst_local_port, pack_id ]
233
+ created_at: Time.new, # 创建时间
234
+ last_recv_at: nil, # 上一次收到流量的时间,过期关闭
235
+ is_closing: false # 是否准备关闭
236
+ }
237
+
238
+ @tund = tund
239
+ @tund_info = tund_info
240
+ add_read( tund, :tund )
241
+ add_tund_ctlmsg( @room, @p2pd_addr )
242
+ end
243
+
244
+ ##
245
+ # pack a heartbeat
246
+ #
247
+ def pack_a_heartbeat
248
+ [ 0, HEARTBEAT, rand( 128 ) ].pack( 'Q>CC' )
249
+ end
250
+
251
+ ##
252
+ # is match tun addr
253
+ #
254
+ def is_match_tun_addr( addrinfo )
255
+ from_addr = addrinfo.to_sockaddr
256
+
257
+ if from_addr != @tund_info[ :tun_addr ]
258
+ puts "#{ Time.new } #{ addrinfo.inspect } not match #{ Addrinfo.new( @tund_info[ :tun_addr ] ).inspect }"
259
+ return false
260
+ end
261
+
262
+ @tund_info[ :last_recv_at ] = Time.new
263
+ true
264
+ end
265
+
266
+ ##
267
+ # add tund ctlmsg
268
+ #
269
+ def add_tund_ctlmsg( data, to_addr = nil )
270
+ unless to_addr
271
+ to_addr = @tund_info[ :tun_addr ]
272
+ end
273
+
274
+ if to_addr
275
+ @tund_info[ :ctlmsgs ] << [ to_addr, data ]
276
+ add_write( @tund )
277
+ end
278
+ end
279
+
280
+ ##
281
+ # add tund wbuff
282
+ #
283
+ def add_tund_wbuff( dst_local_port, pack_id, data )
284
+ @tund_info[ :wbuffs ] << [ dst_local_port, pack_id, data ]
285
+
286
+ if @tund_info[ :wbuffs ].size >= WBUFFS_LIMIT
287
+ spring = @tund_info[ :chunks ].size > 0 ? ( @tund_info[ :spring ] + 1 ) : 0
288
+ filename = "#{ Process.pid }-#{ @tund_info[ :port ] }.#{ spring }"
289
+ chunk_path = File.join( @tund_chunk_dir, filename )
290
+ datas = @tund_info[ :wbuffs ].map{ | _dst_local_port, _pack_id, _data | [ [ _dst_local_port, _pack_id, _data.bytesize ].pack( 'nQ>n' ), _data ].join }
291
+
292
+ begin
293
+ IO.binwrite( chunk_path, datas.join )
294
+ rescue Errno::ENOSPC => e
295
+ puts "#{ Time.new } #{ e.class }, close tund"
296
+ set_is_closing( @tund )
297
+ return
298
+ end
299
+
300
+ @tund_info[ :chunks ] << filename
301
+ @tund_info[ :spring ] = spring
302
+ @tund_info[ :wbuffs ].clear
303
+ end
304
+
305
+ add_write( @tund )
306
+ end
307
+
308
+ ##
309
+ # add dst wbuff
310
+ #
311
+ def add_dst_wbuff( dst, data )
312
+ dst_info = @dst_infos[ dst ]
313
+ dst_info[ :wbuff ] << data
314
+
315
+ if dst_info[ :wbuff ].bytesize >= CHUNK_SIZE
316
+ spring = dst_info[ :chunks ].size > 0 ? ( dst_info[ :spring ] + 1 ) : 0
317
+ filename = "#{ Process.pid }-#{ dst_info[ :local_port ] }.#{ spring }"
318
+ chunk_path = File.join( @dst_chunk_dir, filename )
319
+
320
+ begin
321
+ IO.binwrite( chunk_path, dst_info[ :wbuff ] )
322
+ rescue Errno::ENOSPC => e
323
+ puts "#{ Time.new } #{ e.class }, close dst"
324
+ set_is_closing( dst )
325
+ return
326
+ end
327
+
328
+ dst_info[ :chunks ] << filename
329
+ dst_info[ :spring ] = spring
330
+ dst_info[ :wbuff ].clear
331
+ end
332
+
333
+ add_write( dst )
334
+ end
335
+
336
+ ##
337
+ # add read
338
+ #
339
+ def add_read( sock, role )
340
+ unless @reads.include?( sock )
341
+ @reads << sock
342
+ end
343
+
344
+ @roles[ sock ] = role
345
+ end
346
+
347
+ ##
348
+ # add write
349
+ #
350
+ def add_write( sock )
351
+ if sock && !sock.closed? && !@writes.include?( sock )
352
+ @writes << sock
353
+ end
354
+ end
355
+
356
+ ##
357
+ # set is closing
358
+ #
359
+ def set_is_closing( sock )
360
+ if sock && !sock.closed?
361
+ role = @roles[ sock ]
362
+ # puts "debug1 set #{ role.to_s } is closing"
363
+
364
+ case role
365
+ when :dst
366
+ dst_info = @dst_infos[ sock ]
367
+ dst_info[ :is_closing ] = true
368
+ when :tund
369
+ @tund_info[ :is_closing ] = true
370
+ end
371
+
372
+ @reads.delete( sock )
373
+ add_write( sock )
374
+ end
375
+ end
376
+
377
+ ##
378
+ # close dst
379
+ #
380
+ def close_dst( dst )
381
+ # puts "debug1 close dst"
382
+ close_sock( dst )
383
+ dst_info = @dst_infos.delete( dst )
384
+
385
+ dst_info[ :chunks ].each do | filename |
386
+ begin
387
+ File.delete( File.join( @dst_chunk_dir, filename ) )
388
+ rescue Errno::ENOENT
389
+ end
390
+ end
391
+
392
+ return if @tund.closed?
393
+
394
+ local_port = dst_info[ :local_port ]
395
+ dst_ext = @tund_info[ :dst_exts ][ local_port ]
396
+ return unless dst_ext
397
+
398
+ if dst_ext[ :is_src_closed ]
399
+ # puts "debug1 4-3. after close dst -> src closed ? yes -> del dst ext -> send fin2"
400
+ del_dst_ext( local_port )
401
+ data = [ 0, FIN2, local_port ].pack( 'Q>Cn' )
402
+ add_tund_ctlmsg( data )
403
+ else
404
+ # puts "debug1 3-1. after close dst -> src closed ? no -> send fin1"
405
+ data = [ 0, FIN1, local_port, dst_info[ :biggest_pack_id ], dst_ext[ :continue_src_pack_id ] ].pack( 'Q>CnQ>Q>' )
406
+ add_tund_ctlmsg( data )
407
+ end
408
+ end
409
+
410
+ ##
411
+ # close tund
412
+ #
413
+ def close_tund( tund )
414
+ # puts "debug1 close tund"
415
+ close_sock( tund )
416
+
417
+ @tund_info[ :chunks ].each do | filename |
418
+ begin
419
+ File.delete( File.join( @tund_chunk_dir, filename ) )
420
+ rescue Errno::ENOENT
421
+ end
422
+ end
423
+
424
+ @tund_info[ :dst_exts ].each{ | _, dst_ext | set_is_closing( dst_ext[ :dst ] ) }
425
+ end
426
+
427
+ ##
428
+ # close sock
429
+ #
430
+ def close_sock( sock )
431
+ sock.close
432
+ @reads.delete( sock )
433
+ @writes.delete( sock )
434
+ @roles.delete( sock )
435
+ end
436
+
437
+ ##
438
+ # del dst ext
439
+ #
440
+ def del_dst_ext( dst_local_port )
441
+ dst_ext = @tund_info[ :dst_exts ].delete( dst_local_port )
442
+
443
+ if dst_ext
444
+ @tund_info[ :dst_local_ports ].delete( dst_ext[ :src_id ] )
445
+ end
446
+ end
447
+
448
+ ##
449
+ # release wmems
450
+ #
451
+ def release_wmems( dst_ext, completed_pack_id )
452
+ if completed_pack_id > dst_ext[ :completed_pack_id ]
453
+ # puts "debug2 update completed pack #{ completed_pack_id }"
454
+
455
+ pack_ids = dst_ext[ :wmems ].keys.select { | pack_id | pack_id <= completed_pack_id }
456
+
457
+ pack_ids.each do | pack_id |
458
+ dst_ext[ :wmems ].delete( pack_id )
459
+ dst_ext[ :send_ats ].delete( pack_id )
460
+ end
461
+
462
+ dst_ext[ :completed_pack_id ] = completed_pack_id
463
+ end
464
+ end
465
+
466
+ ##
467
+ # next tick
468
+ #
469
+ def next_tick
470
+ @dotw.write( '.' )
471
+ end
472
+
473
+ ##
474
+ # write dst
475
+ #
476
+ def write_dst( dst )
477
+ dst_info = @dst_infos[ dst ]
478
+ data = dst_info[ :cache ]
479
+ from = :cache
480
+
481
+ if data.empty?
482
+ if dst_info[ :chunks ].any?
483
+ path = File.join( @dst_chunk_dir, dst_info[ :chunks ].shift )
484
+
485
+ begin
486
+ dst_info[ :cache ] = data = IO.binread( path )
487
+ File.delete( path )
488
+ rescue Errno::ENOENT => e
489
+ puts "#{ Time.new } read #{ path } #{ e.class }"
490
+ close_dst( dst )
491
+ return
492
+ end
493
+ else
494
+ data = dst_info[ :wbuff ]
495
+ from = :wbuff
496
+ end
497
+ end
498
+
499
+ if data.empty?
500
+ if dst_info[ :is_closing ]
501
+ close_dst( dst )
502
+ else
503
+ @writes.delete( dst )
504
+ end
505
+
506
+ return
507
+ end
508
+
509
+ begin
510
+ written = dst.write_nonblock( data )
511
+ rescue IO::WaitWritable, Errno::EINTR
512
+ return
513
+ rescue Exception => e
514
+ # puts "debug1 write dst #{ e.class }"
515
+ close_dst( dst )
516
+ return
517
+ end
518
+
519
+ # puts "debug2 write dst #{ written }"
520
+ data = data[ written..-1 ]
521
+ dst_info[ from ] = data
522
+ end
523
+
524
+ ##
525
+ # write tund
526
+ #
527
+ def write_tund( tund )
528
+ if @tund_info[ :is_closing ]
529
+ close_tund( tund )
530
+ new_a_tund
531
+ return
532
+ end
533
+
534
+ # 传ctlmsg
535
+ while @tund_info[ :ctlmsgs ].any?
536
+ to_addr, data = @tund_info[ :ctlmsgs ].first
537
+
538
+ begin
539
+ tund.sendmsg( data, 0, to_addr )
540
+ rescue IO::WaitWritable, Errno::EINTR
541
+ return
542
+ end
543
+
544
+ @tund_info[ :ctlmsgs ].shift
545
+ end
546
+
547
+ # 重传
548
+ while @tund_info[ :resendings ].any?
549
+ dst_local_port, pack_id = @tund_info[ :resendings ].first
550
+ dst_ext = @tund_info[ :dst_exts ][ dst_local_port ]
551
+
552
+ if dst_ext
553
+ data = dst_ext[ :wmems ][ pack_id ]
554
+
555
+ if data
556
+ begin
557
+ tund.sendmsg( data, 0, @tund_info[ :tun_addr ] )
558
+ rescue IO::WaitWritable, Errno::EINTR
559
+ return
560
+ end
561
+ end
562
+ end
563
+
564
+ @tund_info[ :resendings ].shift
565
+ return
566
+ end
567
+
568
+ # 若写后达到上限,暂停取写前
569
+ if @tund_info[ :dst_exts ].map{ | _, dst_ext | dst_ext[ :wmems ].size }.sum >= WMEMS_LIMIT
570
+ unless @tund_info[ :paused ]
571
+ puts "#{ Time.new } pause tund #{ @tund_info[ :port ] }"
572
+ @tund_info[ :paused ] = true
573
+ end
574
+
575
+ @writes.delete( tund )
576
+ return
577
+ end
578
+
579
+ # 取写前
580
+ if @tund_info[ :caches ].any?
581
+ dst_local_port, pack_id, data = @tund_info[ :caches ].first
582
+ from = :caches
583
+ elsif @tund_info[ :chunks ].any?
584
+ path = File.join( @tund_chunk_dir, @tund_info[ :chunks ].shift )
585
+
586
+ begin
587
+ data = IO.binread( path )
588
+ File.delete( path )
589
+ rescue Errno::ENOENT => e
590
+ puts "#{ Time.new } read #{ path } #{ e.class }"
591
+ close_tund( tund )
592
+ return
593
+ end
594
+
595
+ caches = []
596
+
597
+ until data.empty?
598
+ _dst_local_port, _pack_id, pack_size = data[ 0, 12 ].unpack( 'nQ>n' )
599
+ caches << [ _dst_local_port, _pack_id, data[ 12, pack_size ] ]
600
+ data = data[ ( 12 + pack_size )..-1 ]
601
+ end
602
+
603
+ @tund_info[ :caches ] = caches
604
+ dst_local_port, pack_id, data = caches.first
605
+ from = :caches
606
+ elsif @tund_info[ :wbuffs ].any?
607
+ dst_local_port, pack_id, data = @tund_info[ :wbuffs ].first
608
+ from = :wbuffs
609
+ else
610
+ @writes.delete( tund )
611
+ return
612
+ end
613
+
614
+ dst_ext = @tund_info[ :dst_exts ][ dst_local_port ]
615
+
616
+ if dst_ext
617
+ if pack_id <= CONFUSE_UNTIL
618
+ data = @custom.encode( data )
619
+ # puts "debug1 encoded pack #{ pack_id }"
620
+ end
621
+
622
+ data = [ [ pack_id, dst_local_port ].pack( 'Q>n' ), data ].join
623
+
624
+ begin
625
+ tund.sendmsg( data, 0, @tund_info[ :tun_addr ] )
626
+ rescue IO::WaitWritable, Errno::EINTR
627
+ return
628
+ end
629
+
630
+ # puts "debug2 written pack #{ pack_id }"
631
+ now = Time.new
632
+ dst_ext[ :relay_pack_id ] = pack_id
633
+ dst_ext[ :wmems ][ pack_id ] = data
634
+ dst_ext[ :send_ats ][ pack_id ] = now
635
+ dst_ext[ :last_continue_at ] = now
636
+ end
637
+
638
+ @tund_info[ from ].shift
639
+ end
640
+
641
+ ##
642
+ # read dotr
643
+ #
644
+ def read_dotr( dotr )
645
+ dotr.read( 1 )
646
+ end
647
+
648
+ ##
649
+ # read dst
650
+ #
651
+ def read_dst( dst )
652
+ begin
653
+ data = dst.read_nonblock( PACK_SIZE )
654
+ rescue IO::WaitReadable, Errno::EINTR
655
+ return
656
+ rescue Exception => e
657
+ # puts "debug1 read dst #{ e.class }"
658
+ set_is_closing( dst )
659
+ return
660
+ end
661
+
662
+ # puts "debug2 read dst #{ data.inspect }"
663
+ dst_info = @dst_infos[ dst ]
664
+ dst_info[ :last_recv_at ] = Time.new
665
+
666
+ if @tund.closed?
667
+ puts "#{ Time.new } tund closed, close dst"
668
+ set_is_closing( dst )
669
+ return
670
+ end
671
+
672
+ pack_id = dst_info[ :biggest_pack_id ] + 1
673
+ dst_info[ :biggest_pack_id ] = pack_id
674
+ add_tund_wbuff( dst_info[ :local_port ], pack_id, data )
675
+ end
676
+
677
+ ##
678
+ # read tund
679
+ #
680
+ def read_tund( tund )
681
+ data, addrinfo, rflags, *controls = tund.recvmsg
682
+ now = Time.new
683
+ pack_id = data[ 0, 8 ].unpack( 'Q>' ).first
684
+
685
+ if pack_id == 0
686
+ ctl_num = data[ 8 ].unpack( 'C' ).first
687
+
688
+ case ctl_num
689
+ when PEER_ADDR
690
+ return if @tund_info[ :peer_addr ] || ( addrinfo.to_sockaddr != @p2pd_addr )
691
+
692
+ peer_addr = data[ 9..-1 ]
693
+ puts "#{ Time.new } got peer addr #{ Addrinfo.new( peer_addr ).inspect }"
694
+
695
+ @tund_info[ :peer_addr ] = peer_addr
696
+ loop_punch_peer
697
+ when HEARTBEAT
698
+ from_addr = addrinfo.to_sockaddr
699
+ return if from_addr != @tund_info[ :peer_addr ]
700
+
701
+ # puts "debug1 set tun addr #{ Addrinfo.new( from_addr ).inspect }"
702
+ @tund_info[ :tun_addr ] = from_addr
703
+ @tund_info[ :last_recv_at ] = now
704
+ when A_NEW_SOURCE
705
+ return unless is_match_tun_addr( addrinfo )
706
+
707
+ src_id = data[ 9, 8 ].unpack( 'Q>' ).first
708
+ dst_local_port = @tund_info[ :dst_local_ports ][ src_id ]
709
+ # puts "debug1 got a new source #{ src_id }"
710
+
711
+ if dst_local_port
712
+ dst_ext = @tund_info[ :dst_exts ][ dst_local_port ]
713
+ return unless dst_ext
714
+
715
+ if dst_ext[ :dst ].closed?
716
+ dst_local_port = 0
717
+ end
718
+ else
719
+ dst = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
720
+
721
+ if RUBY_PLATFORM.include?( 'linux' )
722
+ dst.setsockopt( Socket::SOL_TCP, Socket::TCP_NODELAY, 1 )
723
+ end
724
+
725
+ begin
726
+ dst.connect_nonblock( @appd_addr )
727
+ rescue IO::WaitWritable
728
+ rescue Exception => e
729
+ puts "#{ Time.new } connect appd #{ e.class }"
730
+ return
731
+ end
732
+
733
+ dst_local_port = dst.local_address.ip_port
734
+
735
+ @dst_infos[ dst ] = {
736
+ local_port: dst_local_port, # 本地端口
737
+ biggest_pack_id: 0, # 最大包号码
738
+ wbuff: '', # 写前
739
+ cache: '', # 块读出缓存
740
+ chunks: [], # 块队列,写前达到块大小时结一个块 filename
741
+ spring: 0, # 块后缀,结块时,如果块队列不为空,则自增,为空,则置为0
742
+ created_at: Time.new, # 创建时间
743
+ last_recv_at: nil, # 上一次收到流量的时间,过期关闭
744
+ is_closing: false # 是否准备关闭
745
+ }
746
+ add_read( dst, :dst )
747
+
748
+ @tund_info[ :dst_local_ports ][ src_id ] = dst_local_port
749
+ @tund_info[ :dst_exts ][ dst_local_port ] = {
750
+ dst: dst, # dst
751
+ src_id: src_id, # 近端src id
752
+ wmems: {}, # 写后 pack_id => data
753
+ send_ats: {}, # 上一次发出时间 pack_id => send_at
754
+ relay_pack_id: 0, # 转发到几
755
+ continue_src_pack_id: 0, # 收到几
756
+ pieces: {}, # 跳号包 src_pack_id => data
757
+ is_src_closed: false, # src是否已关闭
758
+ biggest_src_pack_id: 0, # src最大包号码
759
+ completed_pack_id: 0, # 完成到几(对面收到几)
760
+ last_continue_at: Time.new # 创建,或者上一次收到连续流量,或者发出新包的时间
761
+ }
762
+ end
763
+
764
+ data2 = [ 0, PAIRED, src_id, dst_local_port ].pack( 'Q>CQ>n' )
765
+ # puts "debug1 add ctlmsg paired #{ data2.inspect }"
766
+ add_tund_ctlmsg( data2 )
767
+ when SOURCE_STATUS
768
+ return unless is_match_tun_addr( addrinfo )
769
+
770
+ src_id, relay_src_pack_id, continue_dst_pack_id = data[ 9, 24 ].unpack( 'Q>Q>Q>' )
771
+
772
+ dst_local_port = @tund_info[ :dst_local_ports ][ src_id ]
773
+ return unless dst_local_port
774
+
775
+ dst_ext = @tund_info[ :dst_exts ][ dst_local_port ]
776
+ return unless dst_ext
777
+
778
+ # puts "debug2 got source status"
779
+
780
+ release_wmems( dst_ext, continue_dst_pack_id )
781
+
782
+ # 发miss
783
+ if !dst_ext[ :dst ].closed? && ( dst_ext[ :continue_src_pack_id ] < relay_src_pack_id )
784
+ ranges = []
785
+ curr_pack_id = dst_ext[ :continue_src_pack_id ] + 1
786
+
787
+ dst_ext[ :pieces ].keys.sort.each do | pack_id |
788
+ if pack_id > curr_pack_id
789
+ ranges << [ curr_pack_id, pack_id - 1 ]
790
+ end
791
+
792
+ curr_pack_id = pack_id + 1
793
+ end
794
+
795
+ if curr_pack_id <= relay_src_pack_id
796
+ ranges << [ curr_pack_id, relay_src_pack_id ]
797
+ end
798
+
799
+ pack_count = 0
800
+ # puts "debug1 continue/relay #{ dst_ext[ :continue_src_pack_id ] }/#{ relay_src_pack_id } send MISS #{ ranges.size }"
801
+
802
+ ranges.each do | pack_id_begin, pack_id_end |
803
+ if pack_count >= BREAK_SEND_MISS
804
+ puts "#{ Time.new } break send miss at #{ pack_id_begin }"
805
+ break
806
+ end
807
+
808
+ data2 = [ 0, MISS, src_id, pack_id_begin, pack_id_end ].pack( 'Q>CQ>Q>Q>' )
809
+ add_tund_ctlmsg( data2 )
810
+ pack_count += ( pack_id_end - pack_id_begin + 1 )
811
+ end
812
+ end
813
+ when MISS
814
+ return unless is_match_tun_addr( addrinfo )
815
+
816
+ dst_local_port, pack_id_begin, pack_id_end = data[ 9, 18 ].unpack( 'nQ>Q>' )
817
+
818
+ dst_ext = @tund_info[ :dst_exts ][ dst_local_port ]
819
+ return unless dst_ext
820
+
821
+ ( pack_id_begin..pack_id_end ).each do | pack_id |
822
+ send_at = dst_ext[ :send_ats ][ pack_id ]
823
+
824
+ if send_at
825
+ break if now - send_at < STATUS_INTERVAL
826
+ @tund_info[ :resendings ] << [ dst_local_port, pack_id ]
827
+ end
828
+ end
829
+
830
+ add_write( tund )
831
+ when FIN1
832
+ return unless is_match_tun_addr( addrinfo )
833
+
834
+ src_id, biggest_src_pack_id, continue_dst_pack_id = data[ 9, 24 ].unpack( 'Q>Q>Q>' )
835
+
836
+ dst_local_port = @tund_info[ :dst_local_ports ][ src_id ]
837
+ return unless dst_local_port
838
+
839
+ dst_ext = @tund_info[ :dst_exts ][ dst_local_port ]
840
+ return unless dst_ext
841
+
842
+ # puts "debug1 got fin1 #{ src_id } biggest src pack #{ biggest_src_pack_id } completed dst pack #{ continue_dst_pack_id }"
843
+ dst_ext[ :is_src_closed ] = true
844
+ dst_ext[ :biggest_src_pack_id ] = biggest_src_pack_id
845
+ release_wmems( dst_ext, continue_dst_pack_id )
846
+
847
+ if biggest_src_pack_id == dst_ext[ :continue_src_pack_id ]
848
+ # puts "debug1 4-1. tund recv fin1 -> all traffic received ? -> close dst after write"
849
+ set_is_closing( dst_ext[ :dst ] )
850
+ end
851
+ when FIN2
852
+ return unless is_match_tun_addr( addrinfo )
853
+
854
+ src_id = data[ 9, 8 ].unpack( 'Q>' ).first
855
+
856
+ dst_local_port = @tund_info[ :dst_local_ports ][ src_id ]
857
+ return unless dst_local_port
858
+
859
+ # puts "debug1 3-2. tund recv fin2 -> del dst ext"
860
+ del_dst_ext( dst_local_port )
861
+ when TUN_FIN
862
+ return unless is_match_tun_addr( addrinfo )
863
+
864
+ puts "#{ Time.new } recv tun fin"
865
+ set_is_closing( tund )
866
+ end
867
+
868
+ return
869
+ end
870
+
871
+ return unless is_match_tun_addr( addrinfo )
872
+
873
+ src_id = data[ 8, 8 ].unpack( 'Q>' ).first
874
+
875
+ dst_local_port = @tund_info[ :dst_local_ports ][ src_id ]
876
+ return unless dst_local_port
877
+
878
+ dst_ext = @tund_info[ :dst_exts ][ dst_local_port ]
879
+ return if dst_ext.nil? || dst_ext[ :dst ].closed?
880
+ return if ( pack_id <= dst_ext[ :continue_src_pack_id ] ) || dst_ext[ :pieces ].include?( pack_id )
881
+
882
+ data = data[ 16..-1 ]
883
+ # puts "debug2 got pack #{ pack_id }"
884
+
885
+ if pack_id <= CONFUSE_UNTIL
886
+ # puts "debug2 #{ data.inspect }"
887
+ data = @custom.decode( data )
888
+ # puts "debug1 decoded pack #{ pack_id }"
889
+ end
890
+
891
+ # 放进写前,跳号放碎片缓存
892
+ if pack_id - dst_ext[ :continue_src_pack_id ] == 1
893
+ while dst_ext[ :pieces ].include?( pack_id + 1 )
894
+ data << dst_ext[ :pieces ].delete( pack_id + 1 )
895
+ pack_id += 1
896
+ end
897
+
898
+ dst_ext[ :continue_src_pack_id ] = pack_id
899
+ dst_ext[ :last_continue_at ] = now
900
+ add_dst_wbuff( dst_ext[ :dst ], data )
901
+ # puts "debug2 update continue src pack #{ pack_id }"
902
+
903
+ # 接到流量,若对面已关闭,且流量正好收全,关闭dst
904
+ if dst_ext[ :is_src_closed ] && ( pack_id == dst_ext[ :biggest_src_pack_id ] )
905
+ # puts "debug1 4-2. tund recv traffic -> src closed and all traffic received ? -> close dst after write"
906
+ set_is_closing( dst_ext[ :dst ] )
907
+ return
908
+ end
909
+ else
910
+ dst_ext[ :pieces ][ pack_id ] = data
911
+ end
912
+ end
913
+
914
+ end
915
+ end