TCP, IP, and UDP: Understanding Internet Transport Protocols

Compare TCP vs UDP, learn the three-way handshake, flow control, congestion control, when to use each protocol, and how QUIC changes things.

published: reading time: 35 min read author: GeekWorkBench updated: June 17, 2026
Quick Summary

TCP and UDP are the foundational protocols that move data across the internet. TCP handles reliable, ordered delivery with connection setup and flow control—phone call energy. UDP trades that reliability for speed with a fire-and-forget approach. QUIC layers TCP's guarantees onto UDP's speed, and it's now the transport behind HTTP/3.

TCP, IP, and UDP are the foundational protocols that move data across the internet. TCP handles reliable, ordered delivery with connection setup and flow control, while UDP trades reliability for speed with its connectionless, fire-and-forget model. Understanding when each protocol is appropriate — and how QUIC is changing the equation — is essential for anyone building networked systems. This post covers the layered model, how the three-way handshake works, flow and congestion control, and practical guidance on choosing between TCP and UDP for your application.

Introduction

Networking uses a layered model. Each layer handles specific responsibilities:

graph TB
    A[Application Layer<br/>HTTP, DNS, SMTP] --> B[Transport Layer<br/>TCP, UDP]
    B --> C[Internet Layer<br/>IP]
    C --> D[Link Layer<br/>Ethernet, WiFi]

IP handles addressing and routing. TCP and UDP sit on top of IP, adding their own features. You rarely choose between TCP and UDP directly; you choose an application protocol that uses one or the other.


TCP Protocol

TCP: Transmission Control Protocol

TCP is the workhorse protocol of the internet. It gives applications a stream abstraction that looks like a plain byte pipe, but underneath the kernel is doing a lot of work: ordering packets, retransmitting losses, throttling the sender when the network is congested, and verifying that data arrived intact. The application writes bytes, the kernel decides how to ship them.

Before any byte of application data flows, TCP negotiates a connection through a three-way handshake. After the connection is up, every segment carries a sequence number so the receiver can reassemble the original stream even when packets arrive out of order. When the conversation ends, either side initiates a clean shutdown with a FIN exchange.

The cost of all this reliability is latency and overhead. A TCP segment carries a 20-byte header (60 with options), and the connection setup adds at least one round-trip before the first application byte can be sent. The sections below break down each piece of the contract: the handshake, reliable delivery, flow control, and congestion control.

The Three-Way Handshake

TCP uses a three-way handshake to establish connections:

sequenceDiagram
    participant Client
    participant Server
    Client->>Server: SYN (seq=x)
    Server->>Client: SYN-ACK (seq=y, ack=x+1)
    Client->>Server: ACK (ack=y+1)
    Note over Client,Server: Connection established!
  1. Client sends SYN with a sequence number
  2. Server responds with SYN-ACK, acknowledging the client’s sequence number and sending its own sequence number
  3. Client sends ACK, acknowledging the server’s sequence number

This takes a full round trip before any data can be sent. HTTPS adds TLS on top, requiring more round trips.

Reliable Data Transfer

TCP guarantees that data arrives intact and in order. It does this through:

  • Acknowledgments (ACKs) - Receiver confirms receipt of data
  • Sequence numbers - Data is numbered so the receiver can reorder out-of-order packets
  • Retransmission - If data is not acknowledged, it is retransmitted
// TCP guarantees (simplified)
const sender = {
  sequence: 0,
  send(data) {
    const packet = { data, sequence: this.sequence };
    this.sequence += data.length;
    return packet;
  },
};

const receiver = {
  expectedSequence: 0,
  receive(packet) {
    if (packet.sequence === this.expectedSequence) {
      this.expectedSequence += packet.data.length;
      return { ack: packet.sequence + packet.data.length };
    }
    // Out of order - request retransmission
    return { ack: this.expectedSequence };
  },
};

Flow Control

TCP prevents the sender from overwhelming the receiver. The receiver advertises a window size indicating how much buffer space it has. The sender cannot send more than this window without receiving acknowledgments.

// Flow control window
const receiver = {
  bufferSize: 65535,
  usedBuffer: 0,
  windowSize() {
    return this.bufferSize - this.usedBuffer;
  },
};

Congestion Control

TCP also prevents overwhelming the network. It uses algorithms like slow start, congestion avoidance, and fast retransmit to dynamically adjust sending rate.

graph LR
    A[Slow Start] --> B[Congestion<br/>Avoidance]
    A -->|"packet loss"| C[Reduce Rate]
    B -->|"packet loss"| C
    C --> A

Slow start begins with a small window and exponentially increases it until packets are lost. This probing helps TCP find the available bandwidth without causing congestion.


UDP Protocol

UDP: User Datagram Protocol

UDP is the minimalist sibling of TCP. There is no connection, no handshake, no acknowledgment, no retransmission, no ordering guarantee. The application hands a datagram to the kernel, the kernel puts it on the wire, and that’s the end of the story. If the packet arrives out of order, duplicated, or not at all, the application finds out only by noticing the gap.

The 8-byte UDP header carries almost nothing: source port, destination port, length, and checksum (optional in IPv4, mandatory in IPv6). The kernel does not even reserve any per-connection state, so a single UDP socket can talk to thousands of peers without a corresponding explosion of socket buffers. That property makes UDP the natural fit for one-to-many multicast and for protocols that manage their own reliability on top.

