Implementing a Chat Server in Ruby

5
Categories: Howto, Programming
Posted on: 13th January 2009 by: kitallis

Me and Uncool participated in a ‘Linux Challenge’ recently in one of the IT Fest of the University of Delhi.
Although we just managed a Third Prize by some Python heroics in the end by Uncool, we were behind the First team only by a single problem of implementing a chat program using Unix Pipes. We had little clue about UNIX Pipes so we thought about implementing it by using standard libraries from either Ruby or Python, as they (event organizers) said they would still consider it. Expectedly, they didn’t have any trace of Ruby on their machines which were running Fedora 9.
Uncool was unsuccessful in implementing it in Python, the documentation was ugly enough.
Reasonably angry, I decided to prove myself why we could have at least won the Second prize without even a drop of sweat.

TCPSocket and TCPServer classes in the Ruby Standard Library are braindead-simple to implement.
The Ruby Programming Language shows how (comments are self-explanatory) :

# A Multithreaded Server
 
require 'socket'
 
# This method expects a socket connected to a client.
# It reads lines from the client, reverses them and sends them back.
# Multiple threads may run this method at the same time.
 
def handle_client(c)
 while true
   input = c.gets.chop                 # Read a line of input from the client
   break if !input                     # Exit if no more input
   break if input == "quit"            # or if the client asks to.
   c.puts(input.reverse)               # Otherwise, respond to client.
   c.flush                             # Force our output out
 end
 c.close                               # Close the client socket
end
 
server = TCPServer.open(2000) # Listen on port 2000
 
while true                             # Servers loop forever
 client = server.accept                # Wait for a client to connect
 Thread.start(client) do |c|           # Start a new thread
   handle_client(c)                    # And handle the client on that thread
 end
end

Gserver, one of the better standard libraries in Ruby.

More flexible than the socket library, it can be used to implement application level servers, it has a few useful predefined methods like the number of connections, event logging and handles all threading problems by itself, that means multiple users can connect on a single server at once (Asynchronous Socket programming -  which is, where many clients connect to a single server and send input for processing concurrently, the server then handles all the connected clients asynchronously and process the data as and whenever it is available from any of them.)

This little toy Chat Program reads a single input from the client, shuffles it and sends it back.

CLIENT

require 'socket'                           # Sockets are in standard library
 
sock = TCPSocket.open("localhost",1234)    # Socket to listen on port 1234
 
  l = STDIN.gets                           # Get a single input from console
  sock.puts(l)                             # Send input to the server
  sock.flush                               # Force input
  line = sock.readpartial(4096)            # Read server's response
  puts line                                # Display the response to the user
 
sock.close                                 # Close the socket

SERVER

require 'gserver'
 
# Algorithm to shuffle a string
 
def shuffle(str)
 
lenth = str.length
 
index = (lenth-1)
 
  while(index >= 0) do
 
   random_number = rand(lenth)
 
   str[random_number], str[index] = str[index], str[random_number]
 
   index = index - 1
 
  end
 
str
end
 
class Server < GServer                  # Server class derived from GServer super class
  def initialize(port=1234, *args)      # to use the initialize function
      super(port, *args)
  end
 
  def serve(io)                         # Serve method handles connections
 
      input = io.gets.chop!             # Get input from client console
      io.puts(shuffle(input))           # Return the shuffled input onto the client console
      puts input                        # Print the client message 
 
  end
 
end
 
server = Server.new
 
while (input = gets)                     # Loop server while user gives an input
 
 if input =~ /start/
   server.start                          # Start the server if the user types "init"
 
 end
 
 if input =~ /shutdown/
   server.shutdown                       # Shut the server down if the user types "shutdown"
 
 break
 end
 
end

Thin provides a TCPServer Socket backend which can also be used for similar purposes. Its pretty much the same except that it uses the EventMachine library for the Network I/O.