Don't like ads? PRO users don't see any ads ;-)
Guest

Untitled

By: a guest on May 27th, 2012  |  syntax: None  |  size: 3.31 KB  |  hits: 13  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. require "socket"
  2.  
  3. # Namespace everything under DND
  4. module DND
  5.   Users = {
  6.     'simone' => 'secretpassword',
  7.     'carl'   => 'whatever',
  8.   }
  9.  
  10.   Delimiter = "\r\n"
  11.  
  12.   class Server
  13.     attr_reader :socket, :server_thread, :clients
  14.  
  15.     def initialize(host='0.0.0.0', port=20_000)
  16.       puts "[I] Initialising server"
  17.       @keep_running  = true
  18.       @host          = host
  19.       @port          = port
  20.       @socket        = TCPServer.new(@host, @port)
  21.       @clients       = []
  22.       @connection_id = 0
  23.     end
  24.  
  25.     def stop
  26.       @keep_running = false
  27.     end
  28.  
  29.     # blocks
  30.     def run
  31.       @keep_running  = true
  32.       puts "Listening on #{@host}:#{@port}"
  33.       accept_connection while @keep_running
  34.     end
  35.  
  36.     def accept_connection
  37.       return unless IO.select([@socket], nil, nil, 0.1)
  38.       socket        = @socket.accept
  39.       connection_id = new_connection_id # you could just use the socket's object-id or something...
  40.       puts("[Connection]\tAccepted connection with ID: #{connection_id}")
  41.       client        = Client.new(self, socket, connection_id)
  42.       if client.login then
  43.         puts("[Connection]\tConnection ID #{connection_id} logged in as #{client.username}")
  44.         client.welcome
  45.         @clients << client
  46.         client.run_thread
  47.       else
  48.         client.disconnect
  49.       end
  50.     end
  51.  
  52.     def new_connection_id
  53.       @connection_id += 1 # the last expression's value is the return value already
  54.     end
  55.  
  56.     def disconnect(client)
  57.       @clients.delete(client)
  58.     end
  59.   end
  60.  
  61.   class Client
  62.     attr_reader :socket, :connection_id, :username
  63.  
  64.     def initialize(server, socket, connection_id)
  65.       @server       = server
  66.       @socket       = socket
  67.       @username     = nil
  68.       @password     = nil
  69.     end
  70.  
  71.     def login
  72.       say("Welcome to ZombiServer v0.01")
  73.  
  74.       @username = ask("Your username: ")
  75.       @password = ask("Password: ")
  76.  
  77.       if Users[@username] == @password then
  78.         true
  79.       else
  80.         @username = nil
  81.         @password = nil
  82.  
  83.         false
  84.       end
  85.     end
  86.  
  87.     def welcome
  88.       say("You have successfully logged in as #{@username}")
  89.     end
  90.  
  91.     def disconnect
  92.       say("You're being disconnected")
  93.       @socket.close
  94.       @server.disconnect(self)
  95.     end
  96.  
  97.     def ask(question)
  98.       @socket.print(question)
  99.       @socket.flush           # IO's are line buffered, you have to flush if you want to emit an incomplete line
  100.       @socket.gets.chomp      # chomp removes the trailing newline
  101.     end
  102.  
  103.     def say(line)
  104.       @socket.print(line, Delimiter)
  105.     end
  106.  
  107.     def run_thread
  108.       Thread.new do
  109.         while line = @socket.gets # gets returns nil at eof
  110.           process(line.chomp)
  111.         end
  112.       end
  113.     end
  114.  
  115.     def process(line)
  116.       puts "[Command] User wrote: #{line}"
  117.       case line
  118.         when /\Aquit\z/i
  119.           disconnect
  120.         when /\Ausers\z/i
  121.           say("There are currently #{@server.clients.size} connected users")
  122.         else
  123.           say("Unknown command: #{line.inspect}")
  124.           say("Known commands are: users, quit")
  125.       end
  126.     end
  127.   end
  128. end
  129.  
  130. server = DND::Server.new
  131. trap("SIGINT") { # handle ctrl-C
  132.   trap("SIGINT", "SYSTEM_DEFAULT") # restore original signal handler
  133.   server.stop
  134. }
  135. server.run
  136. puts "","Server terminated"