The trade is that UDP is best understood as a thin wrapper around the IP layer, not a transport in the same sense as TCP. Sections below cover the characteristics, the wire format, and the question of when to choose UDP over TCP or QUIC.

UDP Characteristics

  • No connection establishment (zero latency)
  • No ordering or sequencing
  • No retransmission of lost packets
  • Small header overhead (8 bytes vs TCP’s 20+ bytes)
// UDP is simple
const sender = {
  send(data, address) {
    const datagram = { data, destination: address };
    // Send and forget - no acknowledgment
    return datagram;
  },
};

const receiver = {
  receive(datagram) {
    // Handle datagram - might be duplicate, might be missing
    return datagram.data;
  },
};

UDP Header

UDP has a minimal header:

+----------------+----------------+----------------+----------------+
| Source Port    | Dest Port     | Length        | Checksum       |
+----------------+----------------+----------------+----------------+

Four 16-bit fields. Source port is optional (set to 0 if not used). Length includes header and data. Checksum for error detection.


TCP vs UDP Comparison

Feature Comparison Table

FeatureTCPUDP
ConnectionConnection-orientedConnectionless
ReliabilityGuaranteed deliveryBest effort
OrderingIn-order deliveryNo ordering
SpeedSlower (handshake, ACKs)Faster (no overhead)
Header size20+ bytes8 bytes
Flow controlYesNo
Congestion controlYesNo

When to Use TCP

TCP is the right choice when:

  • You need all data to arrive intact
  • Order matters (files, messages, documents)
  • You can tolerate some latency
  • You are building HTTP servers, email, file transfer

Most web traffic uses TCP. The reliability guarantees mean you do not have to handle missing or duplicate data yourself.

When to Use UDP

UDP works well when:

  • Speed matters more than reliability
  • Real-time applications (voice, video, gaming)
  • You want minimal overhead
  • Application-level error handling is sufficient
  • Multicast or broadcast is needed
// Good UDP use cases
const videoStream = {
  protocol: "UDP",
  // Missing frames are less noticeable than delay
  // Accept some packet loss for real-time playback
};

const voiceCall = {
  protocol: "UDP",
  // Prefer hearing the other person with small gaps
  // Over hearing them perfectly but delayed
};

const dnsQuery = {
  protocol: "UDP",
  // Fast lookup matters more than perfect reliability
  // DNS servers retry if no response
};

Port Numbers

Both TCP and UDP use port numbers to multiplex connections. Ports range from 0 to 65535. Well-known ports (0-1023) are reserved for common services:

PortServiceProtocol
80HTTPTCP
443HTTPSTCP
53DNSUDP (also TCP)
22SSHTCP
25SMTPTCP

Your application can use any port above 1024. Node.js http.createServer() defaults to port 3000, for example.


Topic-Specific Deep Dives

Common Misconceptions

TCP and UDP each get a small set of folk beliefs attached to them. The beliefs are not wrong in their original context, but they get over-applied in systems where they no longer hold. Most of them survive because training material and interview prep love simple rules, so “TCP is reliable, UDP is fast” stays in circulation long after the original caveats got dropped. Conference talks repeat the same framing to save slide time, and vendor marketing of UDP-based products tends to overstate the speed wins. Once a folk belief is in the air, engineers pass it along without ever having to defend it in production.

What makes these myths dangerous is that they often work in the lab and break in production. A test on clean localhost shows UDP blasting data while TCP dawdles through its handshake. A demo streaming local video files works fine on TCP. The gap between demo behavior and real network behavior is where the trouble starts. The sub-sections below look at each myth, trace it back to the conditions that made it true, and describe the conditions where it stops holding.

A useful mental shift when reading claims about protocols: stop asking “which protocol is faster” and start asking “faster under what conditions, measured how, and at what cost to other things I care about.” Reliability, fairness to other flows, NAT traversal, and deployment complexity all shift the answer. The rest of this post covers the building blocks (handshake overhead, congestion control, head-of-line blocking, transport-layer encryption) you will need to actually answer that question for your own system.

”UDP is always faster”

UDP avoids the TCP handshake and per-segment acknowledgment overhead, so in a lab environment it is plainly faster. Real networks change the equation. When packets disappear on the public internet, UDP and TCP diverge sharply. UDP has no congestion control — lost packets vanish, the sender does not slow down, and the receiver gets a stream with gaps. TCP throttles its sending rate on loss, which sounds like a downside but actually limits the damage a lossy link does to the rest of your transfer. On a link running 5% packet loss, a UDP flow can deliver less useful data per second than a TCP flow that has already adapted its window. The speed advantage holds on clean LANs. It stops holding once you are on a variable-latency link full of middle boxes, or once your UDP traffic triggers ISP rate-limiting because something else on the same path was abusing UDP.

The comparison also depends on what you are measuring. UDP latency is lower because there is no handshake and no acknowledgment round trip before the first byte arrives — relevant for DNS queries and real-time gaming where one RTT is a noticeable fraction of the interaction. UDP throughput can be higher in controlled environments because the sender never voluntarily slows down. But if you need reliability and build it on top of UDP, you are now tracking sequence numbers, processing acknowledgments, and handling retransmission. At that point you have reimplemented TCP and lost whatever speed lead you had.

”TCP is for files, UDP is for video”

