p2p2 0.6.5 → 0.6.6

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