Overview
The TCP/IP protocol stack defines a layered model for network communication that enables data transmission across interconnected networks. This four-layer architecture emerged from the ARPANET project in the 1970s and became the foundation for the modern Internet. Unlike the seven-layer OSI model, TCP/IP uses a practical, implementation-focused approach with four distinct layers: Link, Internet, Transport, and Application.
Each layer in the TCP/IP stack handles specific networking responsibilities. The Link layer manages physical network access, the Internet layer routes packets across networks, the Transport layer ensures reliable data delivery, and the Application layer provides services to end-user programs. Data passes down through these layers when sending and up through them when receiving, with each layer adding or removing protocol-specific headers.
The protocol stack operates through encapsulation. When an application sends data, each layer wraps the previous layer's packet with its own header information. A web request starts as HTTP data at the Application layer, gains TCP headers at the Transport layer, receives IP headers at the Internet layer, and gets frame headers at the Link layer before transmission. The receiving system reverses this process, stripping headers at each layer until the application data reaches its destination.
# Basic TCP/IP communication in Ruby
require 'socket'
# Create a TCP server on port 8080
server = TCPServer.new(8080)
puts "Server listening on port 8080"
# Accept incoming connection
client = server.accept
puts "Client connected from #{client.peeraddr[3]}"
# Receive data (Application layer data over TCP/IP)
request = client.gets
puts "Received: #{request}"
# Send response
client.puts "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nHello"
client.close
Ruby's Socket library provides direct access to TCP/IP networking primitives. Developers work primarily at the Transport and Application layers, while the operating system handles Internet and Link layer operations. This abstraction allows focus on application logic without managing low-level packet routing or frame construction.
Key Principles
The TCP/IP stack organizes network communication into four functional layers, each building upon the services of the layer below it. This layered architecture creates clear separation of concerns and allows protocols at each layer to evolve independently.
Link Layer handles the physical transmission of data over a specific network medium. This layer includes protocols like Ethernet, Wi-Fi, and PPP that manage MAC addresses, frame formatting, and direct hardware communication. The Link layer converts packets into electrical signals or radio waves and handles media access control for shared networks. Error detection occurs here through checksums, though error correction typically happens at higher layers.
Internet Layer routes packets across multiple networks using IP addresses. The Internet Protocol (IP) operates at this layer, providing logical addressing and packet forwarding. IP treats each packet independently, making no guarantees about delivery order or reliability. This layer fragments large packets when necessary, adds Time-To-Live values to prevent infinite routing loops, and determines the best path through intermediate routers. ICMP operates at this layer for diagnostic purposes, while ARP resolves IP addresses to MAC addresses.
Transport Layer establishes end-to-end communication between applications. TCP provides reliable, ordered, stream-oriented delivery with connection establishment, flow control, and congestion management. UDP offers unreliable, message-oriented delivery with minimal overhead. TCP maintains connection state, acknowledges received segments, and retransmits lost data. UDP simply sends datagrams without acknowledgment or ordering guarantees. Port numbers at this layer multiplex multiple applications over a single IP address.
Application Layer implements protocols that applications use directly. HTTP, FTP, SMTP, DNS, and SSH operate at this layer, each defining its own message formats and exchange patterns. Application protocols build on Transport layer services, choosing between TCP's reliability or UDP's speed based on application requirements. This layer handles data representation, encryption, compression, and application-specific error handling.
# Demonstrating layer interaction
require 'socket'
require 'resolv'
# Application Layer: DNS resolution
hostname = 'example.com'
ip_address = Resolv.getaddress(hostname)
puts "Resolved #{hostname} to #{ip_address}" # Internet layer address
# Transport Layer: TCP connection with specific port
socket = TCPSocket.new(ip_address, 80) # Port 80 = HTTP
# Application Layer: HTTP request
socket.puts "GET / HTTP/1.1\r\nHost: #{hostname}\r\n\r\n"
# Receive response
response = socket.read
puts "Received #{response.bytesize} bytes"
socket.close
The protocol stack implements the end-to-end principle: intelligence resides at the endpoints rather than in the network. Routers forward packets based on destination addresses without understanding application semantics. This design creates a simple, scalable network core while allowing complex functionality at the edges.
Flow control mechanisms prevent fast senders from overwhelming slow receivers. TCP uses a sliding window protocol where the receiver advertises how much buffer space remains available. The sender limits transmission to this window size, expanding or contracting based on receiver capacity and network conditions.
Congestion control prevents network overload by detecting packet loss as a congestion signal. TCP reduces transmission rate when timeouts occur and gradually increases speed when transmissions succeed. Algorithms like TCP Reno and CUBIC implement sophisticated mechanisms that balance throughput with network health.
The stack handles packet fragmentation when data exceeds the Maximum Transmission Unit (MTU) of the underlying network. IP fragments large packets at the Internet layer and reassembles them at the destination. Modern systems prefer path MTU discovery to avoid fragmentation, sending packets small enough for the entire path.
Ruby Implementation
Ruby provides socket programming through the Socket library, which exposes TCP/IP functionality through object-oriented interfaces. The library includes classes for different protocol levels and communication patterns, abstracting operating system socket APIs.
TCP Sockets offer stream-oriented, connection-based communication. TCPServer creates listening sockets that accept incoming connections, while TCPSocket initiates outbound connections. These classes build on the lower-level Socket class with convenient methods for common TCP operations.
require 'socket'
# Server using TCPServer
server = TCPServer.new('localhost', 9000)
loop do
client = server.accept # Blocks until connection arrives
Thread.new(client) do |conn|
# Each client handled in separate thread
begin
conn.puts "Connected to server at #{Time.now}"
while line = conn.gets
break if line.strip == 'quit'
conn.puts "Echo: #{line}"
end
ensure
conn.close
end
end
end
The Socket class provides lower-level access for specialized requirements. Creating raw sockets requires understanding socket domains (AF_INET for IPv4, AF_INET6 for IPv6), socket types (SOCK_STREAM for TCP, SOCK_DGRAM for UDP), and protocol numbers.
require 'socket'
# Low-level TCP socket creation
socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
sockaddr = Socket.sockaddr_in(80, 'example.com')
begin
socket.connect(sockaddr)
request = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"
socket.write(request)
response = socket.read(1024)
puts response
ensure
socket.close
end
UDP Sockets provide message-oriented, connectionless communication. UDPSocket sends and receives individual datagrams without establishing connections. Each send operation specifies the destination address, and receive operations return both data and sender information.
require 'socket'
# UDP server
server = UDPSocket.new
server.bind('localhost', 9001)
Thread.new do
loop do
data, addr = server.recvfrom(1024)
puts "Received from #{addr[3]}:#{addr[1]}: #{data}"
server.send("ACK: #{data}", 0, addr[3], addr[1])
end
end
# UDP client
client = UDPSocket.new
client.send("Hello UDP", 0, 'localhost', 9001)
response, _ = client.recvfrom(1024)
puts "Response: #{response}"
Ruby's socket library includes non-blocking I/O through the IO.select method and socket options. This allows handling multiple connections without threading overhead, critical for high-concurrency servers.
require 'socket'
server = TCPServer.new('localhost', 9002)
clients = []
loop do
# Wait for activity on server or any client socket
ready = IO.select([server] + clients, nil, nil, 1)
next unless ready
ready[0].each do |socket|
if socket == server
# New connection
clients << server.accept
puts "Client connected. Total: #{clients.size}"
else
# Data from existing client
begin
data = socket.read_nonblock(1024)
socket.write("Received: #{data}")
rescue EOFError, Errno::ECONNRESET
clients.delete(socket)
socket.close
puts "Client disconnected. Total: #{clients.size}"
rescue IO::WaitReadable
# No data available yet
end
end
end
end
Socket Options configure TCP/IP behavior. The setsockopt method adjusts parameters like SO_REUSEADDR for address reuse, TCP_NODELAY to disable Nagle's algorithm, and SO_KEEPALIVE for connection monitoring.
require 'socket'
server = TCPServer.new('localhost', 9003)
# Enable address reuse (avoids "Address already in use" after restart)
server.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1)
# Set TCP keep-alive
server.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, 1)
client = server.accept
# Disable Nagle's algorithm for low-latency communication
client.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
# Set receive buffer size
client.setsockopt(Socket::SOL_SOCKET, Socket::SO_RCVBUF, 65536)
The Resolv library handles DNS resolution, translating hostnames to IP addresses. This operates at the Application layer but provides addresses needed by Transport and Internet layer operations.
require 'resolv'
# Synchronous resolution
ip = Resolv.getaddress('github.com')
puts "GitHub IP: #{ip}"
# Multiple addresses
Resolv::DNS.new.each_address('google.com') do |addr|
puts "Google address: #{addr}"
end
# Reverse DNS lookup
hostname = Resolv.getname('8.8.8.8')
puts "8.8.8.8 resolves to: #{hostname}"
Practical Examples
HTTP Client Implementation
Building an HTTP client demonstrates Application layer protocol implementation over TCP. This example handles request formatting, connection management, and response parsing.
require 'socket'
require 'uri'
class SimpleHTTPClient
def self.get(url)
uri = URI.parse(url)
# Establish TCP connection (Transport layer)
socket = TCPSocket.new(uri.host, uri.port || 80)
# Format HTTP request (Application layer)
request = [
"GET #{uri.path.empty? ? '/' : uri.path} HTTP/1.1",
"Host: #{uri.host}",
"Connection: close",
"User-Agent: SimpleHTTPClient/1.0",
"",
""
].join("\r\n")
# Send request
socket.write(request)
# Read response
response = socket.read
socket.close
# Parse response
headers, body = response.split("\r\n\r\n", 2)
status_line = headers.lines.first
{
status: status_line.split(' ', 3)[1].to_i,
headers: parse_headers(headers),
body: body
}
end
def self.parse_headers(header_text)
headers = {}
header_text.lines.drop(1).each do |line|
key, value = line.strip.split(': ', 2)
headers[key] = value if key
end
headers
end
end
# Usage
result = SimpleHTTPClient.get('http://example.com')
puts "Status: #{result[:status]}"
puts "Content-Type: #{result[:headers]['Content-Type']}"
puts "Body length: #{result[:body].bytesize} bytes"
Port Scanner
A port scanner demonstrates Transport layer interaction, testing TCP connection establishment across multiple ports to identify listening services.
require 'socket'
require 'timeout'
class PortScanner
def self.scan(host, port_range, timeout_seconds = 1)
open_ports = []
port_range.each do |port|
begin
Timeout::timeout(timeout_seconds) do
socket = TCPSocket.new(host, port)
open_ports << port
socket.close
end
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
# Port closed or host unreachable
rescue Timeout::Error
# Connection timeout
rescue => e
puts "Error scanning port #{port}: #{e.message}"
end
end
open_ports
end
end
# Scan common ports
host = 'localhost'
common_ports = [21, 22, 23, 25, 80, 110, 143, 443, 3306, 5432, 8080]
puts "Scanning #{host}..."
open_ports = PortScanner.scan(host, common_ports, 0.5)
if open_ports.empty?
puts "No open ports found"
else
puts "Open ports: #{open_ports.join(', ')}"
end
Multi-Client Chat Server
A chat server demonstrates concurrent connection handling, message broadcasting, and proper socket cleanup with thread management.
require 'socket'
class ChatServer
def initialize(port)
@server = TCPServer.new(port)
@clients = []
@mutex = Mutex.new
end
def start
puts "Chat server started on port #{@server.addr[1]}"
loop do
client = @server.accept
Thread.new(client) do |conn|
handle_client(conn)
end
end
end
private
def handle_client(client)
# Get nickname
client.puts "Enter nickname:"
nickname = client.gets.strip
# Add to clients list
@mutex.synchronize do
@clients << { socket: client, nickname: nickname }
end
broadcast("#{nickname} joined the chat", client)
# Handle messages
loop do
message = client.gets
break unless message
broadcast("#{nickname}: #{message.strip}", client)
end
rescue => e
puts "Client error: #{e.message}"
ensure
# Remove client and notify others
@mutex.synchronize do
client_data = @clients.find { |c| c[:socket] == client }
@clients.delete(client_data)
if client_data
broadcast("#{client_data[:nickname]} left the chat", nil)
end
end
client.close
end
def broadcast(message, sender)
@mutex.synchronize do
@clients.each do |client_data|
next if client_data[:socket] == sender
begin
client_data[:socket].puts(message)
rescue
# Client disconnected, will be cleaned up
end
end
end
end
end
# Start server
server = ChatServer.new(9004)
server.start
UDP Network Time Client
This example demonstrates UDP datagram communication, showing connectionless message exchange with the Network Time Protocol.
require 'socket'
class SimpleNTPClient
NTP_SERVER = 'pool.ntp.org'
NTP_PORT = 123
NTP_EPOCH_OFFSET = 2208988800 # Seconds between 1900 and 1970
def self.get_time
socket = UDPSocket.new
# NTP request packet (48 bytes)
request = [0x1B] + [0] * 47
packet = request.pack('C48')
socket.send(packet, 0, NTP_SERVER, NTP_PORT)
# Wait for response with timeout
response, _ = socket.recvfrom(48)
socket.close
# Parse transmit timestamp (bytes 40-43 for seconds)
seconds = response[40..43].unpack1('N')
# Convert from NTP epoch (1900) to Unix epoch (1970)
unix_timestamp = seconds - NTP_EPOCH_OFFSET
Time.at(unix_timestamp)
end
end
# Get network time
network_time = SimpleNTPClient.get_time
local_time = Time.now
puts "Network time: #{network_time}"
puts "Local time: #{local_time}"
puts "Difference: #{(network_time - local_time).round(2)} seconds"
Security Implications
TCP/IP protocols contain no built-in encryption or authentication. Data travels across networks in plaintext, vulnerable to interception, modification, and spoofing attacks. Security must come from higher-layer protocols or application-level implementations.
Man-in-the-Middle Attacks exploit the lack of endpoint verification. An attacker intercepts communication between two parties, reading or altering messages without detection. TCP's three-way handshake authenticates nothing beyond IP addresses, which attackers can spoof. Applications must implement their own authentication mechanisms, typically through TLS/SSL at the Transport layer or application-specific protocols.
SYN Flooding attacks exploit TCP's connection establishment process. Attackers send many SYN packets with spoofed source addresses, causing servers to allocate resources for half-open connections. The server waits for ACK packets that never arrive, exhausting connection tables and preventing legitimate connections. Defenses include SYN cookies, connection rate limiting, and firewall rules.
require 'socket'
# Vulnerable server without protection
def vulnerable_server(port)
server = TCPServer.new(port)
loop do
# accept blocks, but each SYN allocates resources
# Attacker sends many SYNs without completing handshake
client = server.accept
handle_client(client)
end
end
# Hardened server with connection limits
def hardened_server(port, max_clients = 100)
server = TCPServer.new(port)
clients = []
mutex = Mutex.new
loop do
# Check connection limit
if clients.size >= max_clients
sleep 0.1
next
end
# Use non-blocking accept with timeout
ready = IO.select([server], nil, nil, 1)
next unless ready
begin
client = server.accept_nonblock
mutex.synchronize do
clients << client
end
Thread.new(client) do |conn|
begin
handle_client(conn)
ensure
mutex.synchronize do
clients.delete(conn)
end
conn.close
end
end
rescue IO::WaitReadable
# No connection ready
end
end
end
IP Spoofing allows attackers to forge source addresses in packets. UDP particularly suffers from this since it lacks connection state. An attacker sends packets claiming to originate from a trusted source, bypassing IP-based access controls. Applications must not rely solely on source IP for authentication.
Port Scanning enables reconnaissance by probing which ports accept connections. While not an attack itself, port scanning identifies potential targets and running services. Defensive measures include closing unnecessary ports, using firewalls to restrict access, and implementing rate limiting to detect scanning attempts.
DNS Cache Poisoning corrupts DNS resolution by injecting false records into DNS caches. Attackers redirect traffic to malicious servers by making DNS resolvers believe false IP mappings. DNSSEC provides cryptographic verification of DNS responses, though adoption remains incomplete.
require 'socket'
require 'openssl'
# Insecure TCP communication
def insecure_connection(host, port)
socket = TCPSocket.new(host, port)
socket.puts "SECRET_DATA" # Transmitted in plaintext
response = socket.gets
socket.close
response
end
# Secure TCP communication with TLS
def secure_connection(host, port)
tcp_socket = TCPSocket.new(host, port)
ssl_context = OpenSSL::SSL::SSLContext.new
ssl_context.set_params(verify_mode: OpenSSL::SSL::VERIFY_PEER)
ssl_socket = OpenSSL::SSL::SSLSocket.new(tcp_socket, ssl_context)
ssl_socket.connect
# Verify certificate
ssl_socket.post_connection_check(host)
ssl_socket.puts "SECRET_DATA" # Encrypted over TLS
response = ssl_socket.gets
ssl_socket.close
tcp_socket.close
response
end
TCP Session Hijacking exploits predictable sequence numbers to inject malicious packets into established connections. Modern TCP implementations use random initial sequence numbers to prevent this, but older systems remain vulnerable. Applications should implement their own session tokens rather than relying on TCP connection state for security.
Firewall Rules control which packets reach application sockets. Network-level firewalls filter based on IP addresses, ports, and protocol types. Application-level firewalls inspect packet contents for malicious patterns. Ruby applications cannot directly control system firewalls but should document required ports and protocols.
DDoS Mitigation requires multiple defensive layers. Application code should handle resource limits, implement request throttling, and fail gracefully under load. Rate limiting per source IP prevents single attackers from consuming all resources. Connection pooling and efficient socket handling reduce resource consumption per connection.
Tools & Ecosystem
Ruby's networking ecosystem includes libraries and frameworks that abstract TCP/IP complexities while providing protocol-specific functionality.
Net::HTTP provides HTTP client functionality built on TCP sockets. This standard library class handles connection pooling, SSL/TLS, redirects, and various HTTP methods. Most applications use this instead of raw sockets for HTTP communication.
require 'net/http'
require 'uri'
uri = URI('https://api.github.com/users/octocat')
response = Net::HTTP.get_response(uri)
puts "Status: #{response.code}"
puts "Content-Type: #{response['content-type']}"
puts "Body: #{response.body[0..100]}"
# POST with custom headers
uri = URI('https://httpbin.org/post')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri.path)
request['Content-Type'] = 'application/json'
request.body = '{"key": "value"}'
response = http.request(request)
puts response.body
EventMachine provides event-driven I/O for high-concurrency applications. This gem implements a reactor pattern that handles multiple connections efficiently without threading overhead. EventMachine manages the event loop and callbacks for socket operations.
Async offers fiber-based concurrency for network operations. This gem provides non-blocking I/O with synchronous-looking code through Ruby's fiber scheduler. Async simplifies concurrent socket programming compared to threads or callbacks.
Celluloid combines actor-based concurrency with socket handling. Each actor runs in its own thread, communicating through message passing. Celluloid::IO provides non-blocking socket operations integrated with the actor model.
Puma and Unicorn are production web servers implementing efficient TCP connection handling. Puma uses a thread pool for concurrent request processing, while Unicorn uses forked processes. Both handle HTTP over TCP with optimizations for web traffic patterns.
WEBrick ships with Ruby as a basic HTTP server implementation. While slower than production servers, WEBrick demonstrates pure-Ruby TCP/IP server implementation and serves well for development and testing.
require 'webrick'
server = WEBrick::HTTPServer.new(Port: 8000)
server.mount_proc '/' do |req, res|
res.body = "Request from #{req.peeraddr[3]}\n"
res.body << "Path: #{req.path}\n"
res.body << "Query: #{req.query}\n"
end
trap('INT') { server.shutdown }
server.start
tcpdump and Wireshark capture and analyze TCP/IP packets at the Link and Internet layers. While not Ruby tools, these network analyzers help debug protocol issues and understand packet flow. Ruby applications generate traffic that these tools examine.
netstat and ss display active network connections and listening ports. These command-line tools show which ports Ruby applications bind to and track connection states. Monitoring these tools helps verify server startup and diagnose connection issues.
curl and wget test HTTP endpoints from the command line. These tools verify that Ruby servers correctly handle requests and responses. Both support various HTTP methods, headers, and authentication schemes.
nmap performs network discovery and port scanning. This tool identifies listening services and open ports on target systems. Understanding nmap output helps configure firewalls and verify that applications listen on intended ports.
Common Pitfalls
Forgetting to Close Sockets leads to resource leaks. Each open socket consumes file descriptors, and operating systems impose limits on descriptor counts. Long-running applications that fail to close sockets eventually exhaust available descriptors and cannot accept new connections.
# Problematic: Socket might not close on error
def connect_no_ensure(host, port)
socket = TCPSocket.new(host, port)
socket.puts "DATA"
response = socket.gets
socket.close # Skipped if exception occurs
response
end
# Correct: Always close socket
def connect_with_ensure(host, port)
socket = TCPSocket.new(host, port)
begin
socket.puts "DATA"
response = socket.gets
response
ensure
socket.close if socket
end
end
Blocking on Single Connections prevents servers from handling multiple clients. A server that calls gets or read without timeouts blocks indefinitely if clients never send data. This stalls the entire server or requires threading for each connection.
Ignoring Partial Reads causes data corruption. TCP provides a byte stream, not message boundaries. Calling read might return fewer bytes than requested if the network delivers data in chunks. Applications must loop until receiving the expected amount or implement length prefixes.
# Incorrect: Assumes single read gets all data
def read_message_incorrect(socket)
socket.read(1024) # Might return less than full message
end
# Correct: Read until delimiter or length
def read_message_correct(socket)
# Read until newline delimiter
socket.gets
end
def read_exact_bytes(socket, length)
data = String.new(encoding: 'ASCII-8BIT')
while data.bytesize < length
chunk = socket.read(length - data.bytesize)
break unless chunk
data << chunk
end
data
end
Nagle's Algorithm Delays cause latency in interactive applications. TCP delays small packets to combine them for efficiency, but this hurts request-response protocols. Disabling Nagle's algorithm with TCP_NODELAY eliminates delays for time-sensitive communication.
Not Handling SIGPIPE crashes applications when writing to closed sockets. On Unix systems, writing to a closed TCP socket generates SIGPIPE, which terminates the process by default. Applications must trap this signal or handle socket write errors.
# Handle SIGPIPE to prevent crashes
trap('PIPE') { puts 'Broken pipe ignored' }
# Or handle EPIPE errors
begin
socket.write(data)
rescue Errno::EPIPE
puts 'Client disconnected'
end
Mixing Binary and Text Mode corrupts data. Ruby defaults to text mode, which may translate line endings or encoding. Binary protocols require explicit binary mode to prevent data corruption.
socket = TCPSocket.new('example.com', 80)
socket.binmode # Disable text mode conversions
# Now safe to send binary data
binary_data = [0x89, 0x50, 0x4E, 0x47].pack('C*')
socket.write(binary_data)
Assuming Immediate Delivery breaks timing-sensitive code. TCP buffers data and may delay transmission for efficiency. Applications cannot assume write sends data immediately or that recipients receive data at specific times.
Hardcoding IPv4 excludes IPv6 networks. Modern systems require supporting both address families. Using Socket::AF_UNSPEC in getaddrinfo allows both protocols, or applications must explicitly handle both.
require 'socket'
# Bad: IPv4 only
def connect_ipv4_only(hostname, port)
TCPSocket.new(hostname, port) # Fails if only IPv6 available
end
# Good: IPv4 and IPv6
def connect_dual_stack(hostname, port)
addrs = Socket.getaddrinfo(hostname, port, Socket::AF_UNSPEC, Socket::SOCK_STREAM)
addrs.each do |addr|
begin
socket = Socket.new(addr[4], addr[5], 0)
sockaddr = Socket.sockaddr_in(port, addr[3])
socket.connect(sockaddr)
return socket
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
socket.close if socket
next
end
end
raise "Could not connect to #{hostname}:#{port}"
end
Neglecting Timeouts hangs applications indefinitely. DNS lookups, connection attempts, and read operations all block without timeouts. Setting appropriate timeouts prevents resource exhaustion from stalled operations.
Trusting Source IPs for authentication fails with spoofing. UDP packets particularly suffer from forged source addresses. Applications must implement cryptographic authentication rather than relying on network-layer addressing.
Reference
Protocol Stack Layers
| Layer | Protocols | Purpose | Ruby Access |
|---|---|---|---|
| Application | HTTP, FTP, SMTP, DNS | End-user services | Net::HTTP, Net::FTP, Resolv |
| Transport | TCP, UDP | End-to-end delivery | TCPSocket, UDPSocket |
| Internet | IP, ICMP, ARP | Routing across networks | Operating system handles |
| Link | Ethernet, WiFi, PPP | Physical transmission | Operating system handles |
Socket Classes
| Class | Protocol | Connection | Use Case |
|---|---|---|---|
| TCPSocket | TCP | Connection-oriented | Client connections |
| TCPServer | TCP | Listening server | Accept incoming connections |
| UDPSocket | UDP | Connectionless | Datagram communication |
| Socket | Various | Low-level access | Custom protocol implementation |
| BasicSocket | Abstract | Base class | Socket hierarchy root |
Common Socket Options
| Option | Level | Purpose | Values |
|---|---|---|---|
| SO_REUSEADDR | SOL_SOCKET | Reuse address after close | 0 or 1 |
| SO_KEEPALIVE | SOL_SOCKET | Send keepalive probes | 0 or 1 |
| TCP_NODELAY | IPPROTO_TCP | Disable Nagle algorithm | 0 or 1 |
| SO_RCVBUF | SOL_SOCKET | Receive buffer size | Bytes |
| SO_SNDBUF | SOL_SOCKET | Send buffer size | Bytes |
| SO_LINGER | SOL_SOCKET | Close behavior | Struct with timeout |
TCP Connection States
| State | Description | Transitions |
|---|---|---|
| CLOSED | No connection | Initial state or fully closed |
| LISTEN | Awaiting connection | Server ready for SYN |
| SYN_SENT | Connection requested | Client sent SYN, waiting for SYN-ACK |
| SYN_RECEIVED | Connection request received | Server received SYN, sent SYN-ACK |
| ESTABLISHED | Connection active | Both sides ready for data |
| FIN_WAIT_1 | Connection closing | Sent FIN, waiting for ACK |
| FIN_WAIT_2 | Waiting for peer close | Received ACK, waiting for peer FIN |
| CLOSE_WAIT | Peer closed connection | Received FIN, sending remaining data |
| CLOSING | Both sides closing | Both sent FIN simultaneously |
| LAST_ACK | Final acknowledgment | Sent FIN, waiting for ACK |
| TIME_WAIT | Connection closed | Waiting to ensure ACK received |
Socket Methods
| Method | Class | Purpose | Returns |
|---|---|---|---|
| new | TCPSocket | Create client socket | Socket instance |
| new | TCPServer | Create server socket | Server instance |
| accept | TCPServer | Accept connection | Client socket |
| connect | Socket | Establish connection | 0 on success |
| bind | Socket | Bind to address | 0 on success |
| listen | Socket | Mark as server | 0 on success |
| send | Socket | Send data | Bytes sent |
| recv | Socket | Receive data | Data string |
| close | Socket | Close connection | nil |
| setsockopt | Socket | Set option | 0 on success |
| getsockopt | Socket | Get option | Option value |
Address Families
| Constant | Value | Protocol | Description |
|---|---|---|---|
| AF_INET | 2 | IPv4 | Internet Protocol version 4 |
| AF_INET6 | 10 | IPv6 | Internet Protocol version 6 |
| AF_UNIX | 1 | Local | Unix domain sockets |
| AF_UNSPEC | 0 | Any | Protocol-independent |
Socket Types
| Constant | Protocol | Characteristics |
|---|---|---|
| SOCK_STREAM | TCP | Reliable, ordered, connection-based |
| SOCK_DGRAM | UDP | Unreliable, message-oriented, connectionless |
| SOCK_RAW | IP | Direct access to IP layer |
Error Handling
| Exception | Cause | Recovery |
|---|---|---|
| Errno::ECONNREFUSED | Port not listening | Check server running, verify port |
| Errno::ETIMEDOUT | Connection timeout | Retry with backoff, check network |
| Errno::EHOSTUNREACH | No route to host | Verify address, check routing |
| EOFError | Peer closed connection | Handle graceful shutdown |
| Errno::EPIPE | Write to closed socket | Trap SIGPIPE, handle error |
| SocketError | DNS or address error | Validate hostname, check DNS |