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