io-reactor 0.05
Sign up to get free protection for your applications and to get access to all the features.
- data/CATALOG +10 -0
- data/ChangeLog +303 -0
- data/README +77 -0
- data/examples/chatserver.rb +347 -0
- data/install.rb +85 -0
- data/io-reactor.gemspec +21 -0
- data/lib/io/reactor.rb +317 -0
- data/makedocs.rb +79 -0
- data/test.rb +212 -0
- data/utils.rb +484 -0
- metadata +53 -0
data/CATALOG
ADDED
data/ChangeLog
ADDED
@@ -0,0 +1,303 @@
|
|
1
|
+
2003-07-21 09:04 Michael Granger <ged@FaerieMUD.org>
|
2
|
+
|
3
|
+
* README (1.5, RELEASE_0_04):
|
4
|
+
|
5
|
+
- Changed to IO::Reactor.
|
6
|
+
|
7
|
+
2003-07-21 08:21 Michael Granger <ged@FaerieMUD.org>
|
8
|
+
|
9
|
+
* install.rb (1.1, RELEASE_0_04):
|
10
|
+
|
11
|
+
Initial checkin.
|
12
|
+
|
13
|
+
2003-07-21 00:55 Michael Granger <ged@FaerieMUD.org>
|
14
|
+
|
15
|
+
* test.rb (1.7), utils.rb (1.4), examples/chatserver.rb (1.3),
|
16
|
+
lib/io/reactor.rb (1.11), CATALOG (1.2) (utags: RELEASE_0_04):
|
17
|
+
|
18
|
+
- Refactored into IO::Reactor from Ruby-Poll.
|
19
|
+
|
20
|
+
2003-07-21 00:54 Michael Granger <ged@FaerieMUD.org>
|
21
|
+
|
22
|
+
* extconf.rb (1.6), poll.c (1.6), lib/poll.rb (1.11):
|
23
|
+
|
24
|
+
- Removed for IO::Reactor, which no longer needs a C backend.
|
25
|
+
|
26
|
+
2003-05-08 16:49 Michael Granger <ged@FaerieMUD.org>
|
27
|
+
|
28
|
+
* makesitedocs.rb (1.3, RELEASE_0_04):
|
29
|
+
|
30
|
+
makesitedocs.rb
|
31
|
+
|
32
|
+
2003-04-20 22:37 Michael Granger <ged@FaerieMUD.org>
|
33
|
+
|
34
|
+
* test.rb (1.6):
|
35
|
+
|
36
|
+
Added workaround for recent versions of Test::Unit's API changes.
|
37
|
+
|
38
|
+
2003-04-20 22:34 Michael Granger <ged@FaerieMUD.org>
|
39
|
+
|
40
|
+
* poll.c (1.5):
|
41
|
+
|
42
|
+
Added code for simulating poll(2) with rb_thread_select() for machines without a native poll(). Based on code for Freehaven by
|
43
|
+
Nick Mathewson <nickm@freehaven.net>.
|
44
|
+
|
45
|
+
2003-04-20 22:32 Michael Granger <ged@FaerieMUD.org>
|
46
|
+
|
47
|
+
* extconf.rb (1.5):
|
48
|
+
|
49
|
+
Added fake poll code to allow machines without a poll(2) to use the library. This also might become the primary method of polling, sadly, as Ruby doesn't tolerate other async IO methods very well due to its use of select() for its threading.
|
50
|
+
|
51
|
+
2002-10-20 21:47 Michael Granger <ged@FaerieMUD.org>
|
52
|
+
|
53
|
+
* lib/: poll.rb (1.10), io/reactor.rb (1.10) (utags: RELEASE_0_03):
|
54
|
+
|
55
|
+
- Got rid of a type-checking statement, as it didn't account for using an IO
|
56
|
+
inside a wrappered or delegated object.
|
57
|
+
|
58
|
+
- Fixed use of deprecated 'type' method.
|
59
|
+
|
60
|
+
2002-09-18 06:33 Michael Granger <ged@FaerieMUD.org>
|
61
|
+
|
62
|
+
* makedocs.rb (1.3, RELEASE_0_04, RELEASE_0_03, RELEASE_0_02):
|
63
|
+
|
64
|
+
- Removed dependence on as-yet-undistributed xhtml RDoc template.
|
65
|
+
|
66
|
+
2002-09-06 10:52 Michael Granger <ged@FaerieMUD.org>
|
67
|
+
|
68
|
+
* poll.c (1.4, RELEASE_0_03, RELEASE_0_02):
|
69
|
+
|
70
|
+
- Added interrupt-handling to the _poll call.
|
71
|
+
|
72
|
+
- Added more-informative debugging.
|
73
|
+
|
74
|
+
2002-07-20 10:07 Michael Granger <ged@FaerieMUD.org>
|
75
|
+
|
76
|
+
* lib/: poll.rb (1.9), io/reactor.rb (1.9) (utags: RELEASE_0_02):
|
77
|
+
|
78
|
+
- New method: #setMask().
|
79
|
+
|
80
|
+
- New method: #callback().
|
81
|
+
|
82
|
+
- New method: #args().
|
83
|
+
|
84
|
+
- Implemented the #register() method in terms of the other setter methods to
|
85
|
+
consolidate the maintenance of internal data structures.
|
86
|
+
|
87
|
+
- Added missing callback *args to #setCallback().
|
88
|
+
|
89
|
+
- Cleared up documentation for #register.
|
90
|
+
|
91
|
+
2002-07-20 10:03 Michael Granger <ged@FaerieMUD.org>
|
92
|
+
|
93
|
+
* examples/chatserver.rb (1.2, RELEASE_0_03, RELEASE_0_02):
|
94
|
+
|
95
|
+
- Changed name of user class to 'User' to avoid confusion about its role in the
|
96
|
+
server.
|
97
|
+
|
98
|
+
2002-07-20 10:01 Michael Granger <ged@FaerieMUD.org>
|
99
|
+
|
100
|
+
* test.rb (1.5, RELEASE_0_03, RELEASE_0_02):
|
101
|
+
|
102
|
+
- Added test for the #callback method.
|
103
|
+
|
104
|
+
2002-07-20 10:01 Michael Granger <ged@FaerieMUD.org>
|
105
|
+
|
106
|
+
* makedocs.rb (1.2):
|
107
|
+
|
108
|
+
- Added template argument to make it use the xhtml template.
|
109
|
+
|
110
|
+
2002-07-19 10:29 Michael Granger <ged@FaerieMUD.org>
|
111
|
+
|
112
|
+
* lib/: poll.rb (1.8), io/reactor.rb (1.8):
|
113
|
+
|
114
|
+
- Fixed a funny typo.
|
115
|
+
|
116
|
+
2002-07-18 21:58 Michael Granger <ged@FaerieMUD.org>
|
117
|
+
|
118
|
+
* CATALOG (1.1, RELEASE_0_03, RELEASE_0_02):
|
119
|
+
|
120
|
+
Initial commit.
|
121
|
+
|
122
|
+
2002-07-18 21:58 Michael Granger <ged@FaerieMUD.org>
|
123
|
+
|
124
|
+
* utils.rb (1.3, RELEASE_0_03, RELEASE_0_02):
|
125
|
+
|
126
|
+
- Fixed typo in vetManifest()
|
127
|
+
|
128
|
+
- Added case to read the documentation CATALOG in the base directory if it's not
|
129
|
+
in the docs/ directory.
|
130
|
+
|
131
|
+
2002-07-18 21:55 Michael Granger <ged@FaerieMUD.org>
|
132
|
+
|
133
|
+
* makesitedocs.rb (1.2, RELEASE_0_03, RELEASE_0_02):
|
134
|
+
|
135
|
+
- Merged some of the features from the MUES doc-gen script.
|
136
|
+
|
137
|
+
2002-07-18 21:52 Michael Granger <ged@FaerieMUD.org>
|
138
|
+
|
139
|
+
* lib/: poll.rb (1.7), io/reactor.rb (1.7):
|
140
|
+
|
141
|
+
- Added callback arguments to the register() method.
|
142
|
+
|
143
|
+
2002-07-18 21:52 Michael Granger <ged@FaerieMUD.org>
|
144
|
+
|
145
|
+
* test.rb (1.4):
|
146
|
+
|
147
|
+
- Added test for new "with-args" register() method call.
|
148
|
+
|
149
|
+
2002-07-18 09:42 Michael Granger <ged@FaerieMUD.org>
|
150
|
+
|
151
|
+
* utils.rb (1.2):
|
152
|
+
|
153
|
+
- Added a debugMsg function.
|
154
|
+
|
155
|
+
- Qualified all functions as module_functions.
|
156
|
+
|
157
|
+
2002-07-18 09:40 Michael Granger <ged@FaerieMUD.org>
|
158
|
+
|
159
|
+
* lib/: poll.rb (1.6), io/reactor.rb (1.6):
|
160
|
+
|
161
|
+
- Fixed some typos
|
162
|
+
|
163
|
+
- Added clarification and additional documentation for some methods.
|
164
|
+
|
165
|
+
2002-07-18 09:35 Michael Granger <ged@FaerieMUD.org>
|
166
|
+
|
167
|
+
* README (1.4, RELEASE_0_03, RELEASE_0_02):
|
168
|
+
|
169
|
+
- Fixed a typo.
|
170
|
+
|
171
|
+
2002-04-18 16:53 Michael Granger <ged@FaerieMUD.org>
|
172
|
+
|
173
|
+
* extconf.rb (1.4, RELEASE_0_03, RELEASE_0_02):
|
174
|
+
|
175
|
+
- Corrected a bug in the header =:)
|
176
|
+
|
177
|
+
2002-04-18 12:01 Michael Granger <ged@FaerieMUD.org>
|
178
|
+
|
179
|
+
* makedist.rb (1.1, RELEASE_0_04, RELEASE_0_03, RELEASE_0_02),
|
180
|
+
makesitedocs.rb (1.1), utils.rb (1.1) (utags: RELEASE_0_01):
|
181
|
+
|
182
|
+
Initial commit
|
183
|
+
|
184
|
+
2002-04-18 11:24 Michael Granger <ged@FaerieMUD.org>
|
185
|
+
|
186
|
+
* extconf.rb (1.3, RELEASE_0_01):
|
187
|
+
|
188
|
+
- Corrected path to the docs-generation script.
|
189
|
+
|
190
|
+
2002-04-18 10:23 Michael Granger <ged@FaerieMUD.org>
|
191
|
+
|
192
|
+
* makedocs.rb (1.1, RELEASE_0_01):
|
193
|
+
|
194
|
+
Initial commit
|
195
|
+
|
196
|
+
2002-04-18 10:23 Michael Granger <ged@FaerieMUD.org>
|
197
|
+
|
198
|
+
* poll.c (1.3, RELEASE_0_01):
|
199
|
+
|
200
|
+
- Added some comments for RDoc, should it ever parse protected methods.
|
201
|
+
|
202
|
+
- Changed the name of the second arg to _poll() to handleArray, as it wasn't
|
203
|
+
really an array of file descriptors.
|
204
|
+
|
205
|
+
2002-04-18 08:44 Michael Granger <ged@FaerieMUD.org>
|
206
|
+
|
207
|
+
* test.rb (1.3, RELEASE_0_01):
|
208
|
+
|
209
|
+
- Removed the "ext" dir from the ones being added to the LOAD_PATH, as it's gone
|
210
|
+
away.
|
211
|
+
|
212
|
+
- Removed a require left over from the failed tempfile experiment.
|
213
|
+
|
214
|
+
2002-04-18 08:43 Michael Granger <ged@FaerieMUD.org>
|
215
|
+
|
216
|
+
* extconf.rb (1.2):
|
217
|
+
|
218
|
+
- Moved the ext/extconf.rb stuff into this one because the ext directory is
|
219
|
+
going away.
|
220
|
+
|
221
|
+
2002-04-18 08:42 Michael Granger <ged@FaerieMUD.org>
|
222
|
+
|
223
|
+
* README (1.3, RELEASE_0_01):
|
224
|
+
|
225
|
+
- Changed from alpha to beta
|
226
|
+
|
227
|
+
- Added requirements section
|
228
|
+
|
229
|
+
- Modified installation section to use 'site-install' instead of 'install', and
|
230
|
+
took stuff out that would have been redundant with the requires section.
|
231
|
+
|
232
|
+
2002-04-17 15:34 Michael Granger <ged@FaerieMUD.org>
|
233
|
+
|
234
|
+
* lib/: poll.rb (1.5), io/reactor.rb (1.5) (utags: RELEASE_0_01):
|
235
|
+
|
236
|
+
- Added :yeilds: RDoc construct to #poll.
|
237
|
+
|
238
|
+
2002-04-17 15:20 Michael Granger <ged@FaerieMUD.org>
|
239
|
+
|
240
|
+
* README (1.2):
|
241
|
+
|
242
|
+
- Separated abstract from header.
|
243
|
+
|
244
|
+
2002-04-17 15:18 Michael Granger <ged@FaerieMUD.org>
|
245
|
+
|
246
|
+
* lib/: poll.rb (1.4), io/reactor.rb (1.4):
|
247
|
+
|
248
|
+
- Clarified abstract.
|
249
|
+
|
250
|
+
2002-04-17 15:14 Michael Granger <ged@FaerieMUD.org>
|
251
|
+
|
252
|
+
* README (1.1):
|
253
|
+
|
254
|
+
Initial commit
|
255
|
+
|
256
|
+
2002-04-17 07:03 Michael Granger <ged@FaerieMUD.org>
|
257
|
+
|
258
|
+
* lib/: poll.rb (1.3), io/reactor.rb (1.3):
|
259
|
+
|
260
|
+
- Fixed documentation for the EventMask bitwise operators.
|
261
|
+
|
262
|
+
2002-04-17 06:48 Michael Granger <ged@FaerieMUD.org>
|
263
|
+
|
264
|
+
* lib/: poll.rb (1.2), io/reactor.rb (1.2):
|
265
|
+
|
266
|
+
- Re-implemented after the first one was wiped out by extconf.
|
267
|
+
|
268
|
+
2002-04-17 06:47 Michael Granger <ged@FaerieMUD.org>
|
269
|
+
|
270
|
+
* poll.c (1.2):
|
271
|
+
|
272
|
+
- Added shortcut constants
|
273
|
+
|
274
|
+
- Added _GNU_SOURCE define to catch the additional poll.h constants under Linux
|
275
|
+
|
276
|
+
- Changed _poll to a protected method.
|
277
|
+
|
278
|
+
2002-04-17 06:46 Michael Granger <ged@FaerieMUD.org>
|
279
|
+
|
280
|
+
* test.rb (1.2):
|
281
|
+
|
282
|
+
- Added lots of stuff, redesigned to match the new API
|
283
|
+
|
284
|
+
2002-04-17 06:45 Michael Granger <ged@FaerieMUD.org>
|
285
|
+
|
286
|
+
* examples/chatserver.rb (1.1, RELEASE_0_01):
|
287
|
+
|
288
|
+
Initial commit
|
289
|
+
|
290
|
+
2002-04-16 04:47 Michael Granger <ged@FaerieMUD.org>
|
291
|
+
|
292
|
+
* extconf.rb (1.1), poll.c (1.1), test.rb (1.1), lib/poll.rb (1.1),
|
293
|
+
lib/io/reactor.rb (1.1):
|
294
|
+
|
295
|
+
Initial revision
|
296
|
+
|
297
|
+
2002-04-16 04:47 Michael Granger <ged@FaerieMUD.org>
|
298
|
+
|
299
|
+
* extconf.rb (1.1.1.1), poll.c (1.1.1.1), test.rb (1.1.1.1),
|
300
|
+
lib/poll.rb (1.1.1.1), lib/io/reactor.rb (1.1.1.1) (utags: IMPORT):
|
301
|
+
|
302
|
+
Initial import
|
303
|
+
|
data/README
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
|
2
|
+
= IO-Reactor
|
3
|
+
|
4
|
+
An implementation of the Reactor design pattern for multiplexed asynchronous
|
5
|
+
single-thread IO.
|
6
|
+
|
7
|
+
== Authors
|
8
|
+
|
9
|
+
Michael Granger <ged@FaerieMUD.org>
|
10
|
+
|
11
|
+
|
12
|
+
== Description
|
13
|
+
|
14
|
+
This module is a pure-Ruby implementation of an asynchronous multiplexed IO
|
15
|
+
Reactor which is based on the Reactor design pattern found in _Pattern-Oriented
|
16
|
+
Software Architecture, Volume 2: Patterns for Concurrent and Networked
|
17
|
+
Objects_. It allows a single thread to demultiplex and dispatch events from one
|
18
|
+
or more IO objects to the appropriate handler.
|
19
|
+
|
20
|
+
This module used to be called Ruby-Poll, and used to use the poll(2) system
|
21
|
+
call, but this strategy was not portable, and didn't work with Ruby's
|
22
|
+
threads. It now uses IO::select, which should work everywhere Ruby does.
|
23
|
+
|
24
|
+
I would greatly appreciate feedback on any aspect of this software. Suggestions,
|
25
|
+
feature requests, questions, design critiques, and bug reports are most
|
26
|
+
welcome. Relevant patches and minimal test cases are particularly helpful. I may
|
27
|
+
be reached by email at <ged@FaerieMUD.org>.
|
28
|
+
|
29
|
+
|
30
|
+
== Requirements
|
31
|
+
|
32
|
+
* Ruby >= 1.8.0
|
33
|
+
|
34
|
+
Optional:
|
35
|
+
|
36
|
+
* Dave Thomas's RDoc documentation tool to make HTML documentation.
|
37
|
+
* The Test::Unit module, to run the included test suite.
|
38
|
+
|
39
|
+
|
40
|
+
== Installation
|
41
|
+
|
42
|
+
$ su
|
43
|
+
# ruby install.rb
|
44
|
+
|
45
|
+
If you have the Test::Unit module installed, you can run the test suite by
|
46
|
+
doing:
|
47
|
+
|
48
|
+
$ ruby test.rb
|
49
|
+
|
50
|
+
|
51
|
+
== More Information
|
52
|
+
|
53
|
+
You can find more information about IO-Reactor, including the latest version, at
|
54
|
+
its project page:
|
55
|
+
|
56
|
+
http://www.devEiate.org/code/IO-Reactor.shtml
|
57
|
+
|
58
|
+
|
59
|
+
== Legal
|
60
|
+
|
61
|
+
IO-Reactor is Open Source Software which is Copyright (c) 2001-2003 by The
|
62
|
+
FaerieMUD Consortium.
|
63
|
+
|
64
|
+
You may use, modify, and/or redistribute this software under the same terms as
|
65
|
+
Ruby itself.
|
66
|
+
|
67
|
+
THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
|
68
|
+
INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND
|
69
|
+
FITNESS FOR A PARTICULAR PURPOSE.
|
70
|
+
|
71
|
+
|
72
|
+
$Id: README,v 1.5 2003/07/21 15:04:50 deveiant Exp $
|
73
|
+
|
74
|
+
|
75
|
+
|
76
|
+
|
77
|
+
|
@@ -0,0 +1,347 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# = chatserver.rb
|
3
|
+
#
|
4
|
+
# This is an extremely crude and simple single-threaded multiplexing chat
|
5
|
+
# server. It (hopefully) demonstrates how to use a IO::Reactor object to do IO
|
6
|
+
# multiplexing with events.
|
7
|
+
#
|
8
|
+
# == Synopsis
|
9
|
+
#
|
10
|
+
# $ chatserver.rb [HOST [PORT [LOOPTIMEOUT]]]
|
11
|
+
#
|
12
|
+
# [HOST]
|
13
|
+
# The host or IP the server will bind to
|
14
|
+
#
|
15
|
+
# [PORT]
|
16
|
+
# The port the server will listen on
|
17
|
+
#
|
18
|
+
# [LOOPTIMEOUT]
|
19
|
+
# The number of floating-point seconds between polls. Specifying -1 (or any
|
20
|
+
# negative number, really) here will make the server's event loop block on the
|
21
|
+
# call to #poll.
|
22
|
+
#
|
23
|
+
# == Author
|
24
|
+
#
|
25
|
+
# Michael Granger <ged@FaerieMUD.org>
|
26
|
+
#
|
27
|
+
# Copyright (c) 2002, 2003 The FaerieMUD Consortium. All rights reserved.
|
28
|
+
#
|
29
|
+
# This program is free software. You may use, modify, and/or redistribute this
|
30
|
+
# software under the same terms as Ruby itself.
|
31
|
+
#
|
32
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
33
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
34
|
+
# FOR A PARTICULAR PURPOSE.
|
35
|
+
#
|
36
|
+
# == Version
|
37
|
+
#
|
38
|
+
# $Id: chatserver.rb,v 1.4 2003/08/04 23:53:32 deveiant Exp $
|
39
|
+
#
|
40
|
+
|
41
|
+
require 'io/reactor'
|
42
|
+
require 'socket'
|
43
|
+
|
44
|
+
module Example
|
45
|
+
|
46
|
+
### Chatserver user class -- part of the chatserver example.
|
47
|
+
class User
|
48
|
+
|
49
|
+
MTU = 4096
|
50
|
+
CR = "\015"
|
51
|
+
LF = "\012"
|
52
|
+
EOL = CR + LF
|
53
|
+
|
54
|
+
PROMPT = 'chat> '
|
55
|
+
|
56
|
+
### Create and return a user object which will use the specified
|
57
|
+
### <tt>socket</tt> and <tt>pollObj</tt>.
|
58
|
+
def initialize( socket, server )
|
59
|
+
@socket = socket
|
60
|
+
@server = server
|
61
|
+
@obuffer = ''
|
62
|
+
@ibuffer = ''
|
63
|
+
@peerHost = @socket.peeraddr[2]
|
64
|
+
@peerPort = @socket.peeraddr[1]
|
65
|
+
@connected = true
|
66
|
+
end
|
67
|
+
|
68
|
+
# Object attribute
|
69
|
+
attr_reader :socket, :server, :ibuffer, :obuffer
|
70
|
+
|
71
|
+
|
72
|
+
### Return a stringified version of the user
|
73
|
+
def to_s
|
74
|
+
"%s:%d" % [ @peerHost, @peerPort ]
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
### Add the specified string to the user's output buffer and turn on
|
79
|
+
### output events.
|
80
|
+
def addOutput( string )
|
81
|
+
@obuffer << string.chomp << EOL
|
82
|
+
@server.reactor.enableEvents( @socket, :write )
|
83
|
+
end
|
84
|
+
alias :<< :addOutput
|
85
|
+
|
86
|
+
|
87
|
+
### Write as much of the output buffer to the socket as possible, and return
|
88
|
+
### the number of bytes remaining to be sent.
|
89
|
+
def writeOutput
|
90
|
+
bytes = @socket.syswrite( @obuffer )
|
91
|
+
@obuffer[ 0, bytes ] = '' if bytes.nonzero?
|
92
|
+
return @obuffer.length
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
### Write a prompt to the user
|
97
|
+
def prompt
|
98
|
+
@obuffer << PROMPT
|
99
|
+
@server.reactor.enableEvents( @socket, :write )
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
### Read at most MTU bytes from the socket and append them to the input
|
104
|
+
### buffer. Split off any complete lines (one that end with EOL) and return
|
105
|
+
### them as an Array of Strings.
|
106
|
+
def readInput
|
107
|
+
rary = []
|
108
|
+
@ibuffer << @socket.sysread( MTU )
|
109
|
+
$stderr.puts "Input buffer for user #{self} now: #@ibuffer" if $VERBOSE
|
110
|
+
while (( pos = @ibuffer.index EOL ))
|
111
|
+
$stderr.puts "Found terminating EOL. "\
|
112
|
+
"Splitting off 0..#{pos} of the input buffer." if $VERBOSE
|
113
|
+
rary << @ibuffer[ 0, pos ]
|
114
|
+
@ibuffer[ 0, pos + EOL.length ] = ''
|
115
|
+
end
|
116
|
+
|
117
|
+
return rary
|
118
|
+
rescue EOFError
|
119
|
+
@server.disconnectUser( self )
|
120
|
+
return []
|
121
|
+
end
|
122
|
+
|
123
|
+
|
124
|
+
### Handle poll events on the socket
|
125
|
+
def handleIOEvent( io, event )
|
126
|
+
case event
|
127
|
+
|
128
|
+
when :error
|
129
|
+
@server.disconnectUser( self )
|
130
|
+
|
131
|
+
when :read
|
132
|
+
input = readInput()
|
133
|
+
@server.processInput( self, *input ) unless input.empty?
|
134
|
+
|
135
|
+
when :write
|
136
|
+
bytesLeft = writeOutput()
|
137
|
+
@server.reactor.disableEvents( @socket, :write ) if bytesLeft.zero?
|
138
|
+
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
|
143
|
+
|
144
|
+
### Disconnect the user
|
145
|
+
def disconnect( msg='' )
|
146
|
+
@connected = false
|
147
|
+
unless msg.empty?
|
148
|
+
@obuffer = ">>> Disconnected: #{msg} <<<" + EOL
|
149
|
+
else
|
150
|
+
@obuffer = ">>> Disconnected <<<" + EOL
|
151
|
+
end
|
152
|
+
writeOutput()
|
153
|
+
@socket.close
|
154
|
+
end
|
155
|
+
|
156
|
+
|
157
|
+
### Returns true if the user is still connected
|
158
|
+
def connected?
|
159
|
+
@connected
|
160
|
+
end
|
161
|
+
end # class User
|
162
|
+
|
163
|
+
|
164
|
+
### Example chatserver class -- an extremely crude and simple chat server that
|
165
|
+
### demonstrates how to use Poll to do multiplexing IO in a single thread.
|
166
|
+
class Server
|
167
|
+
|
168
|
+
BANNER = <<-EOF
|
169
|
+
[[ IO::Reactor Example Chatserver ]]
|
170
|
+
Commands: '/quit' to quit, '/shutdown' to shut the server down
|
171
|
+
EOF
|
172
|
+
|
173
|
+
### Instantiate and return a chatserver on the specified host and port
|
174
|
+
def initialize( listenHost="0.0.0.0", listenPort=1138, interval=0.20 )
|
175
|
+
@socket = TCPServer::new( listenHost, listenPort )
|
176
|
+
@users = []
|
177
|
+
@reactor = IO::Reactor::new
|
178
|
+
@pollInterval = interval
|
179
|
+
@shuttingDown = false
|
180
|
+
|
181
|
+
@reactor.register @socket, :read, &method(:handlePollEvent)
|
182
|
+
end
|
183
|
+
|
184
|
+
# Server attributes
|
185
|
+
attr_reader :reactor, :users, :socket
|
186
|
+
|
187
|
+
|
188
|
+
### Main server loop
|
189
|
+
def eventLoop
|
190
|
+
trap( "INT" ) { shutdown("Server caught SIGINT") }
|
191
|
+
trap( "TERM" ) { shutdown("Server caught SIGTERM") }
|
192
|
+
trap( "HUP" ) { disconnectAllUsers(">>> Server reset <<<") }
|
193
|
+
|
194
|
+
until @shuttingDown
|
195
|
+
eventCount = @reactor.poll( @pollInterval )
|
196
|
+
end
|
197
|
+
|
198
|
+
rescue StandardError => e
|
199
|
+
$stderr.puts "Error in server: #{e.message}"
|
200
|
+
$stderr.puts "\t" + e.backtrace.join( "\n\t" )
|
201
|
+
shutdown( "Server error: #{e.message}" )
|
202
|
+
rescue SignalException => e
|
203
|
+
shutdown( "Server caught #{e.type.name}" )
|
204
|
+
ensure
|
205
|
+
trap( "INT", "SIG_IGN" )
|
206
|
+
trap( "TERM", "SIG_IGN" )
|
207
|
+
trap( "HUP", "SIG_IGN" )
|
208
|
+
|
209
|
+
$stderr.puts "Server exiting event loop."
|
210
|
+
end
|
211
|
+
|
212
|
+
|
213
|
+
### Handle a poll event specified by <tt>event</tt> on the specified
|
214
|
+
### <tt>socket</tt>
|
215
|
+
def handlePollEvent( socket, event )
|
216
|
+
$stderr.puts "Got #{event.inspect} event for #{socket.inspect}"
|
217
|
+
|
218
|
+
case event
|
219
|
+
when :error
|
220
|
+
$stderr.puts "Socket error on the listener socket."
|
221
|
+
shutdown()
|
222
|
+
|
223
|
+
when :read
|
224
|
+
clSock = socket.accept
|
225
|
+
user = User::new( clSock, self )
|
226
|
+
$stderr.puts "Accepted connection from #{user}"
|
227
|
+
@reactor.register clSock, :read, &user.method(:handleIOEvent)
|
228
|
+
user.addOutput( BANNER )
|
229
|
+
user.prompt
|
230
|
+
broadcastMsg( "[New connection: #{user}]" )
|
231
|
+
@users << user
|
232
|
+
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
|
237
|
+
### Process the specified input from the specified user
|
238
|
+
def processInput( user, *inputStrings )
|
239
|
+
inputStrings.each {|str|
|
240
|
+
case str
|
241
|
+
|
242
|
+
when %r{^/(\w+)\s*(.*)}
|
243
|
+
handleCommand( user, $1, $2 )
|
244
|
+
|
245
|
+
else
|
246
|
+
user.addOutput( "You>> #{str}" )
|
247
|
+
broadcastMsgFrom( user, str )
|
248
|
+
end
|
249
|
+
}
|
250
|
+
|
251
|
+
user.prompt if user.connected?
|
252
|
+
end
|
253
|
+
|
254
|
+
|
255
|
+
### Handle the specified command from the specified user
|
256
|
+
def handleCommand( user, command, args )
|
257
|
+
case command
|
258
|
+
|
259
|
+
when /quit/
|
260
|
+
disconnectUser( user, 'Quit' )
|
261
|
+
|
262
|
+
when /shutdown/
|
263
|
+
shutdown()
|
264
|
+
|
265
|
+
when /who/
|
266
|
+
user.addOutput( self.wholist(user) )
|
267
|
+
|
268
|
+
else
|
269
|
+
user.addOutput("Unknown command '#{command}'")
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
|
274
|
+
### Broadcast the specified message to all connected users
|
275
|
+
def broadcastMsg( msg )
|
276
|
+
@users.each {|cl|
|
277
|
+
cl.addOutput( msg )
|
278
|
+
}
|
279
|
+
end
|
280
|
+
|
281
|
+
|
282
|
+
### Broadcast the specified message from the specified user
|
283
|
+
def broadcastMsgFrom( user, msg )
|
284
|
+
userDesc = user.to_s
|
285
|
+
|
286
|
+
@users.each {|cl|
|
287
|
+
next if cl == user
|
288
|
+
cl.addOutput( "#{userDesc}>> #{msg}" )
|
289
|
+
}
|
290
|
+
end
|
291
|
+
|
292
|
+
|
293
|
+
### Disconnect the specified user
|
294
|
+
def disconnectUser( user, msg='' )
|
295
|
+
@users -= [ user ]
|
296
|
+
@reactor.unregister( user.socket )
|
297
|
+
user.disconnect( msg )
|
298
|
+
broadcastMsg( "#{user.to_s} Disconnected." )
|
299
|
+
end
|
300
|
+
|
301
|
+
|
302
|
+
### Disconnect all connected users
|
303
|
+
def disconnectAllUsers( msg )
|
304
|
+
@users.each {|user|
|
305
|
+
@reactor.unregister( user.socket )
|
306
|
+
user.disconnect( msg )
|
307
|
+
}
|
308
|
+
@users.clear
|
309
|
+
end
|
310
|
+
|
311
|
+
|
312
|
+
### Shut the server down
|
313
|
+
def shutdown( msg="Server shutdown" )
|
314
|
+
$stderr.puts "Shutting down: #{msg}"
|
315
|
+
@shuttingDown = true
|
316
|
+
@reactor.clear
|
317
|
+
begin
|
318
|
+
@socket.shutdown
|
319
|
+
rescue
|
320
|
+
end
|
321
|
+
disconnectAllUsers( msg )
|
322
|
+
begin
|
323
|
+
@socket.close
|
324
|
+
rescue
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
### Build and return a list of connected users for the specified user.
|
329
|
+
def wholist( user )
|
330
|
+
rval = "[Connected Users]\n" <<
|
331
|
+
" *#{user}*\n"
|
332
|
+
@users.each {|u|
|
333
|
+
next if u == user
|
334
|
+
rval << " #{u}\n"
|
335
|
+
}
|
336
|
+
|
337
|
+
return rval
|
338
|
+
end
|
339
|
+
|
340
|
+
end # class Server
|
341
|
+
end # module Example
|
342
|
+
|
343
|
+
srv = Example::Server::new( *ARGV )
|
344
|
+
$stderr.puts "Chat server listening on #{srv.socket.addr[2]} port #{srv.socket.addr[1]}"
|
345
|
+
srv.eventLoop
|
346
|
+
$stderr.puts "Chat server finished."
|
347
|
+
|