CrackedRuby CrackedRuby

Overview

The Transmission Control Protocol (TCP) provides reliable, ordered, and error-checked delivery of data between applications running on networked computers. TCP operates at the transport layer of the Internet protocol suite and forms the foundation for most application-layer protocols including HTTP, SMTP, FTP, and SSH.

TCP emerged from ARPANET research in the 1970s. The protocol splits application data into segments, adds sequencing information, manages retransmission of lost packets, and ensures data arrives in the correct order. Unlike UDP which offers unreliable datagram delivery, TCP guarantees that data sent from one endpoint arrives intact at the destination.

The protocol operates through a connection-oriented model. Before data transmission begins, TCP establishes a connection through a three-way handshake. During active transmission, TCP monitors packet delivery, maintains flow control to prevent overwhelming the receiver, and implements congestion control to respond to network conditions. When communication completes, TCP terminates the connection through a four-way handshake.

TCP connections occur between two endpoints identified by IP addresses and port numbers. A socket pair (source IP, source port, destination IP, destination port) uniquely identifies each TCP connection. The protocol uses sequence numbers to track bytes sent and acknowledgment numbers to confirm receipt. TCP headers include control flags (SYN, ACK, FIN, RST, PSH, URG) that coordinate connection management and data delivery.

Applications interact with TCP through socket APIs. The operating system kernel implements the TCP protocol stack, handling packet segmentation, acknowledgment tracking, retransmission timers, and window management. Applications write data to sockets and read data from sockets without managing the underlying protocol complexity.

TCP provides several key services to applications: reliable delivery through acknowledgments and retransmissions, ordered delivery through sequence numbers, flow control through sliding windows, congestion control through algorithms like Slow Start and Congestion Avoidance, and full-duplex communication allowing simultaneous bidirectional data transfer.

Key Principles

TCP operates as a stateful, connection-oriented protocol. Each connection maintains state information including sequence numbers, acknowledgment numbers, window sizes, round-trip time estimates, and retransmission timer values. This state management enables the reliability guarantees TCP provides.

Connection Establishment

TCP establishes connections through a three-way handshake:

  1. Client sends SYN segment with initial sequence number (ISN)
  2. Server responds with SYN-ACK segment containing its own ISN and acknowledging client's ISN
  3. Client sends ACK segment acknowledging server's ISN

This handshake synchronizes sequence numbers and establishes initial window sizes. The initial sequence numbers prevent confusion from delayed packets belonging to previous connections between the same endpoints.

Client                    Server
  |                         |
  |----SYN (seq=x)--------->|
  |                         |
  |<---SYN-ACK (seq=y)------|
  |    (ack=x+1)            |
  |                         |
  |----ACK (ack=y+1)------->|
  |                         |

Sequence Numbers and Acknowledgments

TCP assigns a sequence number to each byte of data. The sequence number in a TCP segment indicates the position of the first data byte in that segment. Acknowledgment numbers indicate the next expected sequence number, confirming receipt of all bytes up to that point.

Cumulative acknowledgments confirm receipt of all data up to a specific sequence number. If segments arrive out of order, TCP buffers them until missing segments arrive. Selective acknowledgments (SACK) allow receivers to acknowledge non-contiguous blocks of data, enabling more efficient retransmission of specific lost segments.

Reliability Through Retransmission

TCP retransmits segments when acknowledgments don't arrive within the retransmission timeout (RTO). The RTO calculation considers the measured round-trip time (RTT) and its variation. TCP samples RTT continuously and adjusts RTO using smoothed round-trip time (SRTT) and round-trip time variation (RTTVAR):

SRTT = (1 - α) × SRTT + α × RTT_sample
RTTVAR = (1 - β) × RTTVAR + β × |SRTT - RTT_sample|
RTO = SRTT + max(G, 4 × RTTVAR)

Where α = 1/8, β = 1/4, and G represents the clock granularity.

Flow Control

TCP implements sliding window flow control to prevent fast senders from overwhelming slow receivers. Each ACK segment includes a window size indicating how much data the receiver can accept. The sender limits unacknowledged data to the receiver's advertised window.

The window slides forward as the receiver processes data and sends acknowledgments with updated window sizes. Window scaling extends the maximum window size beyond 65,535 bytes for high-bandwidth networks.

Congestion Control

