p2p2 0.11.0 → 0.12.0

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