Skip to Content
Course content

79: Building a Simple TCP Server with Socket

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Imagine you're running a small hotel. To get the business started, you first have to open the front doors and put a "Open for Business" sign in the window. Then, you sit behind the front desk and just... wait. You aren't talking to anyone yet; you're simply in a state of readiness. When a guest finally walks through the door, you stop waiting, stand up, and engage in a conversation with that specific person. Once they've checked in and left the desk, you sit back down and go back to waiting for the next guest. That's exactly how a TCP server operates.

In Ruby, the socket library provides the tools to do this. Let's map that hotel analogy directly to the code: the "Open" sign is your TCPServer instance, the act of sitting and waiting is the accept method, and the conversation is the read/write stream between the server and the client.

Setting up the Listening Post

To start, we need to tell Ruby which "door" (port) we're opening. I usually pick a port in the 4000-9000 range for local experiments to avoid clashing with system services. In this example, we're building a simple "System Status Server"—something that tells a client how the server is doing when asked.

require 'socket'

# This is our 'Open' sign. We're listening on localhost, port 4221.
server = TCPServer.new('localhost', 4221)
puts "Server is humming along on port 4221..."

At this point, the OS has reserved that port for our program. If you tried to run this script twice at the same time, the second one would crash because the "door" is already occupied.

The Loop of Perpetual Waiting

A server that handles one person and then shuts down isn't very useful. We want our receptionist to stay at the desk. We use a loop combined with server.accept. Now, here is the tricky part: accept is a "blocking" call. This means the program literally stops execution on that line and sleeps until a client actually connects. It doesn't waste CPU cycles; it just waits.

loop do
  # The program pauses here until someone connects
  client = server.accept 
  puts "A guest has arrived!"
  
  # We handle the conversation in a separate block or method
  handle_request(client)
end

Talking Back to the Client

Once accept returns, it gives us a TCPSocket object representing the connection to that specific client. We can treat this object almost like a file—we read from it and write to it. I prefer using gets and puts because they handle the newline characters that TCP streams rely on to know when a message ends.

Let's flesh out that handle_request logic to make it a real status monitor:

def handle_request(client)
  # Read the request from the client
  request = client.gets.chomp
  puts "Client asked: #{request}"

  case request
  when "STATUS"
    # Sending back a custom status message
    client.puts "SYSTEM_OK: Memory usage stable, CPU idling."
  when "TIME"
    client.puts "SERVER_TIME: #{Time.now}"
  else
    client.puts "ERROR: Unknown Command. Try 'STATUS' or 'TIME'."
  end

  # Crucial: close the connection so the client isn't left hanging
  client.close
end

I've seen plenty of beginners forget the client.close line. If you don't close the socket, the client might keep waiting for more data, and your server will slowly leak resources until the OS refuses to open any more sockets. Always clean up after your guests.

To test this, you don't even need to write a Ruby client. You can use a tool like telnet or nc (netcat) from your terminal: nc localhost 4221. Type "STATUS", hit enter, and you'll see your Ruby server respond in real-time.




📋 Practical Task

Build a Remote System Memory Oracle

Your task is to expand the TCP server concept to create a "Memory Oracle." Instead of hard-coded strings, your server should report actual system information to the client.

Requirements:

  • The server must listen on port 5000.
  • If the client sends the command "MEM", the server should respond with the current amount of free memory on the system. (Hint: On Linux/macOS, you can use backticks `free -m` or `vm_stat` and send that string back).
  • If the client sends "QUIT", the server should send "Goodbye!" and then the server process itself should terminate (break the loop).
  • Ensure every connection is closed properly after the response is sent.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.