TCP adjusts transmission rates in response to network congestion. The congestion window (cwnd) limits how much unacknowledged data the sender can transmit. The effective transmission window equals the minimum of the congestion window and the receiver's advertised window.

TCP implements several congestion control phases:

Slow Start: Begins with cwnd = 1 MSS (Maximum Segment Size). Each ACK increases cwnd by 1 MSS, causing exponential growth until reaching the slow start threshold (ssthresh).

Congestion Avoidance: Once cwnd reaches ssthresh, growth becomes linear. Each RTT increases cwnd by approximately 1 MSS.

Fast Retransmit: When receiving three duplicate ACKs, TCP retransmits the missing segment without waiting for timeout.

Fast Recovery: After fast retransmit, TCP reduces ssthresh to half of cwnd, sets cwnd to ssthresh plus 3 MSS, and continues with congestion avoidance.

Connection Termination

TCP connections terminate through a four-way handshake allowing independent closure of each direction:

  1. Active closer sends FIN segment
  2. Passive closer acknowledges with ACK
  3. Passive closer sends its own FIN when ready to close
  4. Active closer acknowledges with ACK

The connection enters TIME_WAIT state after final ACK, lasting 2 × MSL (Maximum Segment Lifetime) to ensure final ACK arrives and prevent delayed segments from confusing future connections.

Ruby Implementation

Ruby provides TCP networking through the Socket library in the standard library. The TCPServer and TCPSocket classes offer higher-level abstractions over raw sockets for common TCP server and client patterns.

Basic TCP Client

require 'socket'

# Connect to server
socket = TCPSocket.new('example.com', 80)

# Send HTTP request
socket.puts "GET / HTTP/1.1"
socket.puts "Host: example.com"
socket.puts "Connection: close"
socket.puts ""

# Read response
response = socket.read
puts response

socket.close

The TCPSocket constructor performs DNS resolution and establishes the TCP connection, handling the three-way handshake automatically. The socket acts as an IO object supporting standard read and write operations.

TCP Server Implementation

require 'socket'

server = TCPServer.new(8080)
puts "Server listening on port 8080"

loop do
  # Accept blocks until client connects
  client = server.accept
  
  # Read client request
  request = client.gets
  puts "Received: #{request}"
  
  # Send response
  client.puts "HTTP/1.1 200 OK"
  client.puts "Content-Type: text/plain"
  client.puts "Connection: close"
  client.puts ""
  client.puts "Hello from TCP server"
  
  client.close
end

TCPServer.accept blocks until a client connects, returning a socket for that specific connection. Each accepted connection gets its own socket instance, allowing concurrent handling of multiple clients.

Multi-threaded TCP Server

require 'socket'

server = TCPServer.new(8080)
puts "Multi-threaded server listening on port 8080"

loop do
  Thread.start(server.accept) do |client|
    begin
      request = client.gets
      
      # Process request
      response = process_request(request)
      
      client.puts response
    rescue => e
      puts "Error handling client: #{e.message}"
    ensure
      client.close
    end
  end
end

def process_request(request)
  # Request processing logic
  "Processed: #{request}"
end

Threading enables concurrent client handling. Each connection runs in its own thread, preventing slow clients from blocking other connections. The ensure block guarantees socket cleanup even when errors occur.

Non-blocking TCP Operations

require 'socket'

socket = TCPSocket.new('example.com', 80)

# Make socket non-blocking
socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, 1)

begin
  # Non-blocking write
  socket.write_nonblock("GET / HTTP/1.1\r\n\r\n")
rescue IO::WaitWritable
  # Socket buffer full, wait for writability
  IO.select(nil, [socket])
  retry
end

begin
  # Non-blocking read
  data = socket.read_nonblock(4096)
  puts data
rescue IO::WaitReadable
  # No data available, wait for readability
  IO.select([socket])
  retry
rescue EOFError
  # Connection closed
  puts "Connection closed by server"
end

socket.close

Non-blocking operations return immediately with IO::WaitReadable or IO::WaitWritable exceptions when operations would block. IO.select enables multiplexing multiple sockets efficiently.

Socket Options and Configuration

require 'socket'

socket = TCPSocket.new('example.com', 80)

# Enable TCP keepalive
socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, 1)

