p2p2 0.8.0 → 0.8.1

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