p2p2 0.6.9 → 0.7.0

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: e5aee043716890c42d6c434d62c94bcb022751137de150233d00b4ba7abdb948
4
- data.tar.gz: 1e57170b02d0bf497762c1f32f6787e3928c3f50b46142d9a53a75c13999aa55
3
+ metadata.gz: a02609d47ec49dddcc0200d7bb242470f30289c170bbdada5e5cc950f8b36de2
4
+ data.tar.gz: 3e0a9f005faaab4d530c3923b01b5cbf898f8c6e81f4d306bad217036aadb5b0
5
5
  SHA512:
6
- metadata.gz: 40d24ce3e319971d2819c21174022e261e1504b0192114328f93f1c92922f59cb47b09793a04f2806a51faea8bdbaf09f8309b8fb97bdbdd80e1161e4e17e101
7
- data.tar.gz: e0701bda3541f15ba567f0653e3ed69e7374bef76a352f8f166aeb6de2d3cbe4cc89de5e04691b3308b202c0a4b6010cc5eb8ef7e8a86119c8ee56d6848e517a
6
+ metadata.gz: ce008a49c906c8066fd7345b564cf69f073dc785fedca6107cca02db5f157fdf06596e24a0454942e05ed61abe92880d08bdf90d6f0382f7cf95bdad4d2f7625
7
+ data.tar.gz: fc6fc4ba37580abec0c97ee57661c012fa76b2a022cd37d9ad5a5e88b7aafb4bc2a6a3de1d8a584796a07684dc6f8b6d89f0300d42bb401ae3bc14ef0864f648
@@ -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,457 +1,458 @@
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
1
+ require 'p2p2/head'
2
+ require 'p2p2/hex'
3
+ require 'p2p2/version'
4
+ require 'socket'
5
+
6
+ ##
7
+ # P2p2::P1 - 内网里的任意应用,访问另一个内网里的应用服务端。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_times = 0
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 ] > 1800
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_times >= 5
127
+ raise e
128
+ end
129
+
130
+ sleep 5
131
+ add_closing( room )
132
+ @reconn_room_times += 1
133
+ return
134
+ end
135
+
136
+ @reconn_room_times = 0
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