# Set keepalive parameters (Linux)
socket.setsockopt(Socket::SOL_TCP, Socket::TCP_KEEPIDLE, 60)
socket.setsockopt(Socket::SOL_TCP, Socket::TCP_KEEPINTVL, 10)
socket.setsockopt(Socket::SOL_TCP, Socket::TCP_KEEPCNT, 3)

# Disable Nagle's algorithm
socket.setsockopt(Socket::SOL_TCP, Socket::TCP_NODELAY, 1)

# Set send buffer size
socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, 65536)

# Set receive buffer size
socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_RCVBUF, 65536)

# Get current options
keepalive = socket.getsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE)
puts "Keepalive enabled: #{keepalive.bool}"

socket.close

Socket options control TCP behavior. TCP_NODELAY disables Nagle's algorithm, reducing latency for small writes. Buffer sizes affect memory usage and throughput. Keepalive probes detect dead connections.

Handling Connection Timeouts

require 'socket'
require 'timeout'

def connect_with_timeout(host, port, timeout_seconds)
  Timeout.timeout(timeout_seconds) do
    TCPSocket.new(host, port)
  end
rescue Timeout::Error
  raise "Connection timeout after #{timeout_seconds} seconds"
end

# Connection with timeout
socket = connect_with_timeout('example.com', 80, 5)

# Read with timeout
begin
  Timeout.timeout(10) do
    data = socket.read
    puts data
  end
rescue Timeout::Error
  puts "Read operation timed out"
ensure
  socket.close if socket
end

Timeout module wraps blocking operations with time limits. This prevents indefinite blocking when servers become unresponsive or network issues occur.

Implementation Approaches

TCP applications follow several architectural patterns depending on requirements for concurrency, scalability, and resource efficiency.

Single-threaded Blocking Server

The simplest server implementation accepts connections sequentially, processing each client completely before accepting the next. This approach uses minimal resources and simple code but cannot handle concurrent clients:

require 'socket'

server = TCPServer.new(8080)

loop do
  client = server.accept
  handle_client(client)
  client.close
end

This pattern works for low-traffic services or testing but creates head-of-line blocking where slow clients delay all subsequent connections.

Thread-per-connection Model

Spawning a thread for each connection enables concurrent client handling. The operating system schedules threads, allowing multiple connections to progress simultaneously:

require 'socket'

server = TCPServer.new(8080)

loop do
  client = server.accept
  Thread.new(client) do |conn|
    handle_client(conn)
    conn.close
  end
end

This approach scales to hundreds of concurrent connections on modern systems. Thread creation overhead and memory consumption become concerns beyond a few thousand connections. Thread pools limit maximum concurrency and reuse threads to reduce overhead.

Thread Pool Implementation

require 'socket'
require 'thread'

server = TCPServer.new(8080)
queue = Queue.new

# Create worker threads
workers = 10.times.map do
  Thread.new do
    loop do
      client = queue.pop
      handle_client(client)
      client.close
    end
  end
end

# Accept connections and queue them
loop do
  client = server.accept
  queue.push(client)
end

Thread pools bound resource consumption and eliminate thread creation overhead for each connection. Queue depth indicates load, enabling monitoring and load shedding when capacity approaches limits.

Non-blocking IO with Event Loop

Event-driven servers use IO multiplexing to handle many connections in a single thread. IO.select monitors multiple sockets and returns when any become readable or writable:

require 'socket'

server = TCPServer.new(8080)
clients = []

loop do
  # Monitor server and all clients
  readable, writable = IO.select([server] + clients, clients, nil, 1)
  
  readable&.each do |socket|
    if socket == server
      # New connection
      clients << server.accept
    else
      # Data available from client
      begin
        data = socket.read_nonblock(4096)
        process_data(socket, data)
      rescue EOFError
        socket.close
        clients.delete(socket)
      end
    end
  end
  
  writable&.each do |socket|
    # Socket ready for writing
    send_pending_data(socket)
  end
end

This pattern scales to tens of thousands of connections with minimal memory overhead. Complexity increases compared to threaded models, particularly when implementing state machines for partial reads and writes.

Reactor Pattern

The Reactor pattern structures event-driven servers with separation between event detection and event handling:

