p2p2 0.6.8 → 0.6.9

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.
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