p2p2 0.7.2 → 0.8.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: d6f2cc3e01ad7ec77c39871c38605d463def1e2bae20bc861ca8dbf90cc39c57
4
- data.tar.gz: a7496d3078b8ba0433b66c8ead4ac4f89464f9cbfc800c70ec476b70b6cd490c
3
+ metadata.gz: 2d61a93963d38a8a12abc64ff59efaf97c23d692c775182342e15cce62683db9
4
+ data.tar.gz: 16e1a886e2f9ce298b427cd042e26ccd5ab2792b21e6ed1999c828f91da4829a
5
5
  SHA512:
6
- metadata.gz: 9dbeb5e2d226794a9a1d83a234bb0eda5cff11106029579ebd0a13703e67ed9791d946c76402174a0dfe1101625cbd3ceeb31e256a9126cd18c58a62dac5baf3
7
- data.tar.gz: eb62e94f286de5f64aa03a34eb88fd9eda618aae1c75c24496d7d8158e23cb05db3400a0a7940d74d45878676240702ea55e0002b4eb4d4974931e4c40ade55c
6
+ metadata.gz: 29c45e0552472d58e051d904d841eee8ba9e984db951ff3c14d3d2a6c6602b672ab8e35d6d2df2e78142f09b0a57f1c160450cde091cd3ef17fe517445847657
7
+ data.tar.gz: 5fd511b5ce41357de53d921b47a9241bc4489b66250dc63245e1c407ead5ca21830b6a69cdfc4f3f01a10e1b0f2b0ac8eb799d5245dd529a0c3f885135471c18
data/lib/p2p2.rb CHANGED
@@ -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'
data/lib/p2p2/head.rb CHANGED
@@ -1,10 +1,28 @@
1
- module P2p2
2
- PACK_SIZE = 1448 # 包大小
3
- CHUNK_SIZE = PACK_SIZE * 1000 # 块大小
4
- REROOM_LIMIT = 100
5
- REP2P_LIMIT = 5 # p2p重试次数。到早了另一头还没从洞里出来,会吃ECONNREFUSED,不慌,再来一发。
6
- SET_TITLE = 1
7
- PAIRING = 2
8
- CTL_CLOSE_ROOM = [ 1 ].pack( 'C' )
9
- CTL_CLOSE_APP = [ 2 ].pack( 'C' )
10
- end
1
+ module P2p2
2
+ PACK_SIZE = 1328 # 包大小 1400(console MTU) - 8(PPPoE header) - 40(IPv6 header) - 8(UDP header) - 8(app/shadow id) - 8(pack id) = 1328
3
+ CHUNK_SIZE = PACK_SIZE * 1000 # 块大小
4
+ WBUFFS_LIMIT = 1000 # 写前上限,超过上限结一个块
5
+ WMEMS_LIMIT = 100_000 # 写后上限,到达上限暂停写
6
+ RESUME_BELOW = 50_000 # 降到多少以下恢复写
7
+ RECONN_AFTER = 5 # 发起连接后多少秒没收到响应,重连
8
+ CHECK_EXPIRE_INTERVAL = 900 # 检查过期间隔
9
+ EXPIRE_AFTER = 1800 # 多久过期
10
+ HEARTBEAT_INTERVAL = 59 # 心跳间隔
11
+ STATUS_INTERVAL = 0.3 # 发送状态间隔
12
+ SEND_STATUS_UNTIL = 20 # 持续的告之对面状态,直到没有流量往来,持续多少秒
13
+ PEER_ADDR = 1
14
+ HEARTBEAT = 2
15
+ A_NEW_APP = 3
16
+ PAIRED = 4
17
+ SHADOW_STATUS = 5
18
+ APP_STATUS = 6
19
+ MISS = 7
20
+ FIN1 = 8
21
+ GOT_FIN1 = 9
22
+ FIN2 = 10
23
+ GOT_FIN2 = 11
24
+ P1_FIN = 12
25
+ P2_FIN = 13
26
+ CTL_CLOSE_SOCK = [ 1 ].pack( 'C' )
27
+ CTL_RESUME = [ 2 ].pack( 'C' )
28
+ end
data/lib/p2p2/p1.rb CHANGED
@@ -1,459 +1,820 @@
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, Errno::ECONNRESET => e
124
- puts "read room #{ e.class } #{ Time.new }"
125
- raise e if @reconn_room_times >= REROOM_LIMIT
126
- @reconn_room_times += 1
127
- sleep 5
128
- add_closing( room )
129
- return
130
- rescue EOFError => e
131
- puts "read room #{ e.class } #{ Time.new }"
132
- sleep 5
133
- add_closing( room )
134
- return
135
- end
136
-
137
- @reconn_room_times = 0
138
- info = @infos[ room ]
139
- info[ :p2_sockaddr ] = data
140
- info[ :updated_at ] = Time.new
141
- new_p1
142
- end
143
-
144
- ##
145
- # read p1
146
- #
147
- def read_p1( p1 )
148
- begin
149
- data = p1.read_nonblock( PACK_SIZE )
150
- rescue IO::WaitReadable, Errno::EINTR, IO::WaitWritable
151
- return
152
- rescue Errno::ECONNREFUSED => e
153
- if @room_info[ :renew_p1_times ] >= REP2P_LIMIT
154
- raise e
155
- end
156
-
157
- sleep 1
158
- add_closing( p1 )
159
- info = @infos[ p1 ]
160
- info[ :need_renew ] = true
161
- @room_info[ :renew_p1_times ] += 1
162
- return
163
- rescue Exception => e
164
- add_closing( p1 )
165
- return
166
- end
167
-
168
- info = @infos[ p1 ]
169
-
170
- if info[ :app ].nil? || info[ :app ].closed?
171
- add_closing( p1 )
172
- return
173
- end
174
-
175
- if info[ :need_decode ]
176
- len = data[ 0, 2 ].unpack( 'n' ).first
177
- head = @hex.decode( data[ 2, len ] )
178
- data = head + data[ ( 2 + len )..-1 ]
179
- info[ :need_decode ] = false
180
- end
181
-
182
- add_write( info[ :app ], data )
183
- @room_info[ :updated_at ] = Time.new
184
- end
185
-
186
- ##
187
- # read app
188
- #
189
- def read_app( app )
190
- begin
191
- data = app.read_nonblock( PACK_SIZE )
192
- rescue IO::WaitReadable, Errno::EINTR, IO::WaitWritable
193
- return
194
- rescue Exception => e
195
- add_closing( app )
196
- return
197
- end
198
-
199
- info = @infos[ app ]
200
-
201
- if info[ :p1 ].nil? || info[ :p1 ].closed?
202
- add_closing( app )
203
- return
204
- end
205
-
206
- if info[ :need_encode ]
207
- data = @hex.encode( data )
208
- data = [ [ data.size ].pack( 'n' ), data ].join
209
- info[ :need_encode ] = false
210
- end
211
-
212
- add_write( info[ :p1 ], data )
213
- @room_info[ :updated_at ] = Time.new
214
- end
215
-
216
- ##
217
- # write room
218
- #
219
- def write_room( room )
220
- if @closings.include?( room )
221
- close_sock( room )
222
- new_room
223
- return
224
- end
225
-
226
- info = @infos[ room ]
227
- room.write( info[ :wbuff ] )
228
- @writes.delete( room )
229
- end
230
-
231
- ##
232
- # write p1
233
- #
234
- def write_p1( p1 )
235
- if @closings.include?( p1 )
236
- info = close_sock( p1 )
237
-
238
- if info[ :need_renew ]
239
- new_p1
240
- return
241
- end
242
-
243
- unless info[ :app ].closed?
244
- add_closing( info[ :app ] )
245
- end
246
-
247
- unless @room.closed?
248
- add_closing( @room )
249
- end
250
-
251
- return
252
- end
253
-
254
- info = @infos[ p1 ]
255
- data, from = get_buff( info )
256
-
257
- if data.empty?
258
- @writes.delete( p1 )
259
- return
260
- end
261
-
262
- begin
263
- written = p1.write_nonblock( data )
264
- rescue IO::WaitWritable, Errno::EINTR, IO::WaitReadable
265
- return
266
- rescue Exception => e
267
- add_closing( p1 )
268
- return
269
- end
270
-
271
- data = data[ written..-1 ]
272
- info[ from ] = data
273
- end
274
-
275
- ##
276
- # write app
277
- #
278
- def write_app( app )
279
- if @closings.include?( app )
280
- info = close_sock( app )
281
-
282
- unless info[ :p1 ].closed?
283
- add_closing( info[ :p1 ] )
284
- end
285
-
286
- return
287
- end
288
-
289
- info = @infos[ app ]
290
- data, from = get_buff( info )
291
-
292
- if data.empty?
293
- @writes.delete( app )
294
- return
295
- end
296
-
297
- begin
298
- written = app.write_nonblock( data )
299
- rescue IO::WaitWritable, Errno::EINTR, IO::WaitReadable
300
- return
301
- rescue Exception => e
302
- add_closing( app )
303
- return
304
- end
305
-
306
- data = data[ written..-1 ]
307
- info[ from ] = data
308
- end
309
-
310
- def get_buff( info )
311
- data, from = info[ :cache ], :cache
312
-
313
- if data.empty?
314
- if info[ :chunks ].any?
315
- path = File.join( info[ :chunk_dir ], info[ :chunks ].shift )
316
- data = info[ :cache ] = IO.binread( path )
317
-
318
- begin
319
- File.delete( path )
320
- rescue Errno::ENOENT
321
- end
322
- else
323
- data, from = info[ :wbuff ], :wbuff
324
- end
325
- end
326
-
327
- [ data, from ]
328
- end
329
-
330
- def add_closing( sock )
331
- unless @closings.include?( sock )
332
- @reads.delete( sock )
333
- @closings << sock
334
- end
335
-
336
- add_write( sock )
337
- end
338
-
339
- def add_write( sock, data = nil )
340
- if data
341
- info = @infos[ sock ]
342
- info[ :wbuff ] << data
343
-
344
- if info[ :wbuff ].size >= CHUNK_SIZE
345
- filename = [ info[ :filename ], info[ :chunk_seed ] ].join( '.' )
346
- chunk_path = File.join( info[ :chunk_dir ], filename )
347
- IO.binwrite( chunk_path, info[ :wbuff ] )
348
- info[ :chunks ] << filename
349
- info[ :chunk_seed ] += 1
350
- info[ :wbuff ].clear
351
- end
352
- end
353
-
354
- unless @writes.include?( sock )
355
- @writes << sock
356
- end
357
- end
358
-
359
- def close_sock( sock )
360
- sock.close
361
- @reads.delete( sock )
362
- @writes.delete( sock )
363
- @closings.delete( sock )
364
- @roles.delete( sock )
365
- info = @infos.delete( sock )
366
-
367
- if info && info[ :chunks ]
368
- info[ :chunks ].each do | filename |
369
- begin
370
- File.delete( File.join( info[ :chunk_dir ], filename ) )
371
- rescue Errno::ENOENT
372
- end
373
- end
374
- end
375
-
376
- info
377
- end
378
-
379
- def new_room
380
- room = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
381
- room.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1 )
382
- room.setsockopt( Socket::SOL_TCP, Socket::TCP_NODELAY, 1 )
383
-
384
- begin
385
- room.connect_nonblock( @roomd_sockaddr )
386
- rescue IO::WaitWritable, Errno::EINTR
387
- end
388
-
389
- title = @title.unpack( "C*" ).map{ | c | c.chr }.join
390
- room_info = {
391
- wbuff: [ [ SET_TITLE ].pack( 'C' ), title ].join,
392
- p2_sockaddr: nil,
393
- renew_p1_times: 0,
394
- updated_at: Time.new
395
- }
396
- @room = room
397
- @room_info = room_info
398
- @roles[ room ] = :room
399
- @infos[ room ] = room_info
400
- @reads << room
401
- @writes << room
402
- end
403
-
404
- def new_p1
405
- p1 = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
406
- p1.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1 )
407
- p1.setsockopt( Socket::SOL_TCP, Socket::TCP_NODELAY, 1 )
408
- p1.bind( @room.local_address ) # use the hole
409
-
410
- begin
411
- p1.connect_nonblock( @room_info[ :p2_sockaddr ] )
412
- rescue IO::WaitWritable, Errno::EINTR
413
- rescue Exception => e
414
- puts "connect p2 #{ e.class } #{ Time.new }"
415
- p1.close
416
- add_closing( @room )
417
- return
418
- end
419
-
420
- app = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
421
- app.setsockopt( Socket::SOL_TCP, Socket::TCP_NODELAY, 1 )
422
-
423
- begin
424
- app.connect_nonblock( @appd_sockaddr )
425
- rescue IO::WaitWritable, Errno::EINTR
426
- end
427
-
428
- p1_info = {
429
- wbuff: '',
430
- cache: '',
431
- filename: [ Process.pid, p1.object_id ].join( '-' ),
432
- chunk_dir: @p1_chunk_dir,
433
- chunks: [],
434
- chunk_seed: 0,
435
- need_decode: true,
436
- need_renew: false,
437
- app: app
438
- }
439
-
440
- app_info = {
441
- wbuff: '',
442
- cache: '',
443
- filename: [ Process.pid, app.object_id ].join( '-' ),
444
- chunk_dir: @app_chunk_dir,
445
- chunks: [],
446
- chunk_seed: 0,
447
- need_encode: true,
448
- p1: p1
449
- }
450
-
451
- @roles[ p1 ] = :p1
452
- @infos[ p1 ] = p1_info
453
- @reads << p1
454
- @roles[ app ] = :app
455
- @infos[ app ] = app_info
456
- @reads << app
457
- end
458
- end
459
- end
1
+ require 'p2p2/head'
2
+ require 'p2p2/hex'
3
+ require 'p2p2/version'
4
+ require 'socket'
5
+
6
+ ##
7
+ # P2p2::P1 - 内网里的任意应用,访问另一个内网里的应用服务端。p1端。
8
+ #
9
+ # 两套关闭
10
+ # ========
11
+ #
12
+ # 1-1. shadow.close -> ext.is_app_closed ? no -> send fin1 loop
13
+ # 1-2. recv got_fin1 -> break loop
14
+ # 1-3. recv fin2 -> send got_fin2 -> del ext
15
+ #
16
+ # 2-1. recv fin1 -> send got_fin1 -> ext.is_app_closed = true
17
+ # 2-2. all sent && ext.biggest_app_pack_id == ext.continue_app_pack_id -> add closing shadow
18
+ # 2-3. shadow.close -> ext.is_app_closed ? yes -> del ext -> loop send fin2
19
+ # 2-4. recv got_fin2 -> break loop
20
+ #
21
+ module P2p2
22
+ class P1
23
+
24
+ ##
25
+ # p2pd_host 配对服务器ip
26
+ # p2pd_port 配对服务器端口
27
+ # appd_host 任意的一个应用的ip
28
+ # appd_port 应用端口
29
+ # title 约定的房间名
30
+ # shadow_chunk_dir 文件缓存目录,缓存shadow来不及写的流量
31
+ # p1_chunk_dir 文件缓存目录,缓存p1来不及写的流量
32
+ #
33
+ def initialize( p2pd_host, p2pd_port, appd_host, appd_port, title, shadow_chunk_dir = '/tmp', p1_chunk_dir = '/tmp' )
34
+ @p2pd_sockaddr = Socket.sockaddr_in( p2pd_port, p2pd_host )
35
+ @appd_sockaddr = Socket.sockaddr_in( appd_port, appd_host )
36
+ @title = title
37
+ @shadow_chunk_dir = shadow_chunk_dir
38
+ @p1_chunk_dir = p1_chunk_dir
39
+ @hex = P2p2::Hex.new
40
+ @mutex = Mutex.new
41
+ @reads = []
42
+ @writes = []
43
+ @closings = []
44
+ @socks = {} # object_id => sock
45
+ @roles = {} # sock => :ctlr / :shadow / :p1
46
+ @infos = {}
47
+
48
+ ctlr, ctlw = IO.pipe
49
+ @ctlw = ctlw
50
+ @roles[ ctlr ] = :ctlr
51
+ add_read( ctlr )
52
+ end
53
+
54
+ def looping
55
+ puts 'looping'
56
+
57
+ new_p1
58
+
59
+ loop do
60
+ rs, ws = IO.select( @reads, @writes )
61
+
62
+ @mutex.synchronize do
63
+ rs.each do | sock |
64
+ case @roles[ sock ]
65
+ when :ctlr
66
+ read_ctlr( sock )
67
+ when :shadow
68
+ read_shadow( sock )
69
+ when :p1
70
+ read_p1( sock )
71
+ end
72
+ end
73
+
74
+ ws.each do | sock |
75
+ case @roles[ sock ]
76
+ when :shadow
77
+ write_shadow( sock )
78
+ when :p1
79
+ write_p1( sock )
80
+ end
81
+ end
82
+ end
83
+ end
84
+ rescue Interrupt => e
85
+ puts e.class
86
+ quit!
87
+ end
88
+
89
+ def quit!
90
+ if @p1 && !@p1.closed? && @p1_info[ :p2_addr ]
91
+ ctlmsg = [ 0, P1_FIN ].pack( 'Q>C' )
92
+ send_pack( @p1, ctlmsg, @p1_info[ :p2_addr ] )
93
+ end
94
+
95
+ exit
96
+ end
97
+
98
+ private
99
+
100
+ ##
101
+ # read ctlr
102
+ #
103
+ def read_ctlr( ctlr )
104
+ case ctlr.read( 1 )
105
+ when CTL_CLOSE_SOCK
106
+ sock_id = ctlr.read( 8 ).unpack( 'Q>' ).first
107
+ sock = @socks[ sock_id ]
108
+
109
+ if sock
110
+ puts "expire p1 #{ sock_id } #{ Time.new }"
111
+ add_closing( sock )
112
+ end
113
+ when CTL_RESUME
114
+ p1_id = ctlr.read( 8 ).unpack( 'Q>' ).first
115
+
116
+ puts "resume #{ p1_id } #{ Time.new }"
117
+ p1 = @socks[ p1_id ]
118
+
119
+ if p1
120
+ add_write( p1 )
121
+ end
122
+ end
123
+ end
124
+
125
+ ##
126
+ # read shadow
127
+ #
128
+ def read_shadow( shadow )
129
+ begin
130
+ data = shadow.read_nonblock( PACK_SIZE )
131
+ rescue IO::WaitReadable, Errno::EINTR
132
+ return
133
+ rescue Exception => e
134
+ add_closing( shadow )
135
+ return
136
+ end
137
+
138
+ info = @infos[ shadow ]
139
+ p1 = info[ :p1 ]
140
+
141
+ if p1.closed?
142
+ add_closing( shadow )
143
+ return
144
+ end
145
+
146
+ p1_info = @infos[ p1 ]
147
+ p1_info[ :wbuffs ] << [ shadow.object_id, data ]
148
+
149
+ if p1_info[ :wbuffs ].size >= WBUFFS_LIMIT
150
+ spring = p1_info[ :chunks ].size > 0 ? ( p1_info[ :spring ] + 1 ) : 0
151
+ filename = "#{ p1.object_id }.#{ spring }"
152
+ chunk_path = File.join( @p1_chunk_dir, filename )
153
+ IO.binwrite( chunk_path, p1_info[ :wbuffs ].map{ | shadow_id, data | "#{ [ shadow_id, data.bytesize ].pack( 'Q>n' ) }#{ data }" }.join )
154
+ p1_info[ :chunks ] << filename
155
+ p1_info[ :spring ] = spring
156
+ p1_info[ :wbuffs ].clear
157
+ end
158
+
159
+ unless p1_info[ :paused ]
160
+ add_write( p1 )
161
+ end
162
+ end
163
+
164
+ ##
165
+ # read p1
166
+ #
167
+ def read_p1( p1 )
168
+ data, addrinfo, rflags, *controls = p1.recvmsg
169
+ now = Time.new
170
+ info = @infos[ p1 ]
171
+ info[ :last_coming_at ] = now
172
+ app_id = data[ 0, 8 ].unpack( 'Q>' ).first
173
+
174
+ if app_id == 0
175
+ case data[ 8 ].unpack( 'C' ).first
176
+ when PEER_ADDR
177
+ return if addrinfo.to_sockaddr != @p2pd_sockaddr
178
+
179
+ unless info[ :p2_addr ]
180
+ # puts "debug peer addr #{ data[ 9..-1 ].inspect } #{ Time.new }"
181
+ info[ :p2_addr ] = data[ 9..-1 ]
182
+ loop_send_status( p1 )
183
+ end
184
+
185
+ ctlmsg = [ 0, HEARTBEAT, rand( 128 ) ].pack( 'Q>CC' )
186
+ send_pack( p1, ctlmsg, info[ :p2_addr ] )
187
+ when A_NEW_APP
188
+ return unless info[ :p2_addr ]
189
+
190
+ app_id = data[ 9, 8 ].unpack( 'Q>' ).first
191
+ shadow_id = info[ :app_ids ][ app_id ]
192
+
193
+ unless shadow_id
194
+ shadow = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
195
+ shadow.setsockopt( Socket::SOL_TCP, Socket::TCP_NODELAY, 1 )
196
+
197
+ begin
198
+ shadow.connect_nonblock( @appd_sockaddr )
199
+ rescue IO::WaitWritable, Errno::EINTR
200
+ end
201
+
202
+ shadow_id = shadow.object_id
203
+
204
+ @socks[ shadow_id ] = shadow
205
+ @roles[ shadow ] = :shadow
206
+ @infos[ shadow ] = {
207
+ wbuff: '', # 写前缓存
208
+ cache: '', # 块读出缓存
209
+ chunks: [], # 块队列,写前达到块大小时结一个块 filename
210
+ spring: 0, # 块后缀,结块时,如果块队列不为空,则自增,为空,则置为0
211
+ p1: p1
212
+ }
213
+
214
+ info[ :shadows ] << shadow
215
+ info[ :shadow_exts ][ shadow_id ] = {
216
+ shadow: shadow,
217
+ wmems: {}, # 写后缓存 pack_id => data
218
+ send_ats: {}, # 上一次发出时间 pack_id => send_at
219
+ biggest_pack_id: 0, # 发到几
220
+ continue_app_pack_id: 0, # 收到几
221
+ pieces: {}, # 跳号包 app_pack_id => data
222
+ app_id: app_id, # 对面id
223
+ is_app_closed: false, # 对面是否已关闭
224
+ biggest_app_pack_id: 0, # 对面发到几
225
+ completed_pack_id: 0, # 完成到几(对面收到几)
226
+ last_traffic_at: nil # 有流量发出,或者有更新收到几,时间戳
227
+ }
228
+ info[ :app_ids ][ app_id ] = shadow_id
229
+ add_read( shadow )
230
+ end
231
+
232
+ ctlmsg = [
233
+ 0,
234
+ PAIRED,
235
+ app_id,
236
+ shadow_id
237
+ ].pack( 'Q>CQ>Q>' )
238
+
239
+ # puts "debug send PAIRED #{ app_id } #{ shadow_id } #{ Time.new }"
240
+ send_pack( p1, ctlmsg, info[ :p2_addr ] )
241
+ when APP_STATUS
242
+ app_id, biggest_app_pack_id, continue_shadow_pack_id = data[ 9, 24 ].unpack( 'Q>Q>Q>' )
243
+ shadow_id = info[ :app_ids ][ app_id ]
244
+ return unless shadow_id
245
+
246
+ ext = info[ :shadow_exts ][ shadow_id ]
247
+ return unless ext
248
+
249
+ # 更新对面发到几
250
+ if biggest_app_pack_id > ext[ :biggest_app_pack_id ]
251
+ ext[ :biggest_app_pack_id ] = biggest_app_pack_id
252
+ end
253
+
254
+ # 更新对面收到几,释放写后
255
+ if continue_shadow_pack_id > ext[ :completed_pack_id ]
256
+ pack_ids = ext[ :wmems ].keys.select { | pack_id | pack_id <= continue_shadow_pack_id }
257
+
258
+ pack_ids.each do | pack_id |
259
+ ext[ :wmems ].delete( pack_id )
260
+ ext[ :send_ats ].delete( pack_id )
261
+ end
262
+
263
+ ext[ :completed_pack_id ] = continue_shadow_pack_id
264
+ end
265
+
266
+ if ext[ :is_app_closed ] && ( ext[ :biggest_app_pack_id ] == ext[ :continue_app_pack_id ] )
267
+ add_write( ext[ :shadow ] )
268
+ return
269
+ end
270
+
271
+ # 发miss
272
+ if !ext[ :shadow ].closed? && ( ext[ :continue_app_pack_id ] < ext[ :biggest_app_pack_id ] )
273
+ ranges = []
274
+ curr_pack_id = ext[ :continue_app_pack_id ] + 1
275
+
276
+ ext[ :pieces ].keys.sort.each do | pack_id |
277
+ if pack_id > curr_pack_id
278
+ ranges << [ curr_pack_id, pack_id - 1 ]
279
+ end
280
+
281
+ curr_pack_id = pack_id + 1
282
+ end
283
+
284
+ if curr_pack_id <= ext[ :biggest_app_pack_id ]
285
+ ranges << [ curr_pack_id, ext[ :biggest_app_pack_id ] ]
286
+ end
287
+
288
+ # puts "debug #{ ext[ :continue_app_pack_id ] }/#{ ext[ :biggest_app_pack_id ] } send MISS #{ ranges.size }"
289
+ ranges.each do | pack_id_begin, pack_id_end |
290
+ ctlmsg = [
291
+ 0,
292
+ MISS,
293
+ app_id,
294
+ pack_id_begin,
295
+ pack_id_end
296
+ ].pack( 'Q>CQ>Q>Q>' )
297
+
298
+ send_pack( p1, ctlmsg, info[ :p2_addr ] )
299
+ end
300
+ end
301
+ when MISS
302
+ shadow_id, pack_id_begin, pack_id_end = data[ 9, 24 ].unpack( 'Q>Q>Q>' )
303
+ ext = info[ :shadow_exts ][ shadow_id ]
304
+ return unless ext
305
+
306
+ ( pack_id_begin..pack_id_end ).each do | pack_id |
307
+ send_at = ext[ :send_ats ][ pack_id ]
308
+
309
+ if send_at
310
+ break if now - send_at < STATUS_INTERVAL
311
+
312
+ info[ :resendings ] << [ shadow_id, pack_id ]
313
+ end
314
+ end
315
+
316
+ add_write( p1 )
317
+ when FIN1
318
+ app_id = data[ 9, 8 ].unpack( 'Q>' ).first
319
+ ctlmsg = [
320
+ 0,
321
+ GOT_FIN1,
322
+ app_id
323
+ ].pack( 'Q>CQ>' )
324
+
325
+ # puts "debug 2-1. recv fin1 -> send got_fin1 -> ext.is_app_closed = true #{ app_id } #{ Time.new }"
326
+ send_pack( p1, ctlmsg, info[ :p2_addr ] )
327
+
328
+ shadow_id = info[ :app_ids ][ app_id ]
329
+ return unless shadow_id
330
+
331
+ ext = info[ :shadow_exts ][ shadow_id ]
332
+ return unless ext
333
+
334
+ ext[ :is_app_closed ] = true
335
+ when GOT_FIN1
336
+ # puts "debug 1-2. recv got_fin1 -> break loop #{ Time.new }"
337
+ shadow_id = data[ 9, 8 ].unpack( 'Q>' ).first
338
+ info[ :fin1s ].delete( shadow_id )
339
+ when FIN2
340
+ # puts "debug 1-3. recv fin2 -> send got_fin2 -> del ext #{ Time.new }"
341
+ app_id = data[ 9, 8 ].unpack( 'Q>' ).first
342
+ ctlmsg = [
343
+ 0,
344
+ GOT_FIN2,
345
+ app_id
346
+ ].pack( 'Q>CQ>' )
347
+
348
+ send_pack( p1, ctlmsg, info[ :p2_addr ] )
349
+
350
+ shadow_id = info[ :app_ids ].delete( app_id )
351
+ return unless shadow_id
352
+
353
+ info[ :shadow_exts ].delete( shadow_id )
354
+ when GOT_FIN2
355
+ # puts "debug 2-4. recv got_fin2 -> break loop #{ Time.new }"
356
+ shadow_id = data[ 9, 8 ].unpack( 'Q>' ).first
357
+ info[ :fin2s ].delete( shadow_id )
358
+ when P2_FIN
359
+ puts "p2 fin #{ Time.new }"
360
+ add_closing( p1 )
361
+ end
362
+
363
+ return
364
+ end
365
+
366
+ shadow_id = info[ :app_ids ][ app_id ]
367
+ return unless shadow_id
368
+
369
+ ext = info[ :shadow_exts ][ shadow_id ]
370
+ return if ext.nil? || ext[ :shadow ].closed?
371
+
372
+ pack_id = data[ 8, 8 ].unpack( 'Q>' ).first
373
+ return if ( pack_id <= ext[ :continue_app_pack_id ] ) || ext[ :pieces ].include?( pack_id )
374
+
375
+ data = data[ 16..-1 ]
376
+
377
+ # 解混淆
378
+ if pack_id == 1
379
+ data = @hex.decode( data )
380
+ end
381
+
382
+ # 放进shadow的写前缓存,跳号放碎片缓存
383
+ if pack_id - ext[ :continue_app_pack_id ] == 1
384
+ while ext[ :pieces ].include?( pack_id + 1 )
385
+ data << ext[ :pieces ].delete( pack_id + 1 )
386
+ pack_id += 1
387
+ end
388
+
389
+ ext[ :continue_app_pack_id ] = pack_id
390
+ ext[ :last_traffic_at ] = now
391
+
392
+ shadow_info = @infos[ ext[ :shadow ] ]
393
+ shadow_info[ :wbuff ] << data
394
+
395
+ if shadow_info[ :wbuff ].bytesize >= CHUNK_SIZE
396
+ spring = shadow_info[ :chunks ].size > 0 ? ( shadow_info[ :spring ] + 1 ) : 0
397
+ filename = "#{ shadow_id }.#{ spring }"
398
+ chunk_path = File.join( @shadow_chunk_dir, filename )
399
+ IO.binwrite( chunk_path, shadow_info[ :wbuff ] )
400
+ shadow_info[ :chunks ] << filename
401
+ shadow_info[ :spring ] = spring
402
+ shadow_info[ :wbuff ].clear
403
+ end
404
+
405
+ add_write( ext[ :shadow ] )
406
+ else
407
+ ext[ :pieces ][ pack_id ] = data
408
+ end
409
+ end
410
+
411
+ ##
412
+ # write shadow
413
+ #
414
+ def write_shadow( shadow )
415
+ if @closings.include?( shadow )
416
+ close_shadow( shadow )
417
+ return
418
+ end
419
+
420
+ info = @infos[ shadow ]
421
+
422
+ # 取写前
423
+ data = info[ :cache ]
424
+ from = :cache
425
+
426
+ if data.empty?
427
+ if info[ :chunks ].any?
428
+ path = File.join( @shadow_chunk_dir, info[ :chunks ].shift )
429
+
430
+ begin
431
+ data = IO.binread( path )
432
+ File.delete( path )
433
+ rescue Errno::ENOENT
434
+ add_closing( shadow )
435
+ return
436
+ end
437
+ else
438
+ data = info[ :wbuff ]
439
+ from = :wbuff
440
+ end
441
+ end
442
+
443
+ if data.empty?
444
+ p1 = info[ :p1 ]
445
+
446
+ if p1.closed?
447
+ add_closing( shadow )
448
+ return
449
+ end
450
+
451
+ p1_info = @infos[ p1 ]
452
+ ext = p1_info[ :shadow_exts ][ shadow.object_id ]
453
+
454
+ if ext[ :is_app_closed ] && ( ext[ :biggest_app_pack_id ] == ext[ :continue_app_pack_id ] )
455
+ # puts "debug 2-2. all sent && ext.biggest_app_pack_id == ext.continue_app_pack_id -> add closing shadow #{ Time.new }"
456
+ add_closing( shadow )
457
+ return
458
+ end
459
+
460
+ @writes.delete( shadow )
461
+ return
462
+ end
463
+
464
+ begin
465
+ written = shadow.write_nonblock( data )
466
+ rescue IO::WaitWritable, Errno::EINTR => e
467
+ info[ from ] = data
468
+ return
469
+ rescue Exception => e
470
+ add_closing( shadow )
471
+ return
472
+ end
473
+
474
+ data = data[ written..-1 ]
475
+ info[ from ] = data
476
+ end
477
+
478
+ ##
479
+ # write p1
480
+ #
481
+ def write_p1( p1 )
482
+ if @closings.include?( p1 )
483
+ close_p1( p1 )
484
+ new_p1
485
+ return
486
+ end
487
+
488
+ now = Time.new
489
+ info = @infos[ p1 ]
490
+
491
+ # 重传
492
+ while info[ :resendings ].any?
493
+ shadow_id, pack_id = info[ :resendings ].shift
494
+ ext = info[ :shadow_exts ][ shadow_id ]
495
+
496
+ if ext
497
+ pack = ext[ :wmems ][ pack_id ]
498
+
499
+ if pack
500
+ send_pack( p1, pack, info[ :p2_addr ] )
501
+ ext[ :last_traffic_at ] = now
502
+ return
503
+ end
504
+ end
505
+ end
506
+
507
+ # 若写后到达上限,暂停取写前
508
+ if info[ :shadow_exts ].map{ | _, ext | ext[ :wmems ].size }.sum >= WMEMS_LIMIT
509
+ unless info[ :paused ]
510
+ puts "pause #{ Time.new }"
511
+ info[ :paused ] = true
512
+ end
513
+
514
+ @writes.delete( p1 )
515
+ return
516
+ end
517
+
518
+ # 取写前
519
+ if info[ :caches ].any?
520
+ shadow_id, data = info[ :caches ].shift
521
+ elsif info[ :chunks ].any?
522
+ path = File.join( @p1_chunk_dir, info[ :chunks ].shift )
523
+
524
+ begin
525
+ data = IO.binread( path )
526
+ File.delete( path )
527
+ rescue Errno::ENOENT
528
+ add_closing( p1 )
529
+ return
530
+ end
531
+
532
+ caches = []
533
+
534
+ until data.empty?
535
+ shadow_id, pack_size = data[ 0, 10 ].unpack( 'Q>n' )
536
+ caches << [ shadow_id, data[ 10, pack_size ] ]
537
+ data = data[ ( 10 + pack_size )..-1 ]
538
+ end
539
+
540
+ shadow_id, data = caches.shift
541
+ info[ :caches ] = caches
542
+ elsif info[ :wbuffs ].any?
543
+ shadow_id, data = info[ :wbuffs ].shift
544
+ else
545
+ @writes.delete( p1 )
546
+ return
547
+ end
548
+
549
+ ext = info[ :shadow_exts ][ shadow_id ]
550
+
551
+ if ext
552
+ pack_id = ext[ :biggest_pack_id ] + 1
553
+
554
+ if pack_id == 1
555
+ data = @hex.encode( data )
556
+ end
557
+
558
+ pack = "#{ [ shadow_id, pack_id ].pack( 'Q>Q>' ) }#{ data }"
559
+ send_pack( p1, pack, info[ :p2_addr ] )
560
+ ext[ :biggest_pack_id ] = pack_id
561
+ ext[ :wmems ][ pack_id ] = pack
562
+ ext[ :send_ats ][ pack_id ] = now
563
+ ext[ :last_traffic_at ] = now
564
+ end
565
+ end
566
+
567
+ def new_p1
568
+ p1 = Socket.new( Socket::AF_INET, Socket::SOCK_DGRAM, 0 )
569
+ p1.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1 )
570
+ p1.bind( Socket.sockaddr_in( 0, '0.0.0.0' ) )
571
+
572
+ p1_info = {
573
+ wbuffs: [], # 写前缓存 [ shadow_id, data ]
574
+ caches: [], # 块读出缓存 [ shadow_id, data ]
575
+ chunks: [], # 块队列 filename
576
+ spring: 0, # 块后缀,结块时,如果块队列不为空,则自增,为空,则置为0
577
+ p2_addr: nil, # 远端地址
578
+ app_ids: {}, # app_id => shadow_id
579
+ shadows: [], # 开着的shadow
580
+ shadow_exts: {}, # 传输相关 shadow_id => {}
581
+ fin1s: [], # fin1: shadow已关闭,等待对面收完流量 shadow_id
582
+ fin2s: [], # fin2: 流量已收完 shadow_id
583
+ last_coming_at: nil, # 上一次来流量的时间
584
+ paused: false, # 是否暂停写
585
+ resendings: [] # 重传队列 [ shadow_id, pack_id ]
586
+ }
587
+
588
+ @p1 = p1
589
+ @p1_info = p1_info
590
+ @socks[ p1.object_id ] = p1
591
+ @roles[ p1 ] = :p1
592
+ @infos[ p1 ] = p1_info
593
+
594
+ send_pack( p1, @title, @p2pd_sockaddr )
595
+ add_read( p1 )
596
+ check_reconn( p1 )
597
+ loop_expire( p1 )
598
+ end
599
+
600
+ def check_reconn( p1 )
601
+ Thread.new do
602
+ sleep RECONN_AFTER
603
+
604
+ unless p1.closed?
605
+ p1_info = @infos[ p1 ]
606
+
607
+ unless p1_info[ :p2_addr ]
608
+ @mutex.synchronize do
609
+ puts "reconn #{ Time.new }"
610
+ @ctlw.write( [ CTL_CLOSE_SOCK, [ p1.object_id ].pack( 'Q>' ) ].join )
611
+ end
612
+ end
613
+ end
614
+ end
615
+ end
616
+
617
+ def loop_expire( p1 )
618
+ Thread.new do
619
+ loop do
620
+ sleep CHECK_EXPIRE_INTERVAL
621
+
622
+ break if p1.closed?
623
+
624
+ p1_info = @infos[ p1 ]
625
+
626
+ if p1_info[ :last_coming_at ] && ( Time.new - p1_info[ :last_coming_at ] > EXPIRE_AFTER )
627
+ @mutex.synchronize do
628
+ @ctlw.write( [ CTL_CLOSE_SOCK, [ p1.object_id ].pack( 'Q>' ) ].join )
629
+ end
630
+ end
631
+ end
632
+ end
633
+ end
634
+
635
+ def loop_send_status( p1 )
636
+ Thread.new do
637
+ loop do
638
+ sleep STATUS_INTERVAL
639
+
640
+ if p1.closed?
641
+ # puts "debug p1 is closed, break send status loop #{ Time.new }"
642
+ break
643
+ end
644
+
645
+ p1_info = @infos[ p1 ]
646
+
647
+ if p1_info[ :shadow_exts ].any?
648
+ @mutex.synchronize do
649
+ now = Time.new
650
+
651
+ p1_info[ :shadow_exts ].each do | shadow_id, ext |
652
+ if ext[ :last_traffic_at ] && ( now - ext[ :last_traffic_at ] < SEND_STATUS_UNTIL )
653
+ ctlmsg = [
654
+ 0,
655
+ SHADOW_STATUS,
656
+ shadow_id,
657
+ ext[ :biggest_pack_id ],
658
+ ext[ :continue_app_pack_id ]
659
+ ].pack( 'Q>CQ>Q>Q>' )
660
+
661
+ send_pack( p1, ctlmsg, p1_info[ :p2_addr ] )
662
+ end
663
+ end
664
+ end
665
+ end
666
+
667
+ if p1_info[ :paused ] && ( p1_info[ :shadow_exts ].map{ | _, ext | ext[ :wmems ].size }.sum < RESUME_BELOW )
668
+ @mutex.synchronize do
669
+ @ctlw.write( [ CTL_RESUME, [ p1.object_id ].pack( 'Q>' ) ].join )
670
+ p1_info[ :paused ] = false
671
+ end
672
+ end
673
+ end
674
+ end
675
+ end
676
+
677
+ def loop_send_fin1( p1, shadow_id )
678
+ Thread.new do
679
+ 100.times do
680
+ break if p1.closed?
681
+
682
+ p1_info = @infos[ p1 ]
683
+
684
+ unless p1_info[ :fin1s ].include?( shadow_id )
685
+ # puts "debug break send fin1 loop #{ Time.new }"
686
+ break
687
+ end
688
+
689
+ @mutex.synchronize do
690
+ ctlmsg = [
691
+ 0,
692
+ FIN1,
693
+ shadow_id
694
+ ].pack( 'Q>CQ>' )
695
+
696
+ # puts "debug send FIN1 #{ shadow_id } #{ Time.new }"
697
+ send_pack( p1, ctlmsg, p1_info[ :p2_addr ] )
698
+ end
699
+
700
+ sleep 1
701
+ end
702
+ end
703
+ end
704
+
705
+ def loop_send_fin2( p1, shadow_id )
706
+ Thread.new do
707
+ 100.times do
708
+ break if p1.closed?
709
+
710
+ p1_info = @infos[ p1 ]
711
+
712
+ unless p1_info[ :fin2s ].include?( shadow_id )
713
+ # puts "debug break send fin2 loop #{ Time.new }"
714
+ break
715
+ end
716
+
717
+ @mutex.synchronize do
718
+ ctlmsg = [
719
+ 0,
720
+ FIN2,
721
+ shadow_id
722
+ ].pack( 'Q>CQ>' )
723
+
724
+ # puts "debug send FIN2 #{ shadow_id } #{ Time.new }"
725
+ send_pack( p1, ctlmsg, p1_info[ :p2_addr ] )
726
+ end
727
+
728
+ sleep 1
729
+ end
730
+ end
731
+ end
732
+
733
+ def send_pack( sock, data, target_sockaddr )
734
+ begin
735
+ sock.sendmsg( data, 0, target_sockaddr )
736
+ rescue IO::WaitWritable, Errno::EINTR => e
737
+ puts "sendmsg #{ e.class } #{ Time.new }"
738
+ end
739
+ end
740
+
741
+ def add_read( sock )
742
+ return if sock.closed? || @reads.include?( sock )
743
+
744
+ @reads << sock
745
+ end
746
+
747
+ def add_write( sock, data = nil )
748
+ return if sock.closed? || @writes.include?( sock )
749
+
750
+ @writes << sock
751
+ end
752
+
753
+ def add_closing( sock )
754
+ return if sock.closed? || @closings.include?( sock )
755
+
756
+ @reads.delete( sock )
757
+ @closings << sock
758
+ add_write( sock )
759
+ end
760
+
761
+ def close_p1( p1 )
762
+ info = close_sock( p1 )
763
+ info[ :shadows ].each { | shadow | add_closing( shadow ) }
764
+ end
765
+
766
+ def close_shadow( shadow )
767
+ info = close_sock( shadow )
768
+ p1 = info[ :p1 ]
769
+ return if p1.closed?
770
+
771
+ p1_info = @infos[ p1 ]
772
+ p1_info[ :shadows ].delete( shadow )
773
+ shadow_id = shadow.object_id
774
+ ext = p1_info[ :shadow_exts ][ shadow_id ]
775
+ return unless ext
776
+
777
+ if ext[ :is_app_closed ]
778
+ # puts "debug 2-3. shadow.close -> ext.is_app_closed ? yes -> del ext -> loop send fin2 #{ Time.new }"
779
+ p1_info[ :app_ids ].delete( ext[ :app_id ] )
780
+ p1_info[ :shadow_exts ].delete( shadow_id )
781
+
782
+ unless p1_info[ :fin2s ].include?( shadow_id )
783
+ p1_info[ :fin2s ] << shadow_id
784
+ loop_send_fin2( p1, shadow_id )
785
+ end
786
+ else
787
+ # puts "debug 1-1. shadow.close -> ext.is_app_closed ? no -> send fin1 loop #{ Time.new }"
788
+
789
+ if p1_info[ :p2_addr ] && !p1_info[ :fin1s ].include?( shadow_id )
790
+ p1_info[ :fin1s ] << shadow_id
791
+ loop_send_fin1( p1, shadow_id )
792
+ end
793
+ end
794
+ end
795
+
796
+ def close_sock( sock )
797
+ sock.close
798
+ @reads.delete( sock )
799
+ @writes.delete( sock )
800
+ @closings.delete( sock )
801
+ @socks.delete( sock.object_id )
802
+ role = @roles.delete( sock )
803
+ info = @infos.delete( sock )
804
+
805
+ if info
806
+ chunk_dir = ( role == :shadow ? @shadow_chunk_dir : @p1_chunk_dir )
807
+
808
+ info[ :chunks ].each do | filename |
809
+ begin
810
+ File.delete( File.join( chunk_dir, filename ) )
811
+ rescue Errno::ENOENT
812
+ end
813
+ end
814
+ end
815
+
816
+ info
817
+ end
818
+
819
+ end
820
+ end