# code: # * George Moschovitis # * Anastasios Koutoumanos # # (c) 2004 Navel, all rights reserved. # $Id: server.rb 101 2004-10-22 12:35:39Z gmosx $ require "thread" require "socket" require "rexml/document" require "n/std" require "n/application" require "n/utils/array" require "n/sync/handler" module N; module Sync # = Server # # A Synchronous server tha communicates with clients using # application specific xml protocols. Typically communicates with # Flash clients utilising the XmlSocket functionality to enable 'push' # flash applications. # # The Macromedia XML Socket protocol is used, \000 is used as a eof # marker. NO: the XML protocol is not really appropriate, better use # a space optimized delimited ASCII protocol. # # === Design: # # Should support clustered operation: A cluster of servers is spawned. # The client connects to one of the servers at random. The client sticks # to the server. Each server considers the others as clients and # rebroadcasts all the events. # # We keep one service per server for simplicity, if we need multiple # services we can implement a multiplexer service (handler). # # === TODO: # # - Investigate if this server can be done with select. # class Server < N::Application MONITOR_INTERVAL = 2 * 60 # a single tcp server accepts all tcp requests attr :tcp_server # the listening address/port for this server. attr_reader :address, :port # the handler class, used to instantiate handlers attr :handler_class # maximum number of clients to connect. attr_accessor :max_handlers # the handler list, all handler attached to the server. attr_accessor :handlers # status attr_accessor :status STATUS_IDLE = 0 STATUS_RUNNING = 10 STATUS_STOPPED = 20 def initialize(address = "localhost", port = 2121, handler_class = N::Sync::Handler, max_handlers = nil) @address, @port = address, port @max_handlers = max_handlers @name, @title = "nsync", "Navel Sync" @status = STATUS_IDLE @handlers = N::SafeArray.new() @handler_class = handler_class super() end # Start the server. # def start() # a single tcp server accepts all tcp requests @tcp_server = TCPServer.new(address, port) start_monitor() run() end # Stop the server. # def stop() @status = STATUS_STOPED end # Run the main loop of the server. # def run() @status = STATUS_RUNNING $log.info "Server is running." begin while (STATUS_RUNNING == @status) socket = @tcp_server.accept() $log.debug "Socket accepted" if $DBG handler = @handler_class.new(self, socket) handlers << handler handler.start() end rescue Exception, StandardError => e $log.error pp_exception(e) # tml, FIXME: # this is dangerous! make more FAULT TOLERANT! end @status = STATUS_IDLE end def shutdown() end_monitor() for handler in handlers handler.stop() end Thread.exit() end # ------------------------------------------------------------------ def start_monitor @monitor = Thread.new { begin while true sleep(MONITOR_INTERVAL) $log.debug "monitor beat -- #{@handlers.size()} handlers" if $DBG for handler in @handlers unless handler.live? $log.info "Idle handler detected!" handler.gc!() end end end rescue Exception, StandardError => e $log.error pp_exception(e) end } end def end_monitor Thread.kill(@monitor) end # ------------------------------------------------------------------ def send(cmd, handler) handler.write(cmd) end def broadcast(cmd) for handler in @handlers handler.write(cmd) end end end if $0 == __FILE__ require "n/logger" $log = Logger.new(STDERR) Server.new().exec() end end; end #modules