This rule is a relic of how video delivery evolved, not a technical constraint. Netflix, YouTube, and most streaming platforms deliver video over TCP. The reasoning is not complicated: a buffered stream already absorbs temporary network variability, a dropped frame in the past is gone no matter what the transport does, and TCP infrastructure is everywhere and well-understood. YouTube uses DASH — Dynamic Adaptive Streaming over HTTP — which breaks video into small segments delivered over regular HTTPS, which is TCP-backed. The occasional rebuffering users experience is almost never the transport misbehaving; it is the adaptive bitrate algorithm picking a quality level the connection cannot sustain right now.

Real-time video calls are different. Frames need to arrive within tens of milliseconds to feel responsive — waiting for a retransmit is worse than a lower-resolution frame. That is where UDP enters. But calling Zoom or Google Meet “UDP applications” is reductive. WebRTC, for example, runs media over SRTP on top of UDP and also specifies NACK (negative acknowledgment) for missing packets, forward error correction (FEC) to repair small losses without a retransmit, and jitter buffering to reorder and delimit the stream before it hits the decoder. RFC 8834 spells this out. The reliability layer is not optional add-on software — it is part of the protocol stack. UDP is the transport baseline WebRTC inherits, not the whole story.

”You always choose between TCP and UDP”

Most of the time the choice is made for you by the application protocol you are using. HTTP/1.1 and HTTP/2 are TCP by design — the browser owns the connection, and HTTP semantics assume reliable in-order delivery. gRPC runs over HTTP/2, which means TCP. AMQP, SMTP, SSH, IMAP — all TCP. The list is long enough that most developers never explicitly choose.

DNS is the exception worth knowing. A standard query goes out over UDP because one request-response fits in a single packet and a TCP handshake would double the lookup time. When a response exceeds the traditional 512-byte UDP limit (extended via EDNS), the server sets a truncate flag and the client reconnects over TCP. The DNS protocol itself decides when to switch based on response size — the application developer does not make that call.

WebRTC is the most layered case. Real-time media travels over SRTP on top of UDP. The call setup — session descriptions, ICE candidates, peer coordinates — runs over WebSocket with TLS, which is TCP. One session, two transports. The protocol stack assigns each role its appropriate transport, not a single global choice. This matters when you are debugging a failed WebRTC connection or designing your own protocol: the transport decision should follow from what your data actually needs to guarantee.


Connecting It All Together

The layers build on each other:

graph TB
    A[Your Application] --> B[HTTP over TCP]
    B --> C[TCP over IP]
    C --> D[IP over Ethernet]
    D --> E[Physical Network]

Each layer encapsulates the one below it. Your HTTP request becomes a TCP segment, then an IP packet, then an Ethernet frame.


QUIC Protocol

QUIC (RFC 9000) runs over UDP and layers TCP’s reliability on top of UDP’s speed. Google built it to get around TCP’s worst inefficiencies, and it’s now the transport behind HTTP/3.

Why QUIC Exists

TCP has three annoying problems QUIC actually solves:

  1. Head-of-line blocking: Lose one TCP packet and everything behind it waits—even if other streams could use the bandwidth
  2. Handshake latency: TCP needs 1 RTT before you even start TLS, then TLS needs another 1-2 RTTs
  3. Congestion control rigidity: TCP’s algorithms live in the OS kernel—you can’t ship a new one without an OS update

QUIC Handshake vs TCP+TLS

sequenceDiagram
    participant Client
    participant Server
    Note over Client,Server: TCP+TLS (TLS 1.3 takes ~1.5 RTT before data)
    Client->>Server: TCP SYN
    Server->>Client: SYN-ACK
    Client->>Server: TCP ACK
    Note over Client,Server: TLS Handshake starts...
    Client->>Server: ClientHello
    Server->>Client: ServerHello
    Client->>Server: Finished
    Note over Client,Server: Data ready at ~1.5 RTT

    Note over Client,Server: QUIC (1 RTT, crypto integrated into transport)
    Client->>Server: QUIC Initial (crypto handshake)
    Server->>Client: QUIC Initial (crypto + data)
    Client->>Server: QUIC Handshake Finished
    Note over Client,Server: Data ready at ~1 RTT

QUIC folds the crypto handshake into the transport layer. That first packet from the client already carries encrypted application data—no more waiting for separate TLS round trips.

QUIC Multi-Stream Advantage

HTTP/1.1 and HTTP/2 both multiplex multiple streams over a single TCP connection. That works until one packet drops—then TCP holds everything until that packet gets retransmitted. QUIC gives each stream its own stream ID:

graph TB
    subgraph "TCP (HTTP/2)"
        A[Stream 1]:::blocked
        B[Stream 2]:::blocked
        C[Stream 3]:::blocked
    end
    subgraph "QUIC (HTTP/3)"
        D[Stream 1]
        E[Stream 2]
        F[Stream 3]
    end
    D --> E --> F

When a QUIC packet drops, only the stream that owns that packet stalls. Every other stream keeps running.

0-RTT Resumption

If you’ve connected to a server before, QUIC can skip the handshake entirely:

  1. Client attaches Early Data with a session ticket from the previous visit
  2. Server decrypts and accepts the data immediately—no waiting
  3. You start sending application data in the very first packet

QUIC vs TCP Comparison

Comparison Table

