p2p2 0.6.5 → 0.6.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,222 +1,294 @@
1
- require 'p2p2/head'
2
- require 'p2p2/version'
3
- require 'socket'
4
-
5
- ##
6
- # P2p2::P2pd - 处于各自nat里的两端p2p。匹配服务器端。
7
- #
8
- #```
9
- # p2pd p2pd
10
- # ^ ^
11
- # ^ ^
12
- # ssh --> p2 --> encode --> nat --> nat --> p1 --> decode --> sshd
13
- #
14
- #```
15
- #
16
- # usage
17
- # =====
18
- #
19
- # 1. Girl::P2pd.new( 5050 ).looping # @server
20
- #
21
- # 2. Girl::P1.new( 'your.server.ip', 5050, '127.0.0.1', 22, '周立波' ).looping # @home1
22
- #
23
- # 3. Girl::P2.new( 'your.server.ip', 5050, '0.0.0.0', 2222, '周立波' ).looping # @home2
24
- #
25
- # 4. ssh -p2222 libo@localhost
26
- #
27
- module P2p2
28
- class P2pd
29
-
30
- ##
31
- # roomd_port 配对服务器端口
32
- # roomd_dir 可在该目录下看到所有的p1
33
- def initialize( roomd_port = 5050, roomd_dir = '/tmp' )
34
- @roomd_port = roomd_port
35
- @roomd_dir = roomd_dir
36
- @mutex = Mutex.new
37
- @roles = {} # sock => :roomd / :room
38
- @pending_p1s = {} # title => room
39
- @pending_p2s = {} # title => room
40
- @infos = {}
41
- @reads = []
42
-
43
- new_roomd
44
- end
45
-
46
- def looping
47
- puts 'looping'
48
-
49
- loop_expire
50
-
51
- loop do
52
- rs, _ = IO.select( @reads )
53
-
54
- @mutex.synchronize do
55
- rs.each do | sock |
56
- case @roles[ sock ]
57
- when :roomd
58
- read_roomd( sock )
59
- when :room
60
- read_room( sock )
61
- end
62
- end
63
- end
64
- end
65
- rescue Interrupt => e
66
- puts e.class
67
- quit!
68
- end
69
-
70
- def quit!
71
- exit
72
- end
73
-
74
- private
75
-
76
- def loop_expire
77
- Thread.new do
78
- loop do
79
- sleep 900
80
-
81
- if @infos.any?
82
- @mutex.synchronize do
83
- now = Time.new
84
-
85
- @infos.select{ | _, info | info[ :last_coming_at ] && ( now - info[ :last_coming_at ] > 1800 ) }.each do | room, _ |
86
- close_sock( room )
87
- end
88
- end
89
- end
90
- end
91
- end
92
- end
93
-
94
- def read_roomd( sock )
95
- begin
96
- room, addr = sock.accept_nonblock
97
- rescue IO::WaitReadable, Errno::EINTR
98
- return
99
- end
100
-
101
- @roles[ room ] = :room
102
- @infos[ room ] = {
103
- title: nil,
104
- last_coming_at: nil,
105
- sockaddr: addr.to_sockaddr
106
- }
107
- @reads << room
108
- end
109
-
110
- def read_room( sock )
111
- begin
112
- data = sock.read_nonblock( PACK_SIZE )
113
- rescue IO::WaitReadable, Errno::EINTR, IO::WaitWritable
114
- return
115
- rescue Exception => e
116
- close_sock( sock )
117
- return
118
- end
119
-
120
- info = @infos[ sock ]
121
- info[ :last_coming_at ] = Time.new
122
-
123
- until data.empty?
124
- ctl_num = data[ 0 ].unpack( 'C' ).first
125
-
126
- case ctl_num
127
- when HEARTBEAT
128
- data = data[ 1..-1 ]
129
- when SET_TITLE
130
- len = data[ 1, 2 ].unpack( 'n' ).first
131
-
132
- if len > 255
133
- puts "title too long"
134
- close_sock( sock )
135
- return
136
- end
137
-
138
- title = data[ 3, len ]
139
-
140
- if @pending_p2s.include?( title )
141
- p2 = @pending_p2s[ title ]
142
- p2_info = @infos[ p2 ]
143
- sock.write( p2_info[ :sockaddr ] )
144
- p2.write( info[ :sockaddr ] )
145
- elsif @pending_p1s.include?( title )
146
- puts "pending p1 #{ title.inspect } already exist"
147
- close_sock( sock )
148
- return
149
- else
150
- @pending_p1s[ title ] = sock
151
- info[ :title ] = title
152
-
153
- begin
154
- File.open( File.join( @roomd_dir, title ), 'w' )
155
- rescue Errno::ENOENT, ArgumentError => e
156
- puts "open title path #{ e.class }"
157
- close_sock( sock )
158
- return
159
- end
160
- end
161
-
162
- data = data[ ( 3 + len )..-1 ]
163
- when PAIRING
164
- len = data[ 1, 2 ].unpack( 'n' ).first
165
-
166
- if len > 255
167
- puts 'pairing title too long'
168
- close_sock( sock )
169
- return
170
- end
171
-
172
- title = data[ 3, len ]
173
-
174
- if @pending_p1s.include?( title )
175
- p1 = @pending_p1s[ title ]
176
- p1_info = @infos[ p1 ]
177
- sock.write( p1_info[ :sockaddr ] )
178
- p1.write( info[ :sockaddr ] )
179
- elsif @pending_p2s.include?( title )
180
- puts "pending p2 #{ title.inspect } already exist"
181
- close_sock( sock )
182
- return
183
- else
184
- @pending_p2s[ title ] = sock
185
- info[ :title ] = title
186
- end
187
-
188
- data = data[ ( 3 + len )..-1 ]
189
- end
190
- end
191
- end
192
-
193
- def close_sock( sock )
194
- sock.close
195
- @roles.delete( sock )
196
- @reads.delete( sock )
197
- info = @infos.delete( sock )
198
-
199
- if info && info[ :title ]
200
- @pending_p1s.delete( info[ :title ] )
201
- @pending_p2s.delete( info[ :title ] )
202
-
203
- begin
204
- File.delete( File.join( @roomd_dir, info[ :title ] ) )
205
- rescue Errno::ENOENT
206
- end
207
- end
208
-
209
- info
210
- end
211
-
212
- def new_roomd
213
- roomd = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
214
- roomd.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1 )
215
- roomd.bind( Socket.pack_sockaddr_in( @roomd_port, '0.0.0.0' ) )
216
- roomd.listen( 511 )
217
-
218
- @roles[ roomd ] = :roomd
219
- @reads << roomd
220
- end
221
- end
222
- end
1
+ require 'p2p2/head'
2
+ require 'p2p2/version'
3
+ require 'socket'
4
+
5
+ ##
6
+ # P2p2::P2pd - 处于nat里的任意应用,访问处于另一个nat里的应用服务端,借助一根p2p管道。配对服务器端。
7
+ #
8
+ #```
9
+ # p2pd p2pd
10
+ # ^ ^
11
+ # ^ ^
12
+ # ssh --> p2 --> encode --> nat --> nat --> p1 --> decode --> sshd
13
+ #
14
+ #```
15
+ #
16
+ # usage
17
+ # =====
18
+ #
19
+ # 1. Girl::P2pd.new( 5050 ).looping # @server
20
+ #
21
+ # 2. Girl::P1.new( 'your.server.ip', 5050, '127.0.0.1', 22, '周立波' ).looping # @home1
22
+ #
23
+ # 3. Girl::P2.new( 'your.server.ip', 5050, '0.0.0.0', 2222, '周立波' ).looping # @home2
24
+ #
25
+ # 4. ssh -p2222 libo@localhost
26
+ #
27
+ module P2p2
28
+ class P2pd
29
+
30
+ ##
31
+ # roomd_port 配对服务器端口
32
+ # roomd_dir 可在该目录下看到所有的p1
33
+ def initialize( roomd_port = 5050, roomd_dir = '/tmp' )
34
+ @roomd_port = roomd_port
35
+ @roomd_dir = roomd_dir
36
+ @mutex = Mutex.new
37
+ @reads = []
38
+ @writes = []
39
+ @closings = []
40
+ @rooms = {} # object_id => room
41
+ @pending_p1s = {} # title => room
42
+ @pending_p2s = {} # title => room
43
+ @roles = {} # sock => :roomd / :room
44
+ @infos = {}
45
+
46
+ ctlr, ctlw = IO.pipe
47
+ @ctlw = ctlw
48
+ @reads << ctlr
49
+ @roles[ ctlr ] = :ctlr
50
+
51
+ new_roomd
52
+ end
53
+
54
+ def looping
55
+ puts 'looping'
56
+
57
+ loop_expire
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 :roomd
68
+ read_roomd( sock )
69
+ when :room
70
+ read_room( sock )
71
+ end
72
+ end
73
+
74
+ ws.each do | sock |
75
+ case @roles[ sock ]
76
+ when :room
77
+ write_room( sock )
78
+ end
79
+ end
80
+ end
81
+ end
82
+ rescue Interrupt => e
83
+ puts e.class
84
+ quit!
85
+ end
86
+
87
+ def quit!
88
+ exit
89
+ end
90
+
91
+ private
92
+
93
+ def loop_expire
94
+ Thread.new do
95
+ loop do
96
+ sleep 900
97
+
98
+ if @infos.any?
99
+ @mutex.synchronize do
100
+ now = Time.new
101
+
102
+ @infos.select{ | _, info | now - info[ :updated_at ] > 1800 }.each do | room, _ |
103
+ @ctlw.write( [ CTL_CLOSE_ROOM, [ room.object_id ].pack( 'N' ) ].join )
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
110
+
111
+ def read_ctlr( sock )
112
+ case sock.read( 1 )
113
+ when CTL_CLOSE_ROOM
114
+ room_id = sock.read( 4 ).unpack( 'N' ).first
115
+ room = @rooms[ room_id ]
116
+
117
+ if room
118
+ add_closing( @rooms[ room_id ] )
119
+ end
120
+ end
121
+ end
122
+
123
+ def read_roomd( sock )
124
+ begin
125
+ room, addr = sock.accept_nonblock
126
+ rescue IO::WaitReadable, Errno::EINTR
127
+ return
128
+ end
129
+
130
+ @rooms[ room.object_id ] = room
131
+ @roles[ room ] = :room
132
+ @infos[ room ] = {
133
+ title: nil,
134
+ wbuff: '',
135
+ updated_at: Time.new,
136
+ sockaddr: addr.to_sockaddr
137
+ }
138
+ @reads << room
139
+ end
140
+
141
+ def read_room( sock )
142
+ begin
143
+ data = sock.read_nonblock( PACK_SIZE )
144
+ rescue IO::WaitReadable, Errno::EINTR, IO::WaitWritable
145
+ return
146
+ rescue Exception => e
147
+ add_closing( sock )
148
+ return
149
+ end
150
+
151
+ info = @infos[ sock ]
152
+ info[ :updated_at ] = Time.new
153
+
154
+ until data.empty?
155
+ ctl_num = data[ 0 ].unpack( 'C' ).first
156
+
157
+ case ctl_num
158
+ when HEARTBEAT
159
+ data = data[ 1..-1 ]
160
+ when SET_TITLE
161
+ len = data[ 1, 2 ].unpack( 'n' ).first
162
+
163
+ if len > 255
164
+ puts "title too long"
165
+ add_closing( sock )
166
+ return
167
+ end
168
+
169
+ title = data[ 3, len ]
170
+
171
+ if @pending_p2s.include?( title )
172
+ p2 = @pending_p2s[ title ]
173
+ p2_info = @infos[ p2 ]
174
+ add_write( sock, p2_info[ :sockaddr ] )
175
+ add_write( p2, info[ :sockaddr ] )
176
+ elsif @pending_p1s.include?( title )
177
+ puts "pending p1 #{ title.inspect } already exist"
178
+ add_closing( sock )
179
+ return
180
+ else
181
+ @pending_p1s[ title ] = sock
182
+ info[ :title ] = title
183
+
184
+ begin
185
+ File.open( File.join( @roomd_dir, title ), 'w' )
186
+ rescue Errno::ENOENT, ArgumentError => e
187
+ puts "open title path #{ e.class }"
188
+ add_closing( sock )
189
+ return
190
+ end
191
+ end
192
+
193
+ data = data[ ( 3 + len )..-1 ]
194
+ when PAIRING
195
+ len = data[ 1, 2 ].unpack( 'n' ).first
196
+
197
+ if len > 255
198
+ puts 'pairing title too long'
199
+ add_closing( sock )
200
+ return
201
+ end
202
+
203
+ title = data[ 3, len ]
204
+
205
+ if @pending_p1s.include?( title )
206
+ p1 = @pending_p1s[ title ]
207
+ p1_info = @infos[ p1 ]
208
+ add_write( sock, p1_info[ :sockaddr ] )
209
+ add_write( p1, info[ :sockaddr ] )
210
+ elsif @pending_p2s.include?( title )
211
+ puts "pending p2 #{ title.inspect } already exist"
212
+ add_closing( sock )
213
+ return
214
+ else
215
+ @pending_p2s[ title ] = sock
216
+ info[ :title ] = title
217
+ end
218
+
219
+ data = data[ ( 3 + len )..-1 ]
220
+ end
221
+ end
222
+ end
223
+
224
+ def write_room( sock )
225
+ if @closings.include?( sock )
226
+ close_sock( sock )
227
+ return
228
+ end
229
+
230
+ info = @infos[ sock ]
231
+ data = info[ :wbuff ]
232
+
233
+ if data.empty?
234
+ @writes.delete( sock )
235
+ return
236
+ end
237
+
238
+ sock.write( data )
239
+ info[ :wbuff ].clear
240
+ end
241
+
242
+ def add_closing( sock )
243
+ unless @closings.include?( sock )
244
+ @reads.delete( sock )
245
+ @closings << sock
246
+ end
247
+
248
+ add_write( sock )
249
+ end
250
+
251
+ def add_write( sock, data = nil )
252
+ if data
253
+ info = @infos[ sock ]
254
+ info[ :wbuff ] << data
255
+ end
256
+
257
+ unless @writes.include?( sock )
258
+ @writes << sock
259
+ end
260
+ end
261
+
262
+ def close_sock( sock )
263
+ sock.close
264
+ @reads.delete( sock )
265
+ @writes.delete( sock )
266
+ @closings.delete( sock )
267
+ @rooms.delete( sock.object_id )
268
+ @roles.delete( sock )
269
+ info = @infos.delete( sock )
270
+
271
+ if info && info[ :title ]
272
+ @pending_p1s.delete( info[ :title ] )
273
+ @pending_p2s.delete( info[ :title ] )
274
+
275
+ begin
276
+ File.delete( File.join( @roomd_dir, info[ :title ] ) )
277
+ rescue Errno::ENOENT
278
+ end
279
+ end
280
+
281
+ info
282
+ end
283
+
284
+ def new_roomd
285
+ roomd = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
286
+ roomd.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1 )
287
+ roomd.bind( Socket.pack_sockaddr_in( @roomd_port, '0.0.0.0' ) )
288
+ roomd.listen( 511 )
289
+
290
+ @roles[ roomd ] = :roomd
291
+ @reads << roomd
292
+ end
293
+ end
294
+ end