Skip to content

Ruby Socket Programming

Socket programming is the foundation of network programming, allowing programs to communicate with other programs over the network. Ruby provides a powerful Socket library that makes network programming simple and intuitive. Whether creating servers or clients, Ruby can handle it with ease. This chapter will explain in detail Socket programming in Ruby, including TCP, UDP communication, and HTTP client implementation.

🎯 Socket Basics

What is a Socket

A Socket is an abstract concept in network programming. It is the endpoint of network communication. Through Sockets, applications can send and receive data over the network. Socket provides a standard communication mechanism, allowing programs on different hosts to communicate with each other.

Socket Types

  1. Stream Socket (SOCK_STREAM): Uses TCP protocol, providing reliable, connection-oriented communication
  2. Datagram Socket (SOCK_DGRAM): Uses UDP protocol, providing connectionless communication
  3. Raw Socket (SOCK_RAW): Provides direct access to underlying protocols

Socket Addressing

Sockets identify an endpoint on the network through IP address and port number:

  • IP Address: Identifies the host on the network (e.g., 192.168.1.100)
  • Port Number: Identifies a specific service on the host (e.g., 80, 443, 3000)

🔌 TCP Socket Programming

TCP Client

TCP (Transmission Control Protocol) is a connection-oriented protocol that provides reliable data transmission.

ruby
require 'socket'

# Create TCP client
def tcp_client(server_host, server_port, message)
  # Create TCP Socket
  socket = Socket.new(:INET, :STREAM)
  
  # Connect to server
  sockaddr = Socket.sockaddr_in(server_port, server_host)
  socket.connect(sockaddr)
  
  # Send message
  socket.write(message)
  puts "Sent: #{message}"
  
  # Receive response
  response = socket.read(1024)
  puts "Received response: #{response}"
  
  # Close connection
  socket.close
end

# Usage example (requires running server first)
# tcp_client('localhost', 3000, "Hello, Server!")

# Simpler approach
def simple_tcp_client(server_host, server_port, message)
  socket = TCPSocket.new(server_host, server_port)
  socket.puts message
  response = socket.gets
  puts "Server response: #{response}"
  socket.close
end

# simple_tcp_client('localhost', 3000, "Hello, Server!")

TCP Server

ruby
require 'socket'

# Create TCP server
def tcp_server(port)
  # Create server Socket
  server = TCPServer.new(port)
  puts "TCP server started, listening on port #{port}"
  
  loop do
    # Wait for client connection
    client = server.accept
    puts "Client connected: #{client.peeraddr[2]}:#{client.peeraddr[1]}"
    
    # Create new thread to handle client
    Thread.new(client) do |client_socket|
      begin
        # Read client message
        while message = client_socket.gets
          puts "Received message: #{message.chomp}"
          
          # Reply to client
          response = "Server received: #{message}"
          client_socket.puts response
          
          # Disconnect if quit command received
          break if message.chomp == 'quit'
        end
      rescue => e
        puts "Client processing error: #{e.message}"
      ensure
        client_socket.close
        puts "Client connection closed"
      end
    end
  end
end

# Start server (run in separate terminal)
# tcp_server(3000)

# More complete TCP server example
class TCPServerExample
  def initialize(port)
    @port = port
    @server = nil
    @clients = []
  end

  def start
    @server = TCPServer.new(@port)
    puts "Server started, listening on port #{@port}"
    
    # Handle interrupt signals
    trap('INT') { shutdown }
    
    loop do
      client = @server.accept
      @clients << client
      puts "New client connected: #{client.peeraddr[2]}:#{client.peeraddr[1]}"
      
      # Create handling thread for each client
      handle_client(client)
    end
  end

  private

  def handle_client(client)
    Thread.new do
      begin
        client.puts "Welcome! Enter 'quit' to exit."
        
        while message = client.gets
          message = message.chomp
          puts "Received message: #{message} (from #{client.peeraddr[2]})"
          
          case message
          when 'quit'
            client.puts "Goodbye!"
            break
          when 'time'
            client.puts "Current time: #{Time.now}"
          when 'clients'
            client.puts "Current connections: #{@clients.length}"
          else
            client.puts "Server reply: #{message}"
          end
        end
      rescue => e
        puts "Client error: #{e.message}"
      ensure
        @clients.delete(client)
        client.close
        puts "Client disconnected"
      end
    end
  end

  def shutdown
    puts "Shutting down server..."
    @server.close if @server
    @clients.each(&:close)
    exit
  end
end

# Usage example
# server = TCPServerExample.new(3001)
# server.start

📨 UDP Socket Programming

UDP Client

