p2p2 0.7.2 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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