FeatureTCP+TLS 1.3QUIC (RFC 9000)
Handshake RTT1-2 RTTs1 RTT (0-RTT on resumption)
Head-of-line blockingYes (TCP)No (stream-level)
Connection migrationBreaks—IP change kills itSurvives address change
Protocol evolutionKernel-level (slow)Userspace (ship anytime)
EncryptionTLS (application)Built into transport
Flow controlPer-connectionPer-stream

When to Choose QUIC

  • Mobile clients switching between WiFi and cellular (connection sticks around)
  • Applications that care about latency (HTTP/3, WebRTC data channels)
  • High packet loss environments (stream isolation prevents a single loss from cascading)
  • If you’re deploying HTTP/3 via CDN, QUIC comes with it automatically

TCP Header Deep Dive

The TCP header has a minimum 20 bytes but can expand with options:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          Source Port          |       Destination Port        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                        Sequence Number                        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Acknowledgment Number                      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Offset | Flags  | Window Size                                  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|         Checksum              |         Urgent Pointer          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Options (variable length)                   |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Key Fields

Sequence Number: Byte position of the first data octet in this segment. Enables in-order reconstruction and retransmission identification.

Acknowledgment Number: Next expected sequence number. All data up to this number has been received.

Flags: Nine control bits including SYN (connection establishment), ACK (acknowledgment valid), FIN (graceful termination), RST (abort), PSH (push data to application).

Window Size: How many bytes the receiver accepts. The 16-bit field gets multiplied by the window scaling factor (up to 2^14x) for high-BDP links—critical for 10 Gbps networks with 100ms RTT.

TCP Options (Partial List)

KindLengthNamePurpose
01EOLEnd of option list
11NOPPadding
24MSSMax segment size
33Window ScalingShift count (up to 2^14)
810TimestampsRTTM and PAWS
42SACK PermittedSelective acknowledgment allowed
5VariableSACK BlockSelective ACK ranges (if negotiated)

Window Scaling (RFC 7323)

On high-bandwidth, high-delay networks, a 65KB window is insufficient:

Link: 10 Gbps, 100ms RTT
Required window: 10 Gbps × 0.1s = 1 GB
Max window with scaling: 65535 × 2^14 = 1 GB

The window scaling option shifts the 16-bit window left by the scale factor (0-14).


NAT Traversal Guide

Network Address Translation (NAT) enables multiple devices to share one public IP. But NAT breaks end-to-end connectivity, which matters for UDP-based protocols like QUIC and WebRTC.

How NAT Works

Private Network (192.168.1.x)          NAT Device           Internet
                                      203.0.113.5 (public)
+----------+                          +-----------+          +--------+
| Client   | 192.168.1.100:5000  -->   | PAT entry |  -->     | Server |
|          | <--   203.0.113.5:5001    | (port map)|  <--    |        |
+----------+                          +-----------+          +--------+

The NAT device tracks source IP, source port, destination IP, destination port. When the response returns, it reverses the mapping.

NAT Types and Traversal Difficulty

NAT TypeBehaviorTraversal Difficulty
Full ConeAny external host can connectEasiest
RestrictedOnly contacted external hostsModerate
Port RestrictedOnly contacted ext host+portHard
SymmetricDifferent mapping per destinationHardest

STUN and TURN

STUN (RFC 8489): Lets clients discover their public IP:port mappings. Works with Full Cone and some Restricted NATs.

// STUN request (simplified)
const stunServer = "stun:stun.l.google.com:19302";
// Client sends binding request
// Server responds with mapped address

TURN (RFC 8656): Relay server for symmetric NATs. All traffic routes through the TURN server—higher latency but always works.

sequenceDiagram
    participant Client
    participant TURN
    participant Target
    Client->>TURN: Allocate request
    TURN->>Client: XOR-Mapped-Address
    Client->>TURN: Send to Target
    TURN->>Target: Data from Client's public IP
    Target->>TURN: Response
    TURN->>Client: Data

NAT Keepalive

NAT mappings expire. Servers send keepalive packets to maintain sessions:

# TCP keepalive (Linux)
echo 60 > /proc/sys/net/ipv4/tcp_keepalive_time     # Start after 60s idle
echo 10 > /proc/sys/net/ipv4/tcp_keepalive_intvl   # Send every 10s
echo 3 > /proc/sys/net/ipv4/tcp_keepalive_probes   # Drop after 3 failures

# Application-level UDP keepalive for QUIC
quic_conn.sendHeartbeat()  # Every 30 seconds typically

QUIC Connection Migration

QUIC handles NAT issues elegantly via connection migration—when your mobile switches from WiFi to cellular, QUIC can continue on the new path using the same connection ID:

// QUIC connection migration (conceptual)
if (networkChanged) {
  const newPath = { address: newIP, port: newPort };
  quicConn.migrate(newPath); // Same connection ID, new path
}

TCP Performance Tuning

Stock Linux settings often limit performance on modern networks. Tuning can dramatically improve throughput.

Socket Buffer Sizes

Default buffers are too small for high-BDP networks:

# View current TCP buffer settings
# min / default / max (bytes)
cat /proc/sys/net/ipv4/tcp_rmem   # Receive buffer
cat /proc/sys/net/ipv4/tcp_wmem   # Send buffer

# Example output:
# 4096    16384   6291456