UDP (User Datagram Protocol) is a connectionless protocol that doesn't guarantee data transmission reliability but is faster.

ruby
require 'socket'

# UDP client
def udp_client(server_host, server_port, message)
  # Create UDP Socket
  socket = UDPSocket.new
  
  # Send message to server
  socket.send(message, 0, server_host, server_port)
  puts "Sent: #{message}"
  
  # Receive response
  response, sender = socket.recvfrom(1024)
  puts "Received response: #{response} (from #{sender[2]}:#{sender[1]})"
  
  # Close Socket
  socket.close
end

# Usage example (requires running UDP server first)
# udp_client('localhost', 3002, "UDP message test")

UDP Server

ruby
require 'socket'

# UDP server
def udp_server(port)
  # Create UDP Socket
  socket = UDPSocket.new
  socket.bind('localhost', port)
  puts "UDP server started, listening on port #{port}"
  
  loop do
    # Receive message
    message, sender = socket.recvfrom(1024)
    puts "Received message: #{message} (from #{sender[2]}:#{sender[1]})"
    
    # Send response
    response = "Server received: #{message}"
    socket.send(response, 0, sender[2], sender[1])
  end
end

# Start UDP server (run in separate terminal)
# udp_server(3002)

# More complete UDP server example
class UDPServerExample
  def initialize(port)
    @port = port
    @socket = nil
  end

  def start
    @socket = UDPSocket.new
    @socket.bind('0.0.0.0', @port)
    puts "UDP server started, listening on port #{@port}"
    
    # Handle interrupt signals
    trap('INT') { shutdown }
    
    loop do
      message, sender = @socket.recvfrom(1024)
      handle_message(message, sender)
    end
  end

  private

  def handle_message(message, sender)
    client_ip, client_port = sender[2], sender[1]
    puts "Received message: #{message.chomp} (from #{client_ip}:#{client_port})"
    
    response = case message.chomp
               when 'time'
                 Time.now.to_s
               when 'ping'
                 'pong'
               else
                 "Server received: #{message}"
               end
    
    @socket.send(response, 0, client_ip, client_port)
  end

  def shutdown
    puts "Shutting down UDP server..."
    @socket.close if @socket
    exit
  end
end

# Usage example
# server = UDPServerExample.new(3003)
# server.start

🌐 HTTP Client

Basic HTTP Requests

ruby
require 'socket'

# Simple HTTP GET request
def simple_http_get(host, port = 80, path = '/')
  # Create TCP connection
  socket = TCPSocket.new(host, port)
  
  # Send HTTP request
  request = "GET #{path} HTTP/1.1\r\n"
  request += "Host: #{host}\r\n"
  request += "Connection: close\r\n"
  request += "\r\n"
  
  socket.write(request)
  
  # Receive response
  response = socket.read
  socket.close
  
  # Parse response
  headers, body = response.split("\r\n\r\n", 2)
  { headers: headers, body: body, status: headers[/HTTP\/[\d\.]+\s+(\d+)/, 1] }
end

# Usage example
# result = simple_http_get('example.com', 80, '/')
# puts result[:body]

HTTP POST Request

ruby
require 'socket'
require 'uri'

def http_post(host, port, path, data)
  socket = TCPSocket.new(host, port)
  
  body = URI.encode_www_form(data)
  
  request = "POST #{path} HTTP/1.1\r\n"
  request += "Host: #{host}\r\n"
  request += "Content-Type: application/x-www-form-urlencoded\r\n"
  request += "Content-Length: #{body.length}\r\n"
  request += "Connection: close\r\n"
  request += "\r\n"
  request += body
  
  socket.write(request)
  
  response = socket.read
  socket.close
  
  puts response
end

# Usage example
# http_post('example.com', 80, '/submit', { name: 'John', age: 30 })
ruby
require 'net/http'
require 'uri'

# HTTP GET request
def http_get(url)
  uri = URI.parse(url)
  response = Net::HTTP.get_response(uri)
  
  {
    status: response.code,
    body: response.body,
    headers: response.to_hash
  }
end

# HTTP POST request
def http_post(url, data)
  uri = URI.parse(url)
  
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = (uri.scheme == 'https')
  
  request = Net::HTTP::Post.new(uri.path)
  request.set_form_data(data)
  
  response = http.request(request)
  
  {
    status: response.code,
    body: response.body
  }
end

# Usage examples
# get_result = http_get('https://api.example.com/data')
# post_result = http_post('https://api.example.com/submit', { name: 'John' })

📚 Next Steps

After mastering Ruby Socket programming, we recommend continuing to learn:

Continue your Ruby learning journey!

Content is for learning and research only.