Ruby
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Methods and Blocks
-
Section 4: Object-Oriented Ruby
-
Section 5: Metaprogramming
-
Section 6: Working with Data and Files
-
Section 7: Ruby Frameworks Overview
-
Section 8: Ecosystem and Testing
-
Section 9: Practical Projects
-
Section 10: Interview Practice
-
Section 11: Data Structures and Algorithms in Ruby
-
Section 12: More Practice Exercises
-
Section 13: Enumerable and Functional Style
-
Section 14: More OOP Practice
-
Section 15: More Testing
-
Section 16: Enumerable and Comparable Modules In Depth
-
Section 17: Ruby Standard Library: Core Utilities
-
Section 18: Ruby Standard Library: Data and Security
-
Section 19: Ruby Standard Library: CLI and Text
-
Section 20: Ruby Networking
-
Section 21: Ruby on Rails Deep Dive
-
Section 22: Ruby Metaprogramming Deep Dive
-
Section 23: Ruby Design Patterns
-
Section 24: Ruby Concurrency
-
Section 25: Ruby Testing Deep Dive
-
Section 26: Ruby Gems and Packaging
-
Section 27: Ruby Performance
-
Section 28: More Practice Exercises
-
Section 29: More Interview Practice
-
Section 30: Rails API Development
-
Section 31: Rails Authentication and Authorization
-
Section 32: Rails Testing Deep Dive
-
Section 33: Rails Performance
-
Section 34: Rails Deployment
-
Section 35: Sinatra and Lightweight Ruby Web Apps
-
Section 36: More Ruby Language Deep Dive
-
Section 37: Ruby 3.x Modern Features
-
Section 38: More Data Structures in Ruby
-
Section 39: More Practical Projects
-
Section 40: Ruby Ecosystem Tools
-
Section 41: More Practice and Review
-
Section 42: Final Practice and Mastery
-
Section 43: Ruby Interview Deep Dive
-
Section 44: Ruby Background Processing Deep Dive
-
Section 45: Ruby GraphQL
-
Section 46: Ruby Object Model Deep Dive
-
Section 47: Ruby Hanami Framework Overview
-
Section 48: Ruby gRPC and Protocol Buffers
-
Section 49: Ruby Data Processing
-
Section 50: Ruby Search Integration
-
Section 51: Ruby File Upload and Media
-
Section 52: Ruby Email and Notifications
-
Section 53: Ruby Admin Panels
-
Section 54: Ruby Feature Flags and Experimentation
-
Section 55: Ruby Monitoring and Observability
-
Section 56: Ruby Docker and Deployment Deep Dive
-
Section 57: Ruby Security Deep Dive
-
Section 58: More Advanced Metaprogramming
-
Section 59: More Final Projects
79: Building a Simple TCP Server with Socket
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.
There are no comments for now.