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