class Reactor
  def initialize
    @handlers = {}
    @running = true
  end
  
  def register(socket, handler)
    @handlers[socket] = handler
  end
  
  def unregister(socket)
    @handlers.delete(socket)
  end
  
  def run
    while @running
      readable = @handlers.keys
      ready_sockets = IO.select(readable, nil, nil, 1)
      
      ready_sockets&.first&.each do |socket|
        handler = @handlers[socket]
        handler.handle_read(socket)
      end
    end
  end
  
  def stop
    @running = false
  end
end

class ClientHandler
  def handle_read(socket)
    data = socket.read_nonblock(4096)
    process(data)
    socket.write("Response\n")
  rescue EOFError
    socket.close
  end
  
  def process(data)
    # Application logic
  end
end

Reactors decouple event monitoring from business logic. Handlers encapsulate connection-specific behavior, improving code organization for complex protocols.

Hybrid Threading and Event Models

Modern servers often combine threading and event-driven approaches. Multiple threads each run event loops, distributing connections across threads:

require 'socket'

server = TCPServer.new(8080)
thread_count = 4

threads = thread_count.times.map do
  Thread.new do
    clients = []
    
    loop do
      readable, _ = IO.select([server] + clients, nil, nil, 0.1)
      next unless readable
      
      readable.each do |socket|
        if socket == server
          clients << server.accept
        else
          handle_client_event(socket)
        end
      end
    end
  end
end

threads.each(&:join)

This hybrid approach balances single-threaded event loop efficiency with multi-core CPU utilization. Each thread handles hundreds or thousands of connections, while multiple threads utilize available CPU cores.

Performance Considerations

TCP performance depends on numerous factors including network conditions, buffer sizes, congestion control algorithms, and application behavior. Understanding these factors enables optimization for specific use cases.

Bandwidth-Delay Product

The bandwidth-delay product (BDP) determines optimal window sizes for maximum throughput. BDP equals bandwidth multiplied by round-trip time:

BDP = bandwidth × RTT

For a 100 Mbps connection with 50ms RTT:

BDP = 100 Mbps × 0.05s = 5 Mb = 625 KB

TCP window sizes should match or exceed BDP for full bandwidth utilization. Default window sizes (64 KB) limit throughput on high-bandwidth, high-latency networks. Window scaling enables windows up to 1 GB.

Nagle's Algorithm Trade-offs

Nagle's algorithm coalesces small writes into larger segments, reducing packet overhead. The algorithm delays transmission when unacknowledged data exists and the current segment is smaller than MSS. This reduces packets but increases latency.

Applications sending many small messages experience delayed transmission. Disabling Nagle's algorithm (TCP_NODELAY) reduces latency but increases packet count. Interactive applications like games and terminals benefit from TCP_NODELAY. Bulk data transfer benefits from Nagle's algorithm.

# Disable Nagle's algorithm for low latency
socket.setsockopt(Socket::SOL_TCP, Socket::TCP_NODELAY, 1)

Socket Buffer Sizing

Send and receive buffers affect throughput and memory usage. Small buffers limit throughput on high-bandwidth connections. Large buffers consume more memory per connection, reducing maximum connection count.

# Increase buffer sizes for high-throughput connections
socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF, 1048576)  # 1 MB
socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_RCVBUF, 1048576)  # 1 MB

Operating systems auto-tune buffer sizes based on connection characteristics. Manual tuning helps when hosting many connections or optimizing specific workloads.

Connection Pooling

Establishing TCP connections involves three-way handshake latency and TLS handshake overhead for secure connections. Connection pooling reuses connections across multiple requests:

class ConnectionPool
  def initialize(host, port, size)
    @host = host
    @port = port
    @available = Queue.new
    
    size.times do
      @available.push(create_connection)
    end
  end
  
  def with_connection
    conn = @available.pop
    yield conn
  ensure
    @available.push(conn)
  end
  
  private
  
  def create_connection
    TCPSocket.new(@host, @port)
  end
end

pool = ConnectionPool.new('example.com', 80, 10)

pool.with_connection do |socket|
  socket.write("GET / HTTP/1.1\r\n\r\n")
  response = socket.read
end

Pools reduce connection establishment overhead for workloads with repeated short requests. Web scraping, API clients, and database connections benefit from pooling.

TCP Fast Open

TCP Fast Open (TFO) eliminates one round-trip from connection establishment by sending data in the SYN packet. The client includes a TFO cookie from previous connections, allowing the server to accept data before completing the handshake.

TFO reduces latency by up to one RTT for repeated connections between the same endpoints. Limited operating system and application support restricts TFO adoption.

