Writing an Internet Server

Problem

You want to run a server for a TCP/IP application-level protocol, but no one has written a Ruby server for the protocol yet. This may be because its a protocol youve made up.

Solution

Use the gserver library in Rubys standard library. It implements a generic TCP/IP server suitable for small to medium-sized tasks.

Heres a very simple chat server written with gserver. It has no end-user features to speak of. People connect to the server with a telnet client, and are identified to each other only by hostname. But its a fully functional, multithreaded, logging server written in about 30 lines of Ruby.

	#!/usr/bin/ruby -w
	# chat.rb
	require  
gserver

	class ChatServer < GServer

	 def initialize(port=20606, host=GServer::DEFAULT_HOST)
	 @clients = []
	 super(port, host, Float::MAX, $stderr, true)
	 end

	 def serve(sock)
	 begin
	 @clients << sock
	 hostname = sock.peeraddr[2] || sock.peeraddr[3]
	 @clients.each do |c|
	 c.puts "#{hostname} has joined the chat." unless c == sock
	 end
	 until sock.eof? do
	 message = sock.gets.chomp
	 break if message == "/quit"
	 @clients.each { |c| c.puts "#{hostname}: #{message}" unless c == sock }
	 end
	 ensure
	 @clients.delete(sock)
	 @clients.each { |c| c.puts "#{hostname} has left the chat." }
	 end
	 end
	end

	server = ChatServer.new(*ARGV[0..2] || 20606)
	server.start(-1)
	server.join

Start the server in a Ruby session, and then use several instances of the telnet program to connect to port 20606 (from several different hosts, if you can). Your telnet sessions will be able to communicate with each other through the server. Your Ruby session will see a log of the connections and disconnections.

Discussion

The GServer class wraps Rubys underlying TCPServer class in a loop that continually receives TCP connections and spawns new threads to process them. Each new thread passes its TCP connection (a TCPSocket object) into the GServer#serve method, which your subclass is responsible for providing.

The TCPSocket works like a bidirectional file. Writing to it pushes data to the client, and reading from it reads data from the client. A server like the sample chat server reads one line at a time from the client; a web server would read the entire request before sending back any data.

In the chat server example, the server echoes one clients input to all the others. In most applications, the client sockets won even know about each other (think a web or FTP server).

The GServer constructor deserves a closer look. Heres its signature, from gserver.rb:

	def initialize(port, host = DEFAULT_HOST, maxConnections = 4,
	 stdlog = $stderr, audit = false, debug = false)

The port and host should be familiar to you from other types of server. maxConnections controls the maximum number of clients that can connect to the server at once. Because a chat server is very high-latency, I set the number effectively to infinity in ChatServer.

stdlog is an IO object to be used as a log. You can write a timestamped entry to the log by calling GServer#log. Setting audit to true turns on some default log messages: these are displayed, for instance, whenever a client connects to or disconnects from the server. Finally, setting debug to true means that, if your code throws an exception, the exception object will be passed into GServer#error. You can override this method to do your own error handling.

Gserver is easy to use, but not as efficient as a Ruby Internet server could be. For high-performance servers, youll want to use IO.select and TCPServer objects, programming to the C sockets API.

See Also

  • ri GServer


Strings

Numbers

Date and Time

Arrays

Hashes

Files and Directories

Code Blocks and Iteration

Objects and Classes8

Modules and Namespaces

Reflection and Metaprogramming

XML and HTML

Graphics and Other File Formats

Databases and Persistence

Internet Services

Web Development Ruby on Rails

Web Services and Distributed Programming

Testing, Debugging, Optimizing, and Documenting

Packaging and Distributing Software

Automating Tasks with Rake

Multitasking and Multithreading

User Interface

Extending Ruby with Other Languages

System Administration



Ruby Cookbook
Ruby Cookbook (Cookbooks (OReilly))
ISBN: 0596523696
EAN: 2147483647
Year: N/A
Pages: 399

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net