p2p2 0.6.9 → 0.7.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.
@@ -1,494 +1,495 @@
1
- require 'p2p2/head'
2
- require 'p2p2/hex'
3
- require 'p2p2/version'
4
- require 'socket'
5
-
6
- ##
7
- # P2p2::P2 - 处于nat里的任意应用,访问处于另一个nat里的应用服务端,借助一根p2p管道。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 ] > 600 )
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_at: nil,
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, EOFError, Errno::ECONNRESET => e
195
- puts "read room #{ e.class } #{ Time.new }"
196
-
197
- if @app_info[ :reconn_room_at ] && ( Time.new - @app_info[ :reconn_room_at ] < 10 )
198
- raise e
199
- end
200
-
201
- sleep 5
202
- add_closing( room )
203
- @app_info[ :reconn_room_at ] = Time.new
204
- return
205
- end
206
-
207
- @app_info[ :p1_sockaddr ] = data
208
- @app_info[ :updated_at ] = Time.new
209
- new_p2
210
- end
211
-
212
- ##
213
- # read p2
214
- #
215
- def read_p2( p2 )
216
- begin
217
- data = p2.read_nonblock( PACK_SIZE )
218
- rescue IO::WaitReadable, Errno::EINTR, IO::WaitWritable
219
- return
220
- rescue Errno::ECONNREFUSED => e
221
- puts "read p2 #{ e.class } #{ Time.new }"
222
-
223
- if @app_info[ :renew_p2_times ] >= REP2P_LIMIT
224
- raise e
225
- end
226
-
227
- sleep 1
228
- add_closing( p2 )
229
- info = @infos[ p2 ]
230
- info[ :need_renew ] = true
231
- @app_info[ :renew_p2_times ] += 1
232
- return
233
- rescue Exception => e
234
- puts "read p2 #{ e.class } #{ Time.new }"
235
- add_closing( p2 )
236
- return
237
- end
238
-
239
- if @app.nil? || @app.closed?
240
- add_closing( p2 )
241
- return
242
- end
243
-
244
- info = @infos[ p2 ]
245
-
246
- if info[ :need_decode ]
247
- len = data[ 0, 2 ].unpack( 'n' ).first
248
- head = @hex.decode( data[ 2, len ] )
249
- data = head + data[ ( 2 + len )..-1 ]
250
- info[ :need_decode ] = false
251
- end
252
-
253
- add_write( @app, data )
254
- @app_info[ :updated_at ] = Time.new
255
- end
256
-
257
- ##
258
- # write app
259
- #
260
- def write_app( app )
261
- if @closings.include?( app )
262
- info = close_sock( app )
263
-
264
- if info[ :room ] && !info[ :room ].closed?
265
- add_closing( info[ :room ] )
266
- end
267
-
268
- if info[ :p2 ] && !info[ :p2 ].closed?
269
- add_closing( info[ :p2 ] )
270
- end
271
-
272
- return
273
- end
274
-
275
- info = @infos[ app ]
276
- data, from = get_buff( info )
277
-
278
- if data.empty?
279
- @writes.delete( app )
280
- return
281
- end
282
-
283
- begin
284
- written = app.write_nonblock( data )
285
- rescue IO::WaitWritable, Errno::EINTR, IO::WaitReadable
286
- return
287
- rescue Exception => e
288
- add_closing( app )
289
- return
290
- end
291
-
292
- data = data[ written..-1 ]
293
- info[ from ] = data
294
- end
295
-
296
- ##
297
- # write room
298
- #
299
- def write_room( room )
300
- if @closings.include?( room )
301
- close_sock( room )
302
-
303
- unless @app.closed?
304
- add_closing( @app )
305
- end
306
-
307
- return
308
- end
309
-
310
- info = @infos[ room ]
311
- room.write( info[ :wbuff ] )
312
- @writes.delete( room )
313
- end
314
-
315
- ##
316
- # write p2
317
- #
318
- def write_p2( p2 )
319
- if @closings.include?( p2 )
320
- info = close_sock( p2 )
321
-
322
- if info[ :need_renew ]
323
- new_p2
324
- return
325
- end
326
-
327
- unless @app_info[ :room ].closed?
328
- add_closing( @app_info[ :room ] )
329
- end
330
-
331
- return
332
- end
333
-
334
- info = @infos[ p2 ]
335
- data, from = get_buff( info )
336
-
337
- if data.empty?
338
- @writes.delete( p2 )
339
- return
340
- end
341
-
342
- begin
343
- written = p2.write_nonblock( data )
344
- rescue IO::WaitWritable, Errno::EINTR, IO::WaitReadable
345
- return
346
- rescue Exception => e
347
- add_closing( p2 )
348
- return
349
- end
350
-
351
- data = data[ written..-1 ]
352
- info[ from ] = data
353
- end
354
-
355
- def get_buff( info )
356
- data, from = info[ :cache ], :cache
357
-
358
- if data.empty?
359
- if info[ :chunks ].any?
360
- path = File.join( info[ :chunk_dir ], info[ :chunks ].shift )
361
- data = info[ :cache ] = IO.binread( path )
362
-
363
- begin
364
- File.delete( path )
365
- rescue Errno::ENOENT
366
- end
367
- else
368
- data, from = info[ :wbuff ], :wbuff
369
- end
370
- end
371
-
372
- [ data, from ]
373
- end
374
-
375
- def add_closing( sock )
376
- unless @closings.include?( sock )
377
- @reads.delete( sock )
378
- @closings << sock
379
- end
380
-
381
- add_write( sock )
382
- end
383
-
384
- def add_write( sock, data = nil )
385
- if data
386
- info = @infos[ sock ]
387
- info[ :wbuff ] << data
388
-
389
- if info[ :wbuff ].size >= CHUNK_SIZE
390
- filename = [ info[ :filename ], info[ :chunk_seed ] ].join( '.' )
391
- chunk_path = File.join( info[ :chunk_dir ], filename )
392
- IO.binwrite( chunk_path, info[ :wbuff ] )
393
- info[ :chunks ] << filename
394
- info[ :chunk_seed ] += 1
395
- info[ :wbuff ].clear
396
- end
397
- end
398
-
399
- unless @writes.include?( sock )
400
- @writes << sock
401
- end
402
- end
403
-
404
- def close_sock( sock )
405
- sock.close
406
- @reads.delete( sock )
407
- @writes.delete( sock )
408
- @closings.delete( sock )
409
- @roles.delete( sock )
410
- info = @infos.delete( sock )
411
-
412
- if info && info[ :chunks ]
413
- info[ :chunks ].each do | filename |
414
- begin
415
- File.delete( File.join( info[ :chunk_dir ], filename ) )
416
- rescue Errno::ENOENT
417
- end
418
- end
419
- end
420
-
421
- info
422
- end
423
-
424
- def new_appd
425
- appd = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
426
- appd.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1 )
427
- appd.setsockopt( Socket::SOL_TCP, Socket::TCP_NODELAY, 1 )
428
- appd.bind( @appd_sockaddr )
429
- appd.listen( 511 )
430
-
431
- @roles[ appd ] = :appd
432
- @reads << appd
433
- end
434
-
435
- def new_room
436
- room = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
437
- room.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1 )
438
- room.setsockopt( Socket::SOL_TCP, Socket::TCP_NODELAY, 1 )
439
-
440
- begin
441
- room.connect_nonblock( @roomd_sockaddr )
442
- rescue IO::WaitWritable, Errno::EINTR
443
- end
444
-
445
- bytes = @title.unpack( "C*" ).map{ | c | c.chr }.join
446
- room_info = {
447
- wbuff: [ [ PAIRING ].pack( 'C' ), bytes ].join
448
- }
449
- @roles[ room ] = :room
450
- @infos[ room ] = room_info
451
- @reads << room
452
- @writes << room
453
- @app_info[ :room ] = room
454
- @app_info[ :updated_at ] = Time.new
455
- end
456
-
457
- def new_p2
458
- p2 = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
459
- p2.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1 )
460
- p2.setsockopt( Socket::SOL_TCP, Socket::TCP_NODELAY, 1 )
461
- p2.bind( @app_info[ :room ].local_address ) # use the hole
462
-
463
- begin
464
- p2.connect_nonblock( @app_info[ :p1_sockaddr ] )
465
- rescue IO::WaitWritable, Errno::EINTR
466
- rescue Exception => e
467
- puts "connect p1 #{ e.class } #{ Time.new }"
468
- p2.close
469
- add_closing( @app_info[ :room ] )
470
- return
471
- end
472
-
473
- p2_info = {
474
- wbuff: @app_info[ :rbuff ],
475
- cache: '',
476
- filename: [ Process.pid, p2.object_id ].join( '-' ),
477
- chunk_dir: @p2_chunk_dir,
478
- chunks: [],
479
- chunk_seed: 0,
480
- need_decode: true,
481
- need_renew: false
482
- }
483
- @roles[ p2 ] = :p2
484
- @infos[ p2 ] = p2_info
485
- @reads << p2
486
-
487
- unless p2_info[ :wbuff ].empty?
488
- @writes << p2
489
- end
490
-
491
- @app_info[ :p2 ] = p2
492
- end
493
- end
494
- end
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, EOFError, Errno::ECONNRESET => e
195
+ puts "read room #{ e.class } #{ Time.new }"
196
+
197
+ if @app_info[ :reconn_room_times ] > 5
198
+ raise e
199
+ end
200
+
201
+ sleep 5
202
+ add_closing( room )
203
+ @app_info[ :reconn_room_times ] += 1
204
+ return
205
+ end
206
+
207
+ @app_info[ :reconn_room_times ] = 0
208
+ @app_info[ :p1_sockaddr ] = data
209
+ @app_info[ :updated_at ] = Time.new
210
+ new_p2
211
+ end
212
+
213
+ ##
214
+ # read p2
215
+ #
216
+ def read_p2( p2 )
217
+ begin
218
+ data = p2.read_nonblock( PACK_SIZE )
219
+ rescue IO::WaitReadable, Errno::EINTR, IO::WaitWritable
220
+ return
221
+ rescue Errno::ECONNREFUSED => e
222
+ puts "read p2 #{ e.class } #{ Time.new }"
223
+
224
+ if @app_info[ :renew_p2_times ] >= REP2P_LIMIT
225
+ raise e
226
+ end
227
+
228
+ sleep 1
229
+ add_closing( p2 )
230
+ info = @infos[ p2 ]
231
+ info[ :need_renew ] = true
232
+ @app_info[ :renew_p2_times ] += 1
233
+ return
234
+ rescue Exception => e
235
+ puts "read p2 #{ e.class } #{ Time.new }"
236
+ add_closing( p2 )
237
+ return
238
+ end
239
+
240
+ if @app.nil? || @app.closed?
241
+ add_closing( p2 )
242
+ return
243
+ end
244
+
245
+ info = @infos[ p2 ]
246
+
247
+ if info[ :need_decode ]
248
+ len = data[ 0, 2 ].unpack( 'n' ).first
249
+ head = @hex.decode( data[ 2, len ] )
250
+ data = head + data[ ( 2 + len )..-1 ]
251
+ info[ :need_decode ] = false
252
+ end
253
+
254
+ add_write( @app, data )
255
+ @app_info[ :updated_at ] = Time.new
256
+ end
257
+
258
+ ##
259
+ # write app
260
+ #
261
+ def write_app( app )
262
+ if @closings.include?( app )
263
+ info = close_sock( app )
264
+
265
+ if info[ :room ] && !info[ :room ].closed?
266
+ add_closing( info[ :room ] )
267
+ end
268
+
269
+ if info[ :p2 ] && !info[ :p2 ].closed?
270
+ add_closing( info[ :p2 ] )
271
+ end
272
+
273
+ return
274
+ end
275
+
276
+ info = @infos[ app ]
277
+ data, from = get_buff( info )
278
+
279
+ if data.empty?
280
+ @writes.delete( app )
281
+ return
282
+ end
283
+
284
+ begin
285
+ written = app.write_nonblock( data )
286
+ rescue IO::WaitWritable, Errno::EINTR, IO::WaitReadable
287
+ return
288
+ rescue Exception => e
289
+ add_closing( app )
290
+ return
291
+ end
292
+
293
+ data = data[ written..-1 ]
294
+ info[ from ] = data
295
+ end
296
+
297
+ ##
298
+ # write room
299
+ #
300
+ def write_room( room )
301
+ if @closings.include?( room )
302
+ close_sock( room )
303
+
304
+ unless @app.closed?
305
+ add_closing( @app )
306
+ end
307
+
308
+ return
309
+ end
310
+
311
+ info = @infos[ room ]
312
+ room.write( info[ :wbuff ] )
313
+ @writes.delete( room )
314
+ end
315
+
316
+ ##
317
+ # write p2
318
+ #
319
+ def write_p2( p2 )
320
+ if @closings.include?( p2 )
321
+ info = close_sock( p2 )
322
+
323
+ if info[ :need_renew ]
324
+ new_p2
325
+ return
326
+ end
327
+
328
+ unless @app_info[ :room ].closed?
329
+ add_closing( @app_info[ :room ] )
330
+ end
331
+
332
+ return
333
+ end
334
+
335
+ info = @infos[ p2 ]
336
+ data, from = get_buff( info )
337
+
338
+ if data.empty?
339
+ @writes.delete( p2 )
340
+ return
341
+ end
342
+
343
+ begin
344
+ written = p2.write_nonblock( data )
345
+ rescue IO::WaitWritable, Errno::EINTR, IO::WaitReadable
346
+ return
347
+ rescue Exception => e
348
+ add_closing( p2 )
349
+ return
350
+ end
351
+
352
+ data = data[ written..-1 ]
353
+ info[ from ] = data
354
+ end
355
+
356
+ def get_buff( info )
357
+ data, from = info[ :cache ], :cache
358
+
359
+ if data.empty?
360
+ if info[ :chunks ].any?
361
+ path = File.join( info[ :chunk_dir ], info[ :chunks ].shift )
362
+ data = info[ :cache ] = IO.binread( path )
363
+
364
+ begin
365
+ File.delete( path )
366
+ rescue Errno::ENOENT
367
+ end
368
+ else
369
+ data, from = info[ :wbuff ], :wbuff
370
+ end
371
+ end
372
+
373
+ [ data, from ]
374
+ end
375
+
376
+ def add_closing( sock )
377
+ unless @closings.include?( sock )
378
+ @reads.delete( sock )
379
+ @closings << sock
380
+ end
381
+
382
+ add_write( sock )
383
+ end
384
+
385
+ def add_write( sock, data = nil )
386
+ if data
387
+ info = @infos[ sock ]
388
+ info[ :wbuff ] << data
389
+
390
+ if info[ :wbuff ].size >= CHUNK_SIZE
391
+ filename = [ info[ :filename ], info[ :chunk_seed ] ].join( '.' )
392
+ chunk_path = File.join( info[ :chunk_dir ], filename )
393
+ IO.binwrite( chunk_path, info[ :wbuff ] )
394
+ info[ :chunks ] << filename
395
+ info[ :chunk_seed ] += 1
396
+ info[ :wbuff ].clear
397
+ end
398
+ end
399
+
400
+ unless @writes.include?( sock )
401
+ @writes << sock
402
+ end
403
+ end
404
+
405
+ def close_sock( sock )
406
+ sock.close
407
+ @reads.delete( sock )
408
+ @writes.delete( sock )
409
+ @closings.delete( sock )
410
+ @roles.delete( sock )
411
+ info = @infos.delete( sock )
412
+
413
+ if info && info[ :chunks ]
414
+ info[ :chunks ].each do | filename |
415
+ begin
416
+ File.delete( File.join( info[ :chunk_dir ], filename ) )
417
+ rescue Errno::ENOENT
418
+ end
419
+ end
420
+ end
421
+
422
+ info
423
+ end
424
+
425
+ def new_appd
426
+ appd = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
427
+ appd.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1 )
428
+ appd.setsockopt( Socket::SOL_TCP, Socket::TCP_NODELAY, 1 )
429
+ appd.bind( @appd_sockaddr )
430
+ appd.listen( 511 )
431
+
432
+ @roles[ appd ] = :appd
433
+ @reads << appd
434
+ end
435
+
436
+ def new_room
437
+ room = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
438
+ room.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1 )
439
+ room.setsockopt( Socket::SOL_TCP, Socket::TCP_NODELAY, 1 )
440
+
441
+ begin
442
+ room.connect_nonblock( @roomd_sockaddr )
443
+ rescue IO::WaitWritable, Errno::EINTR
444
+ end
445
+
446
+ bytes = @title.unpack( "C*" ).map{ | c | c.chr }.join
447
+ room_info = {
448
+ wbuff: [ [ PAIRING ].pack( 'C' ), bytes ].join
449
+ }
450
+ @roles[ room ] = :room
451
+ @infos[ room ] = room_info
452
+ @reads << room
453
+ @writes << room
454
+ @app_info[ :room ] = room
455
+ @app_info[ :updated_at ] = Time.new
456
+ end
457
+
458
+ def new_p2
459
+ p2 = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
460
+ p2.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1 )
461
+ p2.setsockopt( Socket::SOL_TCP, Socket::TCP_NODELAY, 1 )
462
+ p2.bind( @app_info[ :room ].local_address ) # use the hole
463
+
464
+ begin
465
+ p2.connect_nonblock( @app_info[ :p1_sockaddr ] )
466
+ rescue IO::WaitWritable, Errno::EINTR
467
+ rescue Exception => e
468
+ puts "connect p1 #{ e.class } #{ Time.new }"
469
+ p2.close
470
+ add_closing( @app_info[ :room ] )
471
+ return
472
+ end
473
+
474
+ p2_info = {
475
+ wbuff: @app_info[ :rbuff ],
476
+ cache: '',
477
+ filename: [ Process.pid, p2.object_id ].join( '-' ),
478
+ chunk_dir: @p2_chunk_dir,
479
+ chunks: [],
480
+ chunk_seed: 0,
481
+ need_decode: true,
482
+ need_renew: false
483
+ }
484
+ @roles[ p2 ] = :p2
485
+ @infos[ p2 ] = p2_info
486
+ @reads << p2
487
+
488
+ unless p2_info[ :wbuff ].empty?
489
+ @writes << p2
490
+ end
491
+
492
+ @app_info[ :p2 ] = p2
493
+ end
494
+ end
495
+ end