Keepalive Configuration

TCP keepalive probes detect dead connections by sending periodic packets. Default keepalive intervals (2 hours) delay failure detection. Shorter intervals detect failures faster but increase network traffic:

socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, 1)
socket.setsockopt(Socket::SOL_TCP, Socket::TCP_KEEPIDLE, 60)   # Start after 60s idle
socket.setsockopt(Socket::SOL_TCP, Socket::TCP_KEEPINTVL, 10)  # Send every 10s
socket.setsockopt(Socket::SOL_TCP, Socket::TCP_KEEPCNT, 3)     # Give up after 3 probes

Application-layer heartbeats provide more control than TCP keepalive but require protocol support.

Congestion Control Algorithm Selection

Different congestion control algorithms optimize for different network conditions. CUBIC (Linux default) optimizes for high-bandwidth networks. BBR focuses on measuring actual bottleneck bandwidth and round-trip time. Reno provides conservative behavior for network stability.

Most applications accept default congestion control. Changing algorithms requires root privileges and affects all connections on the system.

Security Implications

TCP itself provides no security. Data travels unencrypted and unauthenticated over networks. Several attacks target TCP weaknesses.

SYN Flood Attacks

SYN floods exploit the TCP handshake by sending many SYN packets without completing connections. Servers allocate resources for half-open connections, exhausting available connections or memory.

SYN cookies mitigate SYN floods by encoding connection state in sequence numbers. Instead of allocating resources during SYN-ACK, servers store no state and reconstruct connection parameters from the ACK sequence number.

Operating systems enable SYN cookies automatically under load. Applications see no difference in behavior.

Connection Hijacking

TCP connections are vulnerable to hijacking when attackers guess sequence numbers. Injecting packets with correct sequence numbers inserts malicious data into the stream or terminates connections with RST packets.

Initial sequence number randomization makes guessing difficult but not impossible. Off-path attackers observe traffic to estimate sequence numbers. On-path attackers see actual sequence numbers.

TLS protects against hijacking through encryption and authentication. The encrypted payload prevents meaningful injection, and message authentication codes detect tampering.

Denial of Service Through Resource Exhaustion

Attackers establish many connections to exhaust server resources. Even with SYN cookies, completed connections consume memory for buffers, socket structures, and application state.

Rate limiting, connection limits per source IP, and connection timeouts mitigate resource exhaustion. Application-layer authentication reduces impact by rejecting unauthorized connections early.

require 'socket'

class RateLimitedServer
  def initialize(port, max_connections_per_ip)
    @server = TCPServer.new(port)
    @connections = Hash.new(0)
    @max_per_ip = max_connections_per_ip
  end
  
  def accept_with_limit
    loop do
      client = @server.accept
      ip = client.remote_address.ip_address
      
      if @connections[ip] >= @max_per_ip
        client.close
        next
      end
      
      @connections[ip] += 1
      return [client, ip]
    end
  end
  
  def release(ip)
    @connections[ip] -= 1
  end
end

Port Scanning and Reconnaissance

TCP three-way handshakes reveal open ports. SYN scanning (sending SYN and analyzing responses) maps services quickly. Servers cannot prevent port scanning but can detect and block scanning sources.

Man-in-the-Middle Attacks

TCP provides no endpoint authentication. Attackers intercepting connections impersonate either endpoint. ARP spoofing, DNS spoofing, and BGP hijacking enable position for man-in-the-middle attacks.

TLS prevents man-in-the-middle attacks through certificate validation. Applications should verify certificates against trusted certificate authorities and check hostname matching.

require 'socket'
require 'openssl'

def secure_connection(host, port)
  socket = TCPSocket.new(host, port)
  
  ssl_context = OpenSSL::SSL::SSLContext.new
  ssl_context.verify_mode = OpenSSL::SSL::VERIFY_PEER
  ssl_context.cert_store = OpenSSL::X509::Store.new
  ssl_context.cert_store.set_default_paths
  
  ssl_socket = OpenSSL::SSL::SSLSocket.new(socket, ssl_context)
  ssl_socket.sync_close = true
  ssl_socket.hostname = host  # Enable SNI and hostname verification
  ssl_socket.connect
  
  ssl_socket
end

# Use TLS connection
ssl_socket = secure_connection('example.com', 443)
ssl_socket.write("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n")
response = ssl_socket.read
ssl_socket.close

