p2p2 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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