Rediscovering Ruby: GServer
One of the great aspects in working with Ruby is not just the language itself but the builtin APIs that come with it. In this series of Rediscovering Ruby articles I will be digging through the Core and Standard Ruby APIs looking for those useful gems that are either undiscovered or that have been forgotten over time. First up GServer, a micro-framework allowing you to quickly create servers that can handle multiple client requests.
Introduction
When people think of client-server computing with Ruby they usually default to one of the pre-existing web frameworks, most notably Rails. Oftentimes the work required to get that framework up and running, along with the required webserver and other dependencies, can be more hassle than it’s worth for a very simple server that does not need to be accessed via HTTP. Enter GServer. It comes bundled in the Ruby standard library so there are no dependencies besides the Ruby environment.
An Echo Server
To illustrate how easy it is to get up and running with GServer let’s create a simple echo server which reads in input a line at a time and sends the same input straight back.
To make use of GServer all you need to do is subclass GServer and then implement the serve method which receives a single object, a TCPSocket.
The following code listing is a simple echo server that will read in input a line at a time and echo it back:
1 require 'gserver' 2 class EchoServer < GServer 3 def serve( io ) 4 loop do 5 line = io.readline 6 io.puts( line ) 7 end 8 end 9 end 10 echo_server = EchoServer.new( 5179 ) 11 echo_server.audit = true # Turn logging on 12 echo_server.start # Start server 13 echo_server.join # Prevent exiting while server is running
We can then connect to the echo server using a TCPSocket.
1 >> require 'socket' 2 >> echo = TCPSocket.open( 'localhost', 5179 ) 3 >> echo.puts( "Hello\nWorld" ) 4 >> puts( echo.readline ) 5 => "Hello" 6 >> puts( echo.readline ) 7 => "World"
Notice that we sent one string but ( “Hello\nWorld” ) because we are using readline it is split in two due to the newline character (“\n”).
Controlling the Server
As illustrated in the code listing above start will start the server, which can then be queried with
1 echo_server.stopped? 2 EchoServer.in_service?( 5179 )
To stop the server there are three methods
1 echo_server.stop 2 EchoServer.stop( 5179 ) 3 echo_server.shutdown
The stop methods will stop the server immediately closing any open connections, while shutdown will wait for any open connections to close before gracefully shutting down the server.
Multiple Servers
GServer also allows you to run multiple services (using different port numbers) from the same Ruby script which can be stopped and started individually.
1 echo_server_1 = EchoServer.new( 'localhost', 5179 ) 2 echo_server_2 = EchoServer.new( 'localhost', 5180 ) 3 echo_server_1.start 4 echo_server_2.start 5 echo_server_1.shutdown 6 EchoServer.stop( 5180 )
Logging
Only the serve method is required to be implemented, however there are a number of hook methods that can be implemented to execute code at a certain events which may be useful for functionality like custom logging.
- starting – Server starts up
- connecting( tcp_socket ) – Before serve
- disconnecting( client_port ) – After serve
- stopping – Server shuts down
Note that these methods are only called if the audit attribute is set to true before start is called (see line 11 in the first code listing above). It’s even possible to entirely replace GServer’s logging with your own by also implementing:
- log( message )
- error( exception )
Exception Handling
GServer will handle all exceptions raised in the serve method, making sure the connection is closed properly and keeping the server up and running, ready to serve the next request.
Farrel Lifson is a lead developer at Aimred.