Firewall and Network Segmentation

Limiting TCP connectivity reduces attack surface. Firewalls block unauthorized connections based on source IP, destination port, and other criteria. Network segmentation isolates systems, containing breaches and limiting lateral movement.

Applications should bind to specific interfaces rather than all interfaces when possible:

# Bind only to localhost, preventing external access
server = TCPServer.new('127.0.0.1', 8080)

# Bind to all interfaces
server = TCPServer.new('0.0.0.0', 8080)

Tools & Ecosystem

Ruby's TCP ecosystem includes standard library components, third-party gems, and debugging tools for network programming.

Standard Library Components

The Socket library provides low-level socket access. TCPServer and TCPSocket offer convenient abstractions for common patterns. UDPSocket handles UDP. IPSocket provides IP-specific functionality. BasicSocket defines the base socket interface.

EventMachine

EventMachine implements the Reactor pattern for high-performance network servers:

require 'eventmachine'

module EchoServer
  def post_init
    puts "Client connected"
  end
  
  def receive_data(data)
    send_data("Echo: #{data}")
  end
  
  def unbind
    puts "Client disconnected"
  end
end

EventMachine.run do
  EventMachine.start_server('0.0.0.0', 8080, EchoServer)
  puts "Server running on port 8080"
end

EventMachine handles event loop management, connection lifecycle, and asynchronous operations. The pattern-oriented API simplifies complex server implementations.

Async and Async::IO

The async gem provides fiber-based concurrency for IO operations:

require 'async'
require 'async/io/stream'
require 'async/io/endpoint'

Async do
  endpoint = Async::IO::Endpoint.tcp('example.com', 80)
  
  endpoint.connect do |socket|
    stream = Async::IO::Stream.new(socket)
    
    stream.write("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n")
    
    response = stream.read_until("\r\n\r\n")
    puts response
  end
end

Async uses fibers for cooperative multitasking, avoiding threads overhead while maintaining synchronous code structure.

Celluloid::IO

Celluloid::IO combines actor-based concurrency with event-driven IO:

require 'celluloid/io'

class TcpServer
  include Celluloid::IO
  
  def initialize(host, port)
    @server = Celluloid::IO::TCPServer.new(host, port)
    async.run
  end
  
  def run
    loop do
      async.handle_client(@server.accept)
    end
  end
  
  def handle_client(socket)
    data = socket.readpartial(4096)
    socket.write("Echo: #{data}")
    socket.close
  end
end

server = TcpServer.new('0.0.0.0', 8080)
sleep

Celluloid actors run concurrently, each with independent state. Supervisors restart failed actors automatically.

Debugging Tools

tcpdump captures TCP packets for analysis:

tcpdump -i eth0 tcp port 8080 -w capture.pcap

Wireshark provides graphical packet analysis with TCP stream reconstruction, timing analysis, and protocol dissection.

netstat and ss display active connections:

netstat -an | grep :8080
ss -tan sport :8080

strace traces system calls including socket operations:

strace -e trace=network ruby server.rb

lsof lists open sockets per process:

lsof -i TCP:8080

Connection Testing Tools

telnet tests basic TCP connectivity:

telnet example.com 80
GET / HTTP/1.1
Host: example.com

nc (netcat) provides flexible TCP/UDP testing:

nc -zv example.com 80          # Port scan
nc -l 8080                     # Listen mode
echo "test" | nc example.com 80  # Send data

Performance Testing

iperf3 measures TCP throughput:

iperf3 -s                      # Server
iperf3 -c server_ip           # Client

ab (Apache Bench) load tests HTTP over TCP:

ab -n 1000 -c 10 http://localhost:8080/

Reference

TCP Header Structure

Field Size (bits) Description
Source Port 16 Sending application port number
Destination Port 16 Receiving application port number
Sequence Number 32 Position of first data byte in stream
Acknowledgment Number 32 Next expected sequence number
Data Offset 4 TCP header size in 32-bit words
Reserved 3 Reserved for future use
Flags 9 Control flags (NS, CWR, ECE, URG, ACK, PSH, RST, SYN, FIN)
Window Size 16 Receive window size in bytes
Checksum 16 Error detection checksum
Urgent Pointer 16 Offset to urgent data
Options Variable Optional parameters (max 40 bytes)