# Set autotuning to higher maximums
echo "6291456 25165824 134217728" > /proc/sys/net/ipv4/tcp_rmem
echo "3145728 12582912 67108864" > /proc/sys/net/ipv4/tcp_wmem

TCP Congestion Control

Linux supports multiple congestion control algorithms:

# List available algorithms
sysctl net.ipv4.tcp_available_congestion_control

# Example output: reno cubic bbr
AlgorithmBest ForCharacteristics
cubicGeneral purpose (default)Fixed targeting, Reno replacement
bbrHigh-BDP, variable networksMaximizes throughput, not fair to Reno
renoLegacy, simple networksAvoids cwnd halving on partial losses
vegasLow-latency preferenceProactive queue detection

BBR (Bottleneck Bandwidth and RTT)

BBR models the network instead of reacting to loss:

// BBR conceptually targets bottleneck bandwidth + RTT
const bbrState = {
  bw: 0, // Bottleneck bandwidth
  rtprop: Infinity, // Round trip propagation time
  pacing_rate: bw * 1.25,
  cwnd: 12 * 1500, // In bytes (12 packets)
};

TIME_WAIT and Port Reuse

Connections in TIME_WAIT linger for 2×MSL (60 seconds on Linux by default). High-traffic servers can run out of source ports:

# Enable TIME_WAIT reuse (reuse, don't recycle)
echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse

# Reduce MSL to speed up cleanup (not RFC-compliant)
echo 30 > /proc/sys/net/ipv4/tcp_fin_timeout

# Increase ephemeral port range
echo "32768 61000" > /proc/sys/net/ipv4/ip_local_port_range

Keepalive Tuning

Keepalives detect dead connections but waste bandwidth:

# Application-agnostic keepalive (system-wide)
net.ipv4.tcp_keepalive_time = 7200      # Default: 7200s (2 hours!)
net.ipv4.tcp_keepalive_intvl = 75
net.ipv4.tcp_keepalive_probes = 9

# Better: Application-level with TCP keepalive options
socket.keepAlive(true, 30, 10);  // enable, idle 30s, interval 10s

Quick Recap Checklist

Before diving into implementation, ensure you understand:

  • TCP header fields and their purposes (sequence, ACK, window, flags)
  • Window scaling for high-BDP networks (2^14 multiplier)
  • TCP options: MSS, SACK, timestamps, window scaling
  • NAT types and traversal techniques (STUN/TURN/ICE)
  • QUIC connection migration for mobile networks
  • BBR vs cubic congestion control characteristics
  • TIME_WAIT recycling and port exhaustion prevention
  • Keepalive tuning for production systems

Trade-off Analysis

FactorTCPUDPQUICRecommendation
ReliabilityOrdered deliveryNoneOrdered deliveryOrdered delivery required
Overhead20+ bytes8 bytes20+ bytesLow overhead preferred
SpeedModerateFastestFaster than TCPLatency-sensitive
Congestion ControlBuilt-inNoneBuilt-inStreaming/bulk transfer
ConnectionStateful (3-way)StatelessStateless (0-RTT)Connectionless preferred
TLSSeparate handshakeN/ABuilt-inSecurity + speed
NAT TraversalFull cone issuesSTUN/TURNBetter via IDP2P/multiplayer

When to Use Each Protocol

  • TCP: Web browsing, email, file transfers, any bulk/reliable data
  • UDP: VoIP, video streaming, gaming, DNS, IoT
  • QUIC/HTTP/3: Modern web apps prioritising low latency

Production Failure Scenarios

FailureImpactMitigation
TCP connection timeoutRequests hang; poor user experienceSet appropriate connection timeouts; implement retry logic
UDP packet lossMissing data; application-level failuresImplement application-level acknowledgment and retransmission
Port exhaustionCannot establish new connections; service unavailableMonitor connection counts; implement connection pooling; increase port range
SYN flood attackServer overwhelmed with half-open connectionsUse SYN cookies; implement rate limiting; use DDoS protection
NAT timeoutLong-lived connections break; clients appear disconnectedSend keepalive packets; use connection-oriented protocols when possible
MTU mismatchPackets dropped; connectivity issuesUse Path MTU Discovery; set conservative MTU values
TCP congestion collapseNetwork throughput drops dramaticallyUse proper congestion control algorithms; implement traffic shaping
UDP amplification attackYour servers used to attack othersValidate source addresses; restrict UDP responses; implement rate limiting

Observability Checklist

A transport-level issue is often the first thing that gets blamed when an application slows down, but it is also one of the last things teams have real visibility into. Application metrics show request duration and error rates. Network metrics show link saturation and packet loss. The actual TCP retransmissions, RST counts, and socket buffer pressure sit at a layer that most monitoring setups skim over, even though they often explain the gap between “the app is slow” and “the network is fine.”

What makes transport observability unique is that the signals are per-connection and per-flow, not per-host. A server can have healthy CPU and memory and still be bleeding throughput because of a single misbehaving peer, a stretched RTT to a far region, or a NAT entry expiring under a long-lived connection. Per-host counters hide these. Per-connection counters show them, but at the cost of cardinality that does not scale to a million open sockets. The trick is knowing which aggregate metrics to keep (retransmit rate, RST rate, TIME_WAIT count) and which to drill into only when you have a specific connection in hand.

