Changeset 186

Show
Ignore:
Timestamp:
06/03/06 14:02:34 (3 years ago)
Author:
blackhedd
Message:

socket acceptors

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • experiments/NewMachine/lib/machine/eio.rb

    r183 r186  
    77require 'singleton' 
    88require 'forwardable' 
     9require 'socket' 
    910require 'fcntl' 
     11 
     12 
     13########################################### 
     14 
     15# Add some convenience methods into Reactor. 
     16# For the most part these will be wrappers around methods 
     17# defined in other classes. 
     18# This is how Twisted does it, although we may eventually 
     19# decide to scrap this and make people just use the underlying 
     20# classes directly. 
     21 
     22module Machine 
     23class Reactor 
     24  class << self 
     25 
     26    def listen_tcp *args, &block 
     27      EventableTcpServer.listen_tcp *args, &block 
     28    end 
     29 
     30  end 
     31end 
     32end 
     33 
     34 
     35 
     36########################################### 
     37 
    1038 
    1139module Machine 
     
    122150############################################# 
    123151 
    124  
    125152module Machine 
    126153  StreamData = Struct.new :data 
     
    128155  class EventableStream < EventableIO 
    129156 
    130     def initialize io, dispatcher=nil 
     157    attr_accessor :peername 
     158 
     159    def initialize io, dispatcher=EventDispatcher.new 
    131160      super 
    132161    end 
     
    165194end 
    166195 
    167  
    168  
     196############################################## 
     197 
     198module 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 
     214end 
     215 
     216############################################## 
     217 
     218module 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 
     307end 
     308 
     309 
     310 
     311