p2p2 0.7.2 → 0.8.0

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