The checklist below groups the signals worth collecting into metrics, logs, and alerts. Use the metrics for trend analysis and capacity planning, the logs to root-cause individual incidents, and the alerts to catch regressions before users do. A framing note: connection-level counters in Linux live under /proc/net/netstat and /proc/net/snmp, while per-socket state sits in /proc/net/tcp and /proc/net/udp. Most modern exporters (node_exporter, telegraf, the OpenTelemetry host metrics receiver) already surface the headline numbers. The hard part is usually thresholds, not collection. Defaults are tuned for a LAN, so production thresholds need to account for your real RTT distribution and traffic mix.

Metrics

  • TCP connection rate (new connections per second)
  • Active TCP connections (concurrent connections)
  • TCP connection failures (connection refused, timeout)
  • Segment retransmission rate
  • TCP buffer utilization (bytes in send/receive buffers)
  • UDP packet rate (packets sent/received per second)
  • UDP error rate (checksum failures, buffer overflows)
  • Round-trip time (RTT) for TCP connections
  • Throughput (bytes sent/received per second)

Logs

  • Connection failures with source IP and port
  • TCP reset packets (RST) received
  • UDP packet checksum failures
  • Connection timeouts
  • Port exhaustion warnings
  • Network interface errors

Alerts

  • Connection failure rate exceeds 5%
  • Retransmission rate exceeds 10%
  • Active connections approach limits
  • UDP error rate increases
  • TCP RST rate spikes (potential attack)
  • Network latency anomalies

Security Checklist

  • Use TLS over TCP when encryption is needed (not raw TCP)
  • Implement connection timeouts to prevent resource exhaustion
  • Monitor for SYN flood attacks; enable SYN cookies
  • Use firewall rules to restrict exposed ports
  • Implement rate limiting on TCP/UDP services
  • Validate UDP source addresses to prevent spoofing
  • Use IPsec for network-level encryption when needed
  • Monitor for unusual traffic patterns indicating attacks
  • Implement connection tracking for stateful firewall rules
  • Restrict broadcast and multicast traffic where not needed

Common Pitfalls / Anti-Patterns

Assuming UDP is Always Faster

UDP avoids TCP overhead but does not guarantee delivery or ordering.

// Problem: UDP with no reliability
const socket = dgram.createSocket("udp4");
socket.send(data, port, host, (err) => {
  // No confirmation data arrived - you simply do not know
});

// Better: Implement acknowledgment
socket.send(data, port, host);
socket.on("message", (msg) => {
  if (msg.toString() === "ACK") {
    // Confirmed delivery
  }
});

Ignoring TCP Connection Limits

Each TCP connection consumes file descriptors and memory.

# Check current connection limits
cat /proc/sys/net/core/somaxconn        # Max pending connections
cat /proc/sys/fs/file-max               # System-wide file descriptors

# Monitor active connections
ss -s

Not Handling Connection Termination Properly

Abruptly closing connections can cause data loss.

// Graceful close - ensure data is sent
socket.end(); // Send FIN after remaining data is sent
socket.on("close", () => {
  // Connection fully closed
});

Building Custom Reliability on UDP When TCP Would Work

If you need reliable, ordered delivery, just use TCP.

// Problem: Building reliability on UDP
socket.on("message", (data) => {
  // Must implement sequence numbers, acknowledgments, retransmission
  // This is essentially reimplementing TCP
});

// Better: Just use TCP
const server = net.createServer((socket) => {
  // Reliability built in
});

Interview Questions

1. Explain the TCP three-way handshake process. Why is it necessary?

Expected answer points:

  • Client sends SYN with sequence number x
  • Server responds with SYN-ACK (seq=y, ack=x+1)
  • Client sends ACK (ack=y+1), connection established
  • Necessary to synchronize sequence numbers and confirm both parties can send/receive
2. What is the difference between flow control and congestion control in TCP?

Expected answer points:

  • Flow control prevents overwhelming the receiver (advertising window size)
  • Congestion control prevents overwhelming the network (slow start, avoidance, fast retransmit)
  • Flow control is about the receiver's buffer; congestion control is about network capacity
3. Why is UDP considered faster than TCP? When would you choose UDP over TCP?

Expected answer points:

  • No connection establishment (zero RTT overhead)
  • No acknowledgments, retransmissions, or ordering overhead
  • Smaller header (8 bytes vs 20+ bytes)
  • Choose UDP for real-time apps (video, voice, gaming) where speed > reliability
  • Choose UDP when application-level error handling is sufficient
4. What is head-of-line blocking and how does QUIC solve it?

Expected answer points:

  • TCP HOL blocking: losing one packet blocks all subsequent packets until retransmission
  • QUIC gives each stream its own stream ID and sequence space
  • When a QUIC packet is lost, only that stream stalls—other streams continue
5. Explain the TCP state machine. What states does a connection go through?

Expected answer points:

  • States: CLOSED, LISTEN, SYN_SENT, SYN_RECEIVED, ESTABLISHED, FIN_WAIT_1, FIN_WAIT_2, CLOSE_WAIT, CLOSING, LAST_ACK, TIME_WAIT
  • Server: CLOSED → LISTEN → SYN_RECEIVED → ESTABLISHED → FIN_WAIT_1 → CLOSE_WAIT → LAST_ACK → CLOSED
  • Client: CLOSED → SYN_SENT → ESTABLISHED → FIN_WAIT_1 → FIN_WAIT_2 → CLOSED
