p2p2 0.8.0 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 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