io-reactor 0.05

Sign up to get free protection for your applications and to get access to all the features.
data/CATALOG ADDED
@@ -0,0 +1,10 @@
1
+ #
2
+ # Title: IO-Reactor
3
+ # Main: README
4
+ # Upload: ssh://oberon/www/devEiate.org/public/code/IO-Reactor/
5
+ # Webcvs: http://www.deveiate.org/cgi/viewcvs.cgi/IO-Reactor/
6
+ #
7
+ lib/io/*
8
+ README
9
+ ChangeLog
10
+ examples/*
@@ -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
+