6. What is TIME_WAIT state and why does it exist? How can you mitigate port exhaustion?

Expected answer points:

  • TIME_WAIT lasts 2×MSL (60 seconds on Linux) after graceful close
  • Ensures delayed packets from old connections are discarded
  • Allows proper termination acknowledgment processing
  • Mitigation: tcp_tw_reuse, tcp_fin_timeout, larger ephemeral port range
7. How does TCP congestion control work? Name the phases and algorithms.

Expected answer points:

  • Slow Start: cwnd starts small, exponentially increases until loss
  • Congestion Avoidance: linear increase after threshold reached
  • Fast Retransmit: on 3 duplicate ACKs, retransmit without waiting for timeout
  • Fast Recovery: after retransmit, try to continue without full slow start
  • Algorithms: CUBIC (default), BBR (throughput-focused), Reno, Vegas
8. What is the difference between SACK (Selective Acknowledgment) and cumulative ACK?

Expected answer points:

  • Cumulative ACK: acknowledges only the last in-order byte received
  • SACK: allows receiver to acknowledge non-contiguous blocks of data
  • SACK reduces unnecessary retransmissions when multiple segments are lost
  • SACK must be negotiated during TCP handshake (option kind 4 and 5)
9. Explain NAT traversal techniques. How do STUN and TURN work?

Expected answer points:

  • NAT maps private IPs/ports to public ones for multiple devices sharing one public IP
  • STUN: lets clients discover their public mapping; works with full cone and some restricted NATs
  • TURN: relay server for symmetric NATs; all traffic routes through TURN server
  • ICE: combines STUN/TURN for optimal path selection
10. What is window scaling in TCP and when is it necessary?

Expected answer points:

  • TCP window field is 16 bits, max 65535 bytes—insufficient for high-BDP links
  • Window scaling (RFC 7323) shifts window left by scale factor (0-14)
  • Enables windows up to 65535 × 2^14 = 1 GB
  • Necessary for 10 Gbps+ links with 100ms+ RTT
  • Scale factor negotiated during handshake via TCP option
11. How does the TCP congestion control algorithm work? Name and compare the phases and modern algorithms.

Expected answer points:

  • Slow Start: cwnd starts small (1-4 MSS), doubles each RTT until ssthresh is reached
  • Congestion Avoidance: cwnd increases linearly (1 MSS per RTT) after ssthresh
  • Fast Retransmit: 3 duplicate ACKs trigger immediate retransmission without waiting for timeout
  • Fast Recovery: after retransmit, cwnd is partially inflated to continue without full slow start
  • CUBIC (Linux default): uses cubic function for window growth, RTT-independent, aggressive on high-BDP links
  • BBR (Google): models bandwidth and RTT to find optimal throughput, outperforms CUBIC on lossy networks
12. What is the difference between SACK (Selective Acknowledgment) and cumulative ACK in TCP?

Expected answer points:

  • Cumulative ACK: acknowledges all bytes up to a sequence number; cannot recover multiple lost segments in one RTT
  • SACK: receiver explicitly lists non-contiguous blocks received correctly (RFC 2018)
  • SACK enables sender to retransmit only specific missing segments, reducing unnecessary retransmissions
  • SACK is negotiated at connection setup via the SACK-permitted TCP option
  • Particularly valuable on high-latency satellite and intercontinental links
13. Explain NAT traversal techniques for UDP. How do STUN, TURN, and ICE work together?

Expected answer points:

  • STUN (Session Traversal Utilities for NAT): external server reveals the public IP:port mapping; works for open and cone NATs but fails on symmetric NATs
  • TURN (Traversal Using Relays around NAT): relay server forwards traffic when direct P2P fails; adds latency and server cost
  • ICE (Interactive Connectivity Establishment): tries all candidate pairs (host → STUN → TURN) in priority order and picks the best working path
  • WebRTC uses this stack for peer-to-peer audio/video connections
  • STUN discovers addresses, TURN provides fallback, ICE orchestrates selection
14. How does QUIC (HTTP/3) solve TCP's head-of-line blocking problem?

Expected answer points:

  • TCP HOL blocking: a lost segment blocks all subsequent segments until retransmission, even if they belong to different HTTP/2 streams
  • QUIC multiplexes streams independently — each stream has its own sequence space; loss only blocks that stream
  • QUIC also integrates TLS handshake into connection establishment (0-RTT or 1-RTT vs TCP+TLS 2-3 RTTs)
  • Connection migration: QUIC uses connection IDs so sessions survive IP address changes (mobile network handover); TCP would need a new connection
15. Give concrete use cases where UDP is strictly better than TCP.

Expected answer points:

  • Real-time media (VoIP, video conferencing): late packets are useless; TCP retransmission adds unacceptable delay
  • Online gaming: 100ms latency tolerance; occasional dropped frame is preferable to high jitter from retransmission
  • DNS queries: small request/response fits in one packet; UDP avoids TCP handshake overhead
  • IoT/sensor networks: devices send infrequent small packets; 8-byte UDP overhead vs 20+ bytes for TCP matters at scale
  • Broadcast/multicast: TCP is point-to-point only; UDP natively supports one-to-many delivery
16. What is TIME_WAIT state in TCP, why does it exist, and how do you mitigate port exhaustion?

