Skip to main content

A basic socket server in Ruby

My first bit of socket programming is a Ruby server that reads lines from the socket, and prints them. Not useful on its own, but a stepping stone to more exciting things!

I’m working on a new project that involves sockets, and I decided to try writing a basic socket server in Ruby. Here’s the code I wrote:

require 'socket'

class SocketServer

  # Opens a server listening to `port` and returns a new
  # `TCPServer` object.
  #
  #     SocketServer.open(port) do |server|
  #       loop do
  #         client = server.accept
  #         … do stuff with client connection
  #       end
  #     end
  #
  # The server will automatically be shut down when the block completes.
  def self.open(port)
    server = TCPServer.new(port)

    begin
      yield(server)
    rescue Interrupt
      puts "\nShutting down server..."
    ensure
      server.close
      puts "Server shut down"
    end
  end

  # Manages a client connection on a socket.
  #
  #     client = server.accept
  #
  #     SocketServer.handle_client(client) do |connection|
  #       while line = connection.gets
  #         puts "Received #{line}
  #         … do other stuff with connection
  #       end
  #     end
  #
  # The connection will automatically be closed and cleaned up
  # when the block completes.
  def self.handle_client(client)
    puts "New client connected from: #{client.remote_address.ip_address}"

    begin
      yield(client)
    rescue Errno::ECONNRESET
      puts "Client disconnected unexpectedly"
    ensure
      client.close
      puts "Client disconnected"
    end
  end
end

SocketServer.open(port = 3000) do |server|
  loop do
    client = server.accept

    SocketServer.handle_client(client) do |connection|
      while line = connection.gets
        line.chomp!
        puts "Received: #{line}"
        connection.puts "Received: #{line}"
      end
    end
  end
end

Run it as:

$ ruby server.rb

You can send lines to the server with nc:

$ echo 'Hello world!' | nc localhost 3000

The server will print the messages sent to the socket, for example:

New client connected from: ::1
Received: Hello world!
Client disconnected

As well as my first time doing socket programming, this is the first time I’ve used Ruby’s begin … rescue … ensure blocks, which feel analogous to context managers in Python. Here’s a Stack Overflow answer by Jörg W Mittag which helped me understand what’s going on here.