TCP Control Flags

Flag Name Purpose
SYN Synchronize Initiates connection and synchronizes sequence numbers
ACK Acknowledge Acknowledges received data
FIN Finish Requests connection termination
RST Reset Aborts connection immediately
PSH Push Requests immediate data delivery to application
URG Urgent Indicates urgent data in segment
ECE ECN-Echo Explicit Congestion Notification echo
CWR Congestion Window Reduced Congestion window reduced response
NS Nonce Sum ECN-nonce concealment protection

TCP States

State Description
CLOSED No connection exists
LISTEN Server waiting for connection requests
SYN_SENT Client sent SYN, waiting for SYN-ACK
SYN_RECEIVED Server received SYN, sent SYN-ACK
ESTABLISHED Connection established, data transfer possible
FIN_WAIT_1 Active close initiated, FIN sent
FIN_WAIT_2 Remote FIN acknowledged, waiting for remote FIN
CLOSE_WAIT Remote side closed, waiting for application close
CLOSING Both sides closing simultaneously
LAST_ACK Waiting for final FIN acknowledgment
TIME_WAIT Waiting for delayed segments to expire

Socket Options

Option Level Description
SO_KEEPALIVE SOL_SOCKET Enable TCP keepalive probes
SO_REUSEADDR SOL_SOCKET Allow binding to recently used addresses
SO_REUSEPORT SOL_SOCKET Allow multiple bindings to same port
SO_SNDBUF SOL_SOCKET Set send buffer size
SO_RCVBUF SOL_SOCKET Set receive buffer size
TCP_NODELAY SOL_TCP Disable Nagle's algorithm
TCP_KEEPIDLE SOL_TCP Keepalive idle time before probes
TCP_KEEPINTVL SOL_TCP Interval between keepalive probes
TCP_KEEPCNT SOL_TCP Number of keepalive probes before timeout
TCP_CORK SOL_TCP Delay sending until full frames available

Ruby Socket Classes

Class Purpose
TCPSocket TCP client connections
TCPServer TCP server listening and accepting
UDPSocket UDP datagram communication
UNIXSocket Unix domain socket client
UNIXServer Unix domain socket server
Socket Low-level socket interface
IPSocket Internet protocol socket base class
BasicSocket Base socket functionality

Common TCP Ports

Port Service Description
20 FTP-DATA File Transfer Protocol data
21 FTP File Transfer Protocol control
22 SSH Secure Shell remote access
23 Telnet Unencrypted remote access
25 SMTP Simple Mail Transfer Protocol
80 HTTP Hypertext Transfer Protocol
110 POP3 Post Office Protocol
143 IMAP Internet Message Access Protocol
443 HTTPS HTTP over TLS/SSL
3306 MySQL MySQL database
5432 PostgreSQL PostgreSQL database
6379 Redis Redis data structure store
27017 MongoDB MongoDB database

Performance Parameters

Parameter Typical Value Impact
Initial Congestion Window 10 MSS Connection startup throughput
Maximum Segment Size 1460 bytes (Ethernet) Packet size and efficiency
Receive Window 64 KB - 1 GB Maximum unacknowledged data
Retransmission Timeout 1 sec - 60 sec Recovery from packet loss
TIME_WAIT Duration 2 MSL (60-240 sec) Connection reuse delay
Default Keepalive Time 7200 sec (2 hours) Dead connection detection

Congestion Control Algorithms

Algorithm Characteristics Use Case
Reno Conservative, multiplicative decrease General purpose, stable networks
CUBIC High-bandwidth optimized, less aggressive Default on Linux, fast networks
BBR Bandwidth and RTT probing Variable networks, minimizing bufferbloat
Vegas Delay-based congestion detection Low-latency requirements
Westwood+ Bandwidth estimation Wireless networks

Ruby Socket Error Classes

Error Trigger Condition
Errno::ECONNREFUSED Remote host actively refused connection
Errno::ETIMEDOUT Connection attempt timed out
Errno::ENETUNREACH Network unreachable
Errno::EHOSTUNREACH Host unreachable
Errno::EADDRINUSE Address already in use
Errno::EADDRNOTAVAIL Address not available
IO::WaitReadable Non-blocking read would block
IO::WaitWritable Non-blocking write would block
EOFError Connection closed by remote peer
SocketError Generic socket error