p2p2 0.6.8 → 0.6.9

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: c2ea64b1b56f96705f33185cd1d3453aa7dca90201c959a58c945d37a8b93de6
4
- data.tar.gz: 1531971c59bfbb7733b90fdcc6bd0360c7515bde0feabeb1f11e54a91247e90c
3
+ metadata.gz: e5aee043716890c42d6c434d62c94bcb022751137de150233d00b4ba7abdb948
4
+ data.tar.gz: 1e57170b02d0bf497762c1f32f6787e3928c3f50b46142d9a53a75c13999aa55
5
5
  SHA512:
6
- metadata.gz: 2019e957258fccf58435115404570f2d82168ca2c1edc2a5236ada506ecc3fc194353b1729d42b231f79ab61d7321dfe1c23e888484df4fd4299abc97e9a620c
7
- data.tar.gz: 7bc57270d19f9cf5bf40c2bb460eb091ea8212302d844d3589ef10ba3853b8d22495380580b841bc0f0a72a4179acc2e8d7d1e2afcdba93f5e0b9409463cf467
6
+ metadata.gz: 40d24ce3e319971d2819c21174022e261e1504b0192114328f93f1c92922f59cb47b09793a04f2806a51faea8bdbaf09f8309b8fb97bdbdd80e1161e4e17e101
7
+ data.tar.gz: e0701bda3541f15ba567f0653e3ed69e7374bef76a352f8f166aeb6de2d3cbe4cc89de5e04691b3308b202c0a4b6010cc5eb8ef7e8a86119c8ee56d6848e517a
@@ -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,9 +1,9 @@
1
- module P2p2
2
- PACK_SIZE = 1448 # 包大小
3
- CHUNK_SIZE = PACK_SIZE * 1000 # 块大小
4
- REP2P_LIMIT = 5 # p2p重试次数。到早了另一头还没从洞里出来,会吃ECONNREFUSED,不慌,再来一发。
5
- SET_TITLE = 1
6
- PAIRING = 2
7
- CTL_CLOSE_ROOM = [ 1 ].pack( 'C' )
8
- CTL_CLOSE_APP = [ 2 ].pack( 'C' )
9
- end
1
+ module P2p2
2
+ PACK_SIZE = 1448 # 包大小
3
+ CHUNK_SIZE = PACK_SIZE * 1000 # 块大小
4
+ REP2P_LIMIT = 5 # p2p重试次数。到早了另一头还没从洞里出来,会吃ECONNREFUSED,不慌,再来一发。
5
+ SET_TITLE = 1
6
+ PAIRING = 2
7
+ CTL_CLOSE_ROOM = [ 1 ].pack( 'C' )
8
+ CTL_CLOSE_APP = [ 2 ].pack( 'C' )
9
+ end
@@ -1,458 +1,457 @@
1
- require 'p2p2/head'
2
- require 'p2p2/hex'
3
- require 'p2p2/version'
4
- require 'socket'
5
-
6
- ##
7
- # P2p2::P1 - 处于nat里的任意应用,访问处于另一个nat里的应用服务端,借助一根p2p管道。p1端。
8
- #
9
- module P2p2
10
- class P1
11
-
12
- ##
13
- # roomd_host 配对服务器ip
14
- # roomd_port 配对服务器端口
15
- # appd_host 任意的一个应用的ip
16
- # appd_port 应用端口
17
- # title 约定的房间名
18
- # app_chunk_dir 文件缓存目录,缓存app来不及写的流量
19
- # p1_chunk_dir 文件缓存目录,缓存p1来不及写的流量
20
- #
21
- def initialize( roomd_host, roomd_port, appd_host, appd_port, title, app_chunk_dir = '/tmp', p1_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
- @p1_chunk_dir = p1_chunk_dir
27
- @hex = P2p2::Hex.new
28
- @mutex = Mutex.new
29
- @reads = []
30
- @writes = []
31
- @closings = []
32
- @roles = {} # sock => :ctlr / :room / :p1 / :app
33
- @infos = {}
34
- @reconn_room = false
35
-
36
- ctlr, ctlw = IO.pipe
37
- @ctlw = ctlw
38
- @roles[ ctlr ] = :ctlr
39
- @reads << ctlr
40
-
41
- new_room
42
- end
43
-
44
- def looping
45
- puts 'looping'
46
-
47
- loop_expire
48
-
49
- loop do
50
- rs, ws = IO.select( @reads, @writes )
51
-
52
- @mutex.synchronize do
53
- rs.each do | sock |
54
- case @roles[ sock ]
55
- when :ctlr
56
- read_ctlr( sock )
57
- when :room
58
- read_room( sock )
59
- when :p1
60
- read_p1( sock )
61
- when :app
62
- read_app( sock )
63
- end
64
- end
65
-
66
- ws.each do | sock |
67
- case @roles[ sock ]
68
- when :room
69
- write_room( sock )
70
- when :p1
71
- write_p1( sock )
72
- when :app
73
- write_app( sock )
74
- end
75
- end
76
- end
77
- end
78
- rescue Interrupt => e
79
- puts e.class
80
- quit!
81
- end
82
-
83
- def quit!
84
- exit
85
- end
86
-
87
- private
88
-
89
- def loop_expire
90
- Thread.new do
91
- loop do
92
- sleep 60
93
-
94
- if Time.new - @room_info[ :updated_at ] > 600
95
- @mutex.synchronize do
96
- @ctlw.write( CTL_CLOSE_ROOM )
97
- end
98
- end
99
- end
100
- end
101
- end
102
-
103
- ##
104
- # read ctlr
105
- #
106
- def read_ctlr( ctlr )
107
- case ctlr.read( 1 )
108
- when CTL_CLOSE_ROOM
109
- unless @room.closed?
110
- add_closing( @room )
111
- end
112
- end
113
- end
114
-
115
- ##
116
- # read room
117
- #
118
- def read_room( room )
119
- begin
120
- data = room.read_nonblock( PACK_SIZE )
121
- rescue IO::WaitReadable, Errno::EINTR, IO::WaitWritable
122
- return
123
- rescue Errno::ECONNREFUSED, EOFError, Errno::ECONNRESET => e
124
- puts "read room #{ e.class } #{ Time.new }"
125
-
126
- if @reconn_room
127
- raise e
128
- end
129
-
130
- sleep 5
131
- add_closing( room )
132
- @reconn_room = true
133
- return
134
- end
135
-
136
- @reconn_room = false
137
- info = @infos[ room ]
138
- info[ :p2_sockaddr ] = data
139
- info[ :updated_at ] = Time.new
140
- new_p1
141
- end
142
-
143
- ##
144
- # read p1
145
- #
146
- def read_p1( p1 )
147
- begin
148
- data = p1.read_nonblock( PACK_SIZE )
149
- rescue IO::WaitReadable, Errno::EINTR, IO::WaitWritable
150
- return
151
- rescue Errno::ECONNREFUSED => e
152
- if @room_info[ :renew_p1_times ] >= REP2P_LIMIT
153
- raise e
154
- end
155
-
156
- sleep 1
157
- add_closing( p1 )
158
- info = @infos[ p1 ]
159
- info[ :need_renew ] = true
160
- @room_info[ :renew_p1_times ] += 1
161
- return
162
- rescue Exception => e
163
- add_closing( p1 )
164
- return
165
- end
166
-
167
- info = @infos[ p1 ]
168
-
169
- if info[ :app ].nil? || info[ :app ].closed?
170
- add_closing( p1 )
171
- return
172
- end
173
-
174
- if info[ :need_decode ]
175
- len = data[ 0, 2 ].unpack( 'n' ).first
176
- head = @hex.decode( data[ 2, len ] )
177
- data = head + data[ ( 2 + len )..-1 ]
178
- info[ :need_decode ] = false
179
- end
180
-
181
- add_write( info[ :app ], data )
182
- @room_info[ :updated_at ] = Time.new
183
- end
184
-
185
- ##
186
- # read app
187
- #
188
- def read_app( app )
189
- begin
190
- data = app.read_nonblock( PACK_SIZE )
191
- rescue IO::WaitReadable, Errno::EINTR, IO::WaitWritable
192
- return
193
- rescue Exception => e
194
- add_closing( app )
195
- return
196
- end
197
-
198
- info = @infos[ app ]
199
-
200
- if info[ :p1 ].nil? || info[ :p1 ].closed?
201
- add_closing( app )
202
- return
203
- end
204
-
205
- if info[ :need_encode ]
206
- data = @hex.encode( data )
207
- data = [ [ data.size ].pack( 'n' ), data ].join
208
- info[ :need_encode ] = false
209
- end
210
-
211
- add_write( info[ :p1 ], data )
212
- @room_info[ :updated_at ] = Time.new
213
- end
214
-
215
- ##
216
- # write room
217
- #
218
- def write_room( room )
219
- if @closings.include?( room )
220
- close_sock( room )
221
- new_room
222
- return
223
- end
224
-
225
- info = @infos[ room ]
226
- room.write( info[ :wbuff ] )
227
- @writes.delete( room )
228
- end
229
-
230
- ##
231
- # write p1
232
- #
233
- def write_p1( p1 )
234
- if @closings.include?( p1 )
235
- info = close_sock( p1 )
236
-
237
- if info[ :need_renew ]
238
- new_p1
239
- return
240
- end
241
-
242
- unless info[ :app ].closed?
243
- add_closing( info[ :app ] )
244
- end
245
-
246
- unless @room.closed?
247
- add_closing( @room )
248
- end
249
-
250
- return
251
- end
252
-
253
- info = @infos[ p1 ]
254
- data, from = get_buff( info )
255
-
256
- if data.empty?
257
- @writes.delete( p1 )
258
- return
259
- end
260
-
261
- begin
262
- written = p1.write_nonblock( data )
263
- rescue IO::WaitWritable, Errno::EINTR, IO::WaitReadable
264
- return
265
- rescue Exception => e
266
- add_closing( p1 )
267
- return
268
- end
269
-
270
- data = data[ written..-1 ]
271
- info[ from ] = data
272
- end
273
-
274
- ##
275
- # write app
276
- #
277
- def write_app( app )
278
- if @closings.include?( app )
279
- info = close_sock( app )
280
-
281
- unless info[ :p1 ].closed?
282
- add_closing( info[ :p1 ] )
283
- end
284
-
285
- return
286
- end
287
-
288
- info = @infos[ app ]
289
- data, from = get_buff( info )
290
-
291
- if data.empty?
292
- @writes.delete( app )
293
- return
294
- end
295
-
296
- begin
297
- written = app.write_nonblock( data )
298
- rescue IO::WaitWritable, Errno::EINTR, IO::WaitReadable
299
- return
300
- rescue Exception => e
301
- add_closing( app )
302
- return
303
- end
304
-
305
- data = data[ written..-1 ]
306
- info[ from ] = data
307
- end
308
-
309
- def get_buff( info )
310
- data, from = info[ :cache ], :cache
311
-
312
- if data.empty?
313
- if info[ :chunks ].any?
314
- path = File.join( info[ :chunk_dir ], info[ :chunks ].shift )
315
- data = info[ :cache ] = IO.binread( path )
316
-
317
- begin
318
- File.delete( path )
319
- rescue Errno::ENOENT
320
- end
321
- else
322
- data, from = info[ :wbuff ], :wbuff
323
- end
324
- end
325
-
326
- [ data, from ]
327
- end
328
-
329
- def add_closing( sock )
330
- unless @closings.include?( sock )
331
- @reads.delete( sock )
332
- @closings << sock
333
- end
334
-
335
- add_write( sock )
336
- end
337
-
338
- def add_write( sock, data = nil )
339
- if data
340
- info = @infos[ sock ]
341
- info[ :wbuff ] << data
342
-
343
- if info[ :wbuff ].size >= CHUNK_SIZE
344
- filename = [ info[ :filename ], info[ :chunk_seed ] ].join( '.' )
345
- chunk_path = File.join( info[ :chunk_dir ], filename )
346
- IO.binwrite( chunk_path, info[ :wbuff ] )
347
- info[ :chunks ] << filename
348
- info[ :chunk_seed ] += 1
349
- info[ :wbuff ].clear
350
- end
351
- end
352
-
353
- unless @writes.include?( sock )
354
- @writes << sock
355
- end
356
- end
357
-
358
- def close_sock( sock )
359
- sock.close
360
- @reads.delete( sock )
361
- @writes.delete( sock )
362
- @closings.delete( sock )
363
- @roles.delete( sock )
364
- info = @infos.delete( sock )
365
-
366
- if info && info[ :chunks ]
367
- info[ :chunks ].each do | filename |
368
- begin
369
- File.delete( File.join( info[ :chunk_dir ], filename ) )
370
- rescue Errno::ENOENT
371
- end
372
- end
373
- end
374
-
375
- info
376
- end
377
-
378
- def new_room
379
- room = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
380
- room.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1 )
381
- room.setsockopt( Socket::SOL_TCP, Socket::TCP_NODELAY, 1 )
382
-
383
- begin
384
- room.connect_nonblock( @roomd_sockaddr )
385
- rescue IO::WaitWritable, Errno::EINTR
386
- end
387
-
388
- title = @title.unpack( "C*" ).map{ | c | c.chr }.join
389
- room_info = {
390
- wbuff: [ [ SET_TITLE ].pack( 'C' ), title ].join,
391
- p2_sockaddr: nil,
392
- renew_p1_times: 0,
393
- updated_at: Time.new
394
- }
395
- @room = room
396
- @room_info = room_info
397
- @roles[ room ] = :room
398
- @infos[ room ] = room_info
399
- @reads << room
400
- @writes << room
401
- end
402
-
403
- def new_p1
404
- p1 = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
405
- p1.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1 )
406
- p1.setsockopt( Socket::SOL_TCP, Socket::TCP_NODELAY, 1 )
407
- p1.bind( @room.local_address ) # use the hole
408
-
409
- begin
410
- p1.connect_nonblock( @room_info[ :p2_sockaddr ] )
411
- rescue IO::WaitWritable, Errno::EINTR
412
- rescue Exception => e
413
- puts "connect p2 #{ e.class } #{ Time.new }"
414
- p1.close
415
- add_closing( @room )
416
- return
417
- end
418
-
419
- app = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
420
- app.setsockopt( Socket::SOL_TCP, Socket::TCP_NODELAY, 1 )
421
-
422
- begin
423
- app.connect_nonblock( @appd_sockaddr )
424
- rescue IO::WaitWritable, Errno::EINTR
425
- end
426
-
427
- p1_info = {
428
- wbuff: '',
429
- cache: '',
430
- filename: [ Process.pid, p1.object_id ].join( '-' ),
431
- chunk_dir: @p1_chunk_dir,
432
- chunks: [],
433
- chunk_seed: 0,
434
- need_decode: true,
435
- need_renew: false,
436
- app: app
437
- }
438
-
439
- app_info = {
440
- wbuff: '',
441
- cache: '',
442
- filename: [ Process.pid, app.object_id ].join( '-' ),
443
- chunk_dir: @app_chunk_dir,
444
- chunks: [],
445
- chunk_seed: 0,
446
- need_encode: true,
447
- p1: p1
448
- }
449
-
450
- @roles[ p1 ] = :p1
451
- @infos[ p1 ] = p1_info
452
- @reads << p1
453
- @roles[ app ] = :app
454
- @infos[ app ] = app_info
455
- @reads << app
456
- end
457
- end
458
- end
1
+ require 'p2p2/head'
2
+ require 'p2p2/hex'
3
+ require 'p2p2/version'
4
+ require 'socket'
5
+
6
+ ##
7
+ # P2p2::P1 - 处于nat里的任意应用,访问处于另一个nat里的应用服务端,借助一根p2p管道。p1端。
8
+ #
9
+ module P2p2
10
+ class P1
11
+
12
+ ##
13
+ # roomd_host 配对服务器ip
14
+ # roomd_port 配对服务器端口
15
+ # appd_host 任意的一个应用的ip
16
+ # appd_port 应用端口
17
+ # title 约定的房间名
18
+ # app_chunk_dir 文件缓存目录,缓存app来不及写的流量
19
+ # p1_chunk_dir 文件缓存目录,缓存p1来不及写的流量
20
+ #
21
+ def initialize( roomd_host, roomd_port, appd_host, appd_port, title, app_chunk_dir = '/tmp', p1_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
+ @p1_chunk_dir = p1_chunk_dir
27
+ @hex = P2p2::Hex.new
28
+ @mutex = Mutex.new
29
+ @reads = []
30
+ @writes = []
31
+ @closings = []
32
+ @roles = {} # sock => :ctlr / :room / :p1 / :app
33
+ @infos = {}
34
+ @reconn_room_at = nil
35
+
36
+ ctlr, ctlw = IO.pipe
37
+ @ctlw = ctlw
38
+ @roles[ ctlr ] = :ctlr
39
+ @reads << ctlr
40
+
41
+ new_room
42
+ end
43
+
44
+ def looping
45
+ puts 'looping'
46
+
47
+ loop_expire
48
+
49
+ loop do
50
+ rs, ws = IO.select( @reads, @writes )
51
+
52
+ @mutex.synchronize do
53
+ rs.each do | sock |
54
+ case @roles[ sock ]
55
+ when :ctlr
56
+ read_ctlr( sock )
57
+ when :room
58
+ read_room( sock )
59
+ when :p1
60
+ read_p1( sock )
61
+ when :app
62
+ read_app( sock )
63
+ end
64
+ end
65
+
66
+ ws.each do | sock |
67
+ case @roles[ sock ]
68
+ when :room
69
+ write_room( sock )
70
+ when :p1
71
+ write_p1( sock )
72
+ when :app
73
+ write_app( sock )
74
+ end
75
+ end
76
+ end
77
+ end
78
+ rescue Interrupt => e
79
+ puts e.class
80
+ quit!
81
+ end
82
+
83
+ def quit!
84
+ exit
85
+ end
86
+
87
+ private
88
+
89
+ def loop_expire
90
+ Thread.new do
91
+ loop do
92
+ sleep 60
93
+
94
+ if Time.new - @room_info[ :updated_at ] > 600
95
+ @mutex.synchronize do
96
+ @ctlw.write( CTL_CLOSE_ROOM )
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+
103
+ ##
104
+ # read ctlr
105
+ #
106
+ def read_ctlr( ctlr )
107
+ case ctlr.read( 1 )
108
+ when CTL_CLOSE_ROOM
109
+ unless @room.closed?
110
+ add_closing( @room )
111
+ end
112
+ end
113
+ end
114
+
115
+ ##
116
+ # read room
117
+ #
118
+ def read_room( room )
119
+ begin
120
+ data = room.read_nonblock( PACK_SIZE )
121
+ rescue IO::WaitReadable, Errno::EINTR, IO::WaitWritable
122
+ return
123
+ rescue Errno::ECONNREFUSED, EOFError, Errno::ECONNRESET => e
124
+ puts "read room #{ e.class } #{ Time.new }"
125
+
126
+ if @reconn_room_at && ( Time.new - @reconn_room_at < 10 )
127
+ raise e
128
+ end
129
+
130
+ sleep 5
131
+ add_closing( room )
132
+ @reconn_room_at = Time.new
133
+ return
134
+ end
135
+
136
+ info = @infos[ room ]
137
+ info[ :p2_sockaddr ] = data
138
+ info[ :updated_at ] = Time.new
139
+ new_p1
140
+ end
141
+
142
+ ##
143
+ # read p1
144
+ #
145
+ def read_p1( p1 )
146
+ begin
147
+ data = p1.read_nonblock( PACK_SIZE )
148
+ rescue IO::WaitReadable, Errno::EINTR, IO::WaitWritable
149
+ return
150
+ rescue Errno::ECONNREFUSED => e
151
+ if @room_info[ :renew_p1_times ] >= REP2P_LIMIT
152
+ raise e
153
+ end
154
+
155
+ sleep 1
156
+ add_closing( p1 )
157
+ info = @infos[ p1 ]
158
+ info[ :need_renew ] = true
159
+ @room_info[ :renew_p1_times ] += 1
160
+ return
161
+ rescue Exception => e
162
+ add_closing( p1 )
163
+ return
164
+ end
165
+
166
+ info = @infos[ p1 ]
167
+
168
+ if info[ :app ].nil? || info[ :app ].closed?
169
+ add_closing( p1 )
170
+ return
171
+ end
172
+
173
+ if info[ :need_decode ]
174
+ len = data[ 0, 2 ].unpack( 'n' ).first
175
+ head = @hex.decode( data[ 2, len ] )
176
+ data = head + data[ ( 2 + len )..-1 ]
177
+ info[ :need_decode ] = false
178
+ end
179
+
180
+ add_write( info[ :app ], data )
181
+ @room_info[ :updated_at ] = Time.new
182
+ end
183
+
184
+ ##
185
+ # read app
186
+ #
187
+ def read_app( app )
188
+ begin
189
+ data = app.read_nonblock( PACK_SIZE )
190
+ rescue IO::WaitReadable, Errno::EINTR, IO::WaitWritable
191
+ return
192
+ rescue Exception => e
193
+ add_closing( app )
194
+ return
195
+ end
196
+
197
+ info = @infos[ app ]
198
+
199
+ if info[ :p1 ].nil? || info[ :p1 ].closed?
200
+ add_closing( app )
201
+ return
202
+ end
203
+
204
+ if info[ :need_encode ]
205
+ data = @hex.encode( data )
206
+ data = [ [ data.size ].pack( 'n' ), data ].join
207
+ info[ :need_encode ] = false
208
+ end
209
+
210
+ add_write( info[ :p1 ], data )
211
+ @room_info[ :updated_at ] = Time.new
212
+ end
213
+
214
+ ##
215
+ # write room
216
+ #
217
+ def write_room( room )
218
+ if @closings.include?( room )
219
+ close_sock( room )
220
+ new_room
221
+ return
222
+ end
223
+
224
+ info = @infos[ room ]
225
+ room.write( info[ :wbuff ] )
226
+ @writes.delete( room )
227
+ end
228
+
229
+ ##
230
+ # write p1
231
+ #
232
+ def write_p1( p1 )
233
+ if @closings.include?( p1 )
234
+ info = close_sock( p1 )
235
+
236
+ if info[ :need_renew ]
237
+ new_p1
238
+ return
239
+ end
240
+
241
+ unless info[ :app ].closed?
242
+ add_closing( info[ :app ] )
243
+ end
244
+
245
+ unless @room.closed?
246
+ add_closing( @room )
247
+ end
248
+
249
+ return
250
+ end
251
+
252
+ info = @infos[ p1 ]
253
+ data, from = get_buff( info )
254
+
255
+ if data.empty?
256
+ @writes.delete( p1 )
257
+ return
258
+ end
259
+
260
+ begin
261
+ written = p1.write_nonblock( data )
262
+ rescue IO::WaitWritable, Errno::EINTR, IO::WaitReadable
263
+ return
264
+ rescue Exception => e
265
+ add_closing( p1 )
266
+ return
267
+ end
268
+
269
+ data = data[ written..-1 ]
270
+ info[ from ] = data
271
+ end
272
+
273
+ ##
274
+ # write app
275
+ #
276
+ def write_app( app )
277
+ if @closings.include?( app )
278
+ info = close_sock( app )
279
+
280
+ unless info[ :p1 ].closed?
281
+ add_closing( info[ :p1 ] )
282
+ end
283
+
284
+ return
285
+ end
286
+
287
+ info = @infos[ app ]
288
+ data, from = get_buff( info )
289
+
290
+ if data.empty?
291
+ @writes.delete( app )
292
+ return
293
+ end
294
+
295
+ begin
296
+ written = app.write_nonblock( data )
297
+ rescue IO::WaitWritable, Errno::EINTR, IO::WaitReadable
298
+ return
299
+ rescue Exception => e
300
+ add_closing( app )
301
+ return
302
+ end
303
+
304
+ data = data[ written..-1 ]
305
+ info[ from ] = data
306
+ end
307
+
308
+ def get_buff( info )
309
+ data, from = info[ :cache ], :cache
310
+
311
+ if data.empty?
312
+ if info[ :chunks ].any?
313
+ path = File.join( info[ :chunk_dir ], info[ :chunks ].shift )
314
+ data = info[ :cache ] = IO.binread( path )
315
+
316
+ begin
317
+ File.delete( path )
318
+ rescue Errno::ENOENT
319
+ end
320
+ else
321
+ data, from = info[ :wbuff ], :wbuff
322
+ end
323
+ end
324
+
325
+ [ data, from ]
326
+ end
327
+
328
+ def add_closing( sock )
329
+ unless @closings.include?( sock )
330
+ @reads.delete( sock )
331
+ @closings << sock
332
+ end
333
+
334
+ add_write( sock )
335
+ end
336
+
337
+ def add_write( sock, data = nil )
338
+ if data
339
+ info = @infos[ sock ]
340
+ info[ :wbuff ] << data
341
+
342
+ if info[ :wbuff ].size >= CHUNK_SIZE
343
+ filename = [ info[ :filename ], info[ :chunk_seed ] ].join( '.' )
344
+ chunk_path = File.join( info[ :chunk_dir ], filename )
345
+ IO.binwrite( chunk_path, info[ :wbuff ] )
346
+ info[ :chunks ] << filename
347
+ info[ :chunk_seed ] += 1
348
+ info[ :wbuff ].clear
349
+ end
350
+ end
351
+
352
+ unless @writes.include?( sock )
353
+ @writes << sock
354
+ end
355
+ end
356
+
357
+ def close_sock( sock )
358
+ sock.close
359
+ @reads.delete( sock )
360
+ @writes.delete( sock )
361
+ @closings.delete( sock )
362
+ @roles.delete( sock )
363
+ info = @infos.delete( sock )
364
+
365
+ if info && info[ :chunks ]
366
+ info[ :chunks ].each do | filename |
367
+ begin
368
+ File.delete( File.join( info[ :chunk_dir ], filename ) )
369
+ rescue Errno::ENOENT
370
+ end
371
+ end
372
+ end
373
+
374
+ info
375
+ end
376
+
377
+ def new_room
378
+ room = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
379
+ room.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1 )
380
+ room.setsockopt( Socket::SOL_TCP, Socket::TCP_NODELAY, 1 )
381
+
382
+ begin
383
+ room.connect_nonblock( @roomd_sockaddr )
384
+ rescue IO::WaitWritable, Errno::EINTR
385
+ end
386
+
387
+ title = @title.unpack( "C*" ).map{ | c | c.chr }.join
388
+ room_info = {
389
+ wbuff: [ [ SET_TITLE ].pack( 'C' ), title ].join,
390
+ p2_sockaddr: nil,
391
+ renew_p1_times: 0,
392
+ updated_at: Time.new
393
+ }
394
+ @room = room
395
+ @room_info = room_info
396
+ @roles[ room ] = :room
397
+ @infos[ room ] = room_info
398
+ @reads << room
399
+ @writes << room
400
+ end
401
+
402
+ def new_p1
403
+ p1 = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
404
+ p1.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1 )
405
+ p1.setsockopt( Socket::SOL_TCP, Socket::TCP_NODELAY, 1 )
406
+ p1.bind( @room.local_address ) # use the hole
407
+
408
+ begin
409
+ p1.connect_nonblock( @room_info[ :p2_sockaddr ] )
410
+ rescue IO::WaitWritable, Errno::EINTR
411
+ rescue Exception => e
412
+ puts "connect p2 #{ e.class } #{ Time.new }"
413
+ p1.close
414
+ add_closing( @room )
415
+ return
416
+ end
417
+
418
+ app = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
419
+ app.setsockopt( Socket::SOL_TCP, Socket::TCP_NODELAY, 1 )
420
+
421
+ begin
422
+ app.connect_nonblock( @appd_sockaddr )
423
+ rescue IO::WaitWritable, Errno::EINTR
424
+ end
425
+
426
+ p1_info = {
427
+ wbuff: '',
428
+ cache: '',
429
+ filename: [ Process.pid, p1.object_id ].join( '-' ),
430
+ chunk_dir: @p1_chunk_dir,
431
+ chunks: [],
432
+ chunk_seed: 0,
433
+ need_decode: true,
434
+ need_renew: false,
435
+ app: app
436
+ }
437
+
438
+ app_info = {
439
+ wbuff: '',
440
+ cache: '',
441
+ filename: [ Process.pid, app.object_id ].join( '-' ),
442
+ chunk_dir: @app_chunk_dir,
443
+ chunks: [],
444
+ chunk_seed: 0,
445
+ need_encode: true,
446
+ p1: p1
447
+ }
448
+
449
+ @roles[ p1 ] = :p1
450
+ @infos[ p1 ] = p1_info
451
+ @reads << p1
452
+ @roles[ app ] = :app
453
+ @infos[ app ] = app_info
454
+ @reads << app
455
+ end
456
+ end
457
+ end