| 167 | | |
|---|
| 168 | | |
|---|
| | 196 | ############################################## |
|---|
| | 197 | |
|---|
| | 198 | module Machine |
|---|
| | 199 | AcceptedConnection = Struct.new :descriptor, :peername, :dispatch_class |
|---|
| | 200 | |
|---|
| | 201 | class ConnectionAcceptor < EventDispatcher |
|---|
| | 202 | def initialize |
|---|
| | 203 | super |
|---|
| | 204 | add_handler AcceptedConnection, self, :accepted_connection |
|---|
| | 205 | end |
|---|
| | 206 | |
|---|
| | 207 | def accepted_connection evt |
|---|
| | 208 | es = EventableStream.new evt.descriptor, evt.dispatch_class.new |
|---|
| | 209 | es.peername = evt.peername |
|---|
| | 210 | end |
|---|
| | 211 | |
|---|
| | 212 | end |
|---|
| | 213 | |
|---|
| | 214 | end |
|---|
| | 215 | |
|---|
| | 216 | ############################################## |
|---|
| | 217 | |
|---|
| | 218 | module Machine |
|---|
| | 219 | class EventableTcpServer < EventableIO |
|---|
| | 220 | |
|---|
| | 221 | |
|---|
| | 222 | |
|---|
| | 223 | #-- |
|---|
| | 224 | # sugar over starting a TCP server. |
|---|
| | 225 | # INCOMPLETE, will throw a bunch of different socket-library |
|---|
| | 226 | # errors (DNS, no-bind, etc) which we ought to wrap and |
|---|
| | 227 | # either re-raise, or generate events for. |
|---|
| | 228 | # INCOMPLETE, need to similarly sugar creation of Unix-domain sockets. |
|---|
| | 229 | # Either a different method, or observe the params: for unix |
|---|
| | 230 | # only a filename is needed. |
|---|
| | 231 | # Of course we'll also need named pipes and whatever that Windows |
|---|
| | 232 | # near-equivalent is called. |
|---|
| | 233 | # TODO: As of May 29, 2006, Ruby's TCPServer object knows about |
|---|
| | 234 | # nonblocking accept, so this code can be rewritten to use it. |
|---|
| | 235 | # |
|---|
| | 236 | def self.listen_tcp host, port, dispatcher |
|---|
| | 237 | sd = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 ) |
|---|
| | 238 | sd.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true ) |
|---|
| | 239 | sd.bind( Socket.pack_sockaddr_in( port, host )) |
|---|
| | 240 | sd.listen( 50 ) # 5 is what you see in all the books. Ain't enough. |
|---|
| | 241 | EventableTcpServer.new sd, dispatcher |
|---|
| | 242 | end |
|---|
| | 243 | |
|---|
| | 244 | |
|---|
| | 245 | #-- |
|---|
| | 246 | # Ruby accept_nonblock is applied on class Socket, |
|---|
| | 247 | # but for some unknown reason, TCPServer is not a |
|---|
| | 248 | # subclass of Socket. It's a subclass of IO->BasicSocket. |
|---|
| | 249 | # So we can't do non-blocking I/O of TCPServers. |
|---|
| | 250 | # This is the required idiom for creating a TCP server: |
|---|
| | 251 | # sd = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0) |
|---|
| | 252 | # sd.bind( Socket.pack_sockaddr_in( port, server )) |
|---|
| | 253 | # sd.listen(5) |
|---|
| | 254 | # eio = TcpServerEventableIO.new( sd ) |
|---|
| | 255 | # UPDATE: As of May 29 2006, Ruby's TCPServer objects knows |
|---|
| | 256 | # about nonblocking accept. |
|---|
| | 257 | # |
|---|
| | 258 | # For Unix-domain sockets, the idiom is: |
|---|
| | 259 | # sd = Socket.new( Socket::AF_UNIX, Socket::SOCK_STREAM, 0) |
|---|
| | 260 | # sd.bind( Socket.pack_sockaddr_un( socketname )) |
|---|
| | 261 | # sd.listen(5) |
|---|
| | 262 | # eio = TcpServerEventableIO.new( sd ) |
|---|
| | 263 | # |
|---|
| | 264 | # In the constructor, we call super with a nil dispatcher, |
|---|
| | 265 | # and we remember LOCALLY the dispatcher we get from the caller. |
|---|
| | 266 | # The caller gives us a CLASS, not an object, which we will |
|---|
| | 267 | # use to instantiate dispatchers when we accept connections. |
|---|
| | 268 | # |
|---|
| | 269 | # POSSIBLE TODO: We may want to add another parameter, to specify |
|---|
| | 270 | # a real dispatcher for the acceptor, that would handle AcceptedConnection |
|---|
| | 271 | # events. Not sure how useful that would be, but it might be. |
|---|
| | 272 | # If we did that, we'd probably want to add in a small default |
|---|
| | 273 | # dispatcher, and have dispatcher=nil be the third, not the second |
|---|
| | 274 | # constructor parameter, so programmers who don't need it can ignore it. |
|---|
| | 275 | # |
|---|
| | 276 | def initialize io, dispatch_class, dispatcher=ConnectionAcceptor.new |
|---|
| | 277 | super io, dispatcher |
|---|
| | 278 | @dispatch_class = dispatch_class |
|---|
| | 279 | raise "TCP server dispatcher must be a Class" unless dispatch_class.is_a?(Class) |
|---|
| | 280 | end |
|---|
| | 281 | |
|---|
| | 282 | def select_for_writing? |
|---|
| | 283 | false |
|---|
| | 284 | end |
|---|
| | 285 | |
|---|
| | 286 | def select_for_reading? |
|---|
| | 287 | true |
|---|
| | 288 | end |
|---|
| | 289 | |
|---|
| | 290 | #-- |
|---|
| | 291 | # accept_nonblock returns an array consisting of the accepted |
|---|
| | 292 | # socket and a sockaddr_in which names the peer. |
|---|
| | 293 | def eventable_read |
|---|
| | 294 | begin |
|---|
| | 295 | 10.times { |
|---|
| | 296 | sd = AcceptedConnection.new |
|---|
| | 297 | sd.descriptor,sd.peername = io.accept_nonblock |
|---|
| | 298 | sd.dispatch_class = @dispatch_class |
|---|
| | 299 | @dispatcher.send_event sd |
|---|
| | 300 | } |
|---|
| | 301 | rescue Errno::EWOULDBLOCK, Errno::EAGAIN |
|---|
| | 302 | end |
|---|
| | 303 | end |
|---|
| | 304 | |
|---|
| | 305 | |
|---|
| | 306 | end |
|---|
| | 307 | end |
|---|
| | 308 | |
|---|
| | 309 | |
|---|
| | 310 | |
|---|
| | 311 | |
|---|