Expected answer points:

  • After a connection closes, the endpoint lingers in TIME_WAIT for 2×MSL (typically 60 seconds on Linux)
  • Purpose: absorb delayed segments from old connections that might arrive during a new connection with the same 4-tuple
  • Port exhaustion: each TIME_WAIT socket holds a local port, limiting new connections on that port
  • Mitigations: tcp_tw_reuse (allow TIME_WAIT sockets to be reused); tcp_fin_timeout (reduce MSL); SO_LINGER with timeout=0 (abortive close); increase ephemeral port range (ip_local_port_range)
17. What is the TCP three-way handshake cost in terms of latency, and how does TCP Fast Open reduce it?

Expected answer points:

  • Classic handshake costs 1.5 RTTs before any application data can be sent (SYN → SYN-ACK → ACK)
  • TCP Fast Open (TFO, RFC 7413): on a previously connected client, the SYN packet can carry an TLS-equivalent cookie and the first HTTP request, reducing latency to 1 RTT
  • TFO requires both client and server to support it; the first connection still costs 1.5 RTTs to establish the TFO cookie
  • QUIC goes further: 0-RTT (no handshake) or 1-RTT (full handshake with 0-RTT data on reconnect)
18. How does connection pooling work at the transport layer? What are the trade-offs?

Expected answer points:

  • Maintain a pool of pre-established TCP connections to amortise the 1.5 RTT handshake cost across many requests
  • Database clients (PostgreSQL, Redis), HTTP/1.1 clients, and gRPC use connection pools
  • Trade-offs: memory overhead for idle connections; risk of stale connections closed by NAT or load balancer; pool sizing is critical (too small = contention, too large = resource waste)
  • HTTP/2 multiplexing reduces per-host connection count, but connection pools remain essential for non-HTTP protocols
19. What are the implications of UDP fragmentation and why does Path MTU Discovery matter?

Expected answer points:

  • When a UDP datagram exceeds the path MTU, the sender must fragment it; only the destination reassembles
  • Any lost fragment invalidates the entire datagram (all-or-nothing); makes UDP worse on high-loss links
  • Path MTU Discovery (PMTUD): sets the DF (Don't Fragment) bit; ICMP "packet too big" informs sender of max MTU
  • Some networks block all ICMP, silently breaking PMTUD (ICMP black hole); fallback to a conservative MTU (576 bytes) is often needed
  • TCP handles this transparently: it probes the path and adapts MSS to fit
20. Compare TCP and UDP from a security perspective. Which is harder to secure and why?

Expected answer points:

  • TCP is harder to spoof: sequence numbers and ACK tracking make random packet injection difficult without an existing connection
  • UDP is easy to spoof; stateless nature makes it trivial to forge source addresses
  • QUIC combines UDP simplicity with built-in TLS 1.3 encryption, achieving TCP+TLS security with less overhead
  • DDoS amplification is more practical with UDP (DNS, NTP, QUIC) because request/response ratios heavily favour the attacker
  • Both benefit from IPsec (network-layer encryption); application-layer TLS can be used on either protocol

Further Reading

TCP and UDP serve different needs. TCP provides reliability, ordering, and flow control at the cost of latency. UDP provides speed and simplicity at the cost of reliability guarantees.

For application-layer protocols, see the HTTP/HTTPS post. For DNS specifically, the DNS & Domain Management post covers name resolution in detail.


Conclusion

Key Bullets

  • TCP provides reliable, ordered, connection-oriented delivery with flow and congestion control
  • UDP provides fast, unreliable, connectionless delivery without overhead
  • TCP three-way handshake establishes connections; UDP sends immediately
  • TCP uses acknowledgments, sequence numbers, and retransmission for reliability
  • UDP header is 8 bytes; TCP header is 20+ bytes minimum
  • Choose TCP when correctness matters; choose UDP when speed and low latency matter more
  • Common TCP ports: 80 (HTTP), 443 (HTTPS), 22 (SSH), 25 (SMTP), 53 (DNS)
  • DNS uses UDP port 53 for queries, TCP for zone transfers and large responses

Copy/Paste Checklist

# Check TCP connection states
ss -tunapl | grep -E "(State|Recv-Q|Send-Q)"

# Monitor TCP metrics
cat /proc/net/tcp
cat /proc/net/tcp6

# Check UDP statistics
cat /proc/net/udp
cat /proc/net/udp6

# Test TCP connection with netcat
nc -zv host.example.com 443

# Test UDP connectivity (limited)
nc -zvu host.example.com 53

# Check for open ports
ss -tunapl | grep LISTEN

# View TCP window sizes
cat /proc/sys/net/ipv4/tcp_rmem
cat /proc/sys/net/ipv4/tcp_wmem

# Test MTU
ping -M do -s 1472 example.com

Category

Related Posts

HTTP and HTTPS Protocol: A Complete Guide to Web Communication

Deep dive into HTTP methods, status codes, headers, keep-alive, and protocol evolution. Understand HTTP/1.1, HTTP/2, and HTTP/3 differences.

#networking #http #https

Cloud Security: IAM, Network Isolation, and Encryption

Implement defense-in-depth security for cloud infrastructure—identity and access management, network isolation, encryption, and security monitoring.

#cloud #security #iam

Docker Networking: From Bridge to Overlay

Master Docker's networking models—bridge, host, overlay, and macvlan—for connecting containers across hosts and distributed applications.

#docker #networking #containers