TCP/IP is the foundational protocol suite of the internet. IP (Internet Protocol) operates at the Internet layer and handles addressing and routing between networks. TCP (Transmission Control Protocol) operates at the Transport layer on top of IP and provides reliable, ordered, connection-oriented byte-stream delivery. They are distinct layers: the IP sections below explain addressing, NAT, and IPv4/IPv6 operation; the TCP sections explain one transport that runs over IP. Application protocols such as HTTP, gRPC, and database protocols then depend on that transport.

Understanding TCP/IP matters for debugging latency, connection issues, and designing systems that handle network failures correctly.

The TCP/IP Stack

Application Layer   HTTP, gRPC, WebSocket, SMTP, DNS
Transport Layer     TCP (reliable) / UDP (unreliable)
Internet Layer      IP (addressing + routing)
Link Layer          Ethernet, Wi-Fi (physical transmission)

Each layer adds a header and passes the packet down. On the receiving end, each layer strips its header and passes the payload up.

IP Addressing and NAT

The “IP” half of TCP/IP is addressing. An IP address identifies an interface; a port (16-bit, 0–65535) selects a transport endpoint on that machine. TCP identifies a connection by the 4-tuple (src IP, src port, dst IP, dst port) within its protocol namespace.

  • IPv4 — 32-bit addresses (~4.3 billion), written 192.168.1.10. Exhausted, which is why NAT exists.
  • IPv6 — 128-bit addresses, written 2001:db8::1. Its address space removes address-conservation as a reason for NAT, but does not remove firewall policy or every translation-based transition mechanism.
  • Private ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) are non-routable on the public internet.
  • Transport ports split into well-known (0–1023, e.g. 80/443), registered, and ephemeral client-side ports. A client can exhaust usable source tuples when many recently closed TCP connections target the same remote endpoint.

NAPT, the common many-to-one form of NAT, rewrites both an address and a port. If 10.0.0.7:53000 sends to 203.0.113.20:443, a gateway might record this state:

Inside tuplePublic mappingRemote tupleState
10.0.0.7:53000198.51.100.8:41001203.0.113.20:443TCP established, idle 18 s

The server replies to 198.51.100.8:41001; the gateway uses the mapping to demultiplex the packet back to 10.0.0.7:53000. Mappings expire, and different devices use different endpoint filtering and timeout behavior. Unsolicited inbound traffic has no mapping, so it needs a configured port forward, a rendezvous-assisted hole punch, or a relay. Carrier-grade NAT adds another shared translation layer and makes inbound reachability and per-subscriber attribution harder.

NAT is translation, not firewall policy. A stateful firewall decides which packets may pass; a translator rewrites packet fields and maintains mappings. They are often implemented on the same gateway, which is why they are easy to confuse. IPv6 restores end-to-end addressing, but an IPv6 firewall should still deny unwanted inbound traffic.

The NAT diagram from the reviewed source is intentionally absent: it links an unrelated HTTP-header image.

IP Layer: IPv4 and IPv6

ConcernIPv4IPv6Operational consequence
Address and notation32-bit dotted decimal, for example 192.0.2.10128-bit hexadecimal; consecutive zero groups can be compressed once, for example 2001:db8::10Logs, ACLs, parsers, and metrics must handle both forms without truncation
Base headerVariable 20–60 bytes; includes a header checksumFixed 40 bytes; no header checksum; optional information uses extension headersIPv6 removes per-hop checksum recomputation but extension-header handling still needs testing
FragmentationA source or router may fragment unless prohibitedOnly the source fragments; routers return ICMPv6 Packet Too BigBroken PMTU feedback causes large-flow stalls even when small probes pass
Local neighbor lookupARP maps IPv4 addresses to link-layer addressesNeighbor Discovery uses ICMPv6 and scoped multicastIPv6 does not use ARP or broadcast
Address conservationPrivate addressing plus NAPT is commonGlobally unique addressing is practicalIPv6 removes the conservation need for NAPT, not the need for traffic filtering

Do not declare dual stack universally best. It gives native reachability during migration, but it doubles policy, observability, DNS, and failure surfaces: an AAAA record can send clients down a broken IPv6 path while IPv4 remains healthy. Use dual stack when both paths are operated and tested. IPv6-only with DNS64/NAT64 can be simpler inside controlled client networks; IPv4-only remains a compatibility constraint, not an end state.

TCP Connection: Three-Way Handshake

Before data flows, TCP establishes a connection with a three-way handshake:

Client → Server: SYN  (seq=100)
Server → Client: SYN-ACK  (seq=200, ack=101)
Client → Server: ACK  (ack=201)
── Connection established ──
Client → Server: DATA
  • SYN: client proposes a starting sequence number.
  • SYN-ACK: server acknowledges and proposes its own sequence number.
  • ACK: client acknowledges the server’s sequence number.

This handshake adds one round-trip before an ordinary TCP connection can carry application data. Connection reuse and HTTP/2 multiplexing amortize that cost. QUIC combines transport security and connection establishment; a new connection still takes a round trip, while a previously authenticated session may send replayable 0-RTT data.

Reliability Mechanisms

TCP detects loss and preserves an ordered byte stream while the connection remains viable through:

  • Sequence numbers: every byte is numbered. The receiver reorders out-of-order segments.
  • Acknowledgments (ACK): the receiver acknowledges received bytes. Unacknowledged segments are retransmitted.
  • Retransmission timeout (RTO): if no ACK arrives within the timeout, the segment is retransmitted.
  • Duplicate ACKs / Fast Retransmit: three duplicate ACKs signal a lost segment; TCP retransmits without waiting for the timeout.

WARNING

Head-of-line (HOL) blocking is the price of in-order delivery: if segment #5 is lost, segments #6–#10 sit in the receive buffer and cannot be delivered to the application until #5 is retransmitted — even though they arrived fine. This is exactly why HTTP/2’s many streams over one TCP connection can stall together on a single lost packet, and why QUIC/HTTP/3 moves multiplexing into independent UDP-based streams. See UDP.

MTU, MSS, and Keep-Alive

  • MTU (Maximum Transmission Unit) — the largest packet a link carries, typically 1500 bytes for an Ethernet IP payload. MSS (Maximum Segment Size) is the TCP payload that fits in one unfragmented packet (MTU minus IP+TCP headers, commonly ~1460 for IPv4 without options). IPv4 routers may fragment packets unless prohibited; IPv6 routers never do. Path MTU Discovery uses ICMP feedback to find the largest packet the path accepts. Filtering that feedback can create a black hole where small packets work and large transfers repeatedly time out.
  • TCP keep-alive sends periodic probes on an idle connection to detect a peer that vanished without a FIN (crash, cable pull). Without it, a half-open connection can sit “established” forever. Tune intervals (SO_KEEPALIVE) for long-lived connections behind NAT/load balancers, which silently drop idle flows.
  • Window scaling / bandwidth-delay product — on high-latency, high-bandwidth links (“long fat networks”), the default receive window caps throughput; the window-scaling option (and a window ≥ bandwidth × RTT) is needed to keep the pipe full.

Flow Control and Congestion Control

Flow control prevents the sender from overwhelming the receiver. The receiver advertises a receive window (how many bytes it can buffer). The sender cannot have more unacknowledged bytes in flight than the window size.

Congestion control prevents the sender from overwhelming the network. TCP starts with a small congestion window and grows it exponentially (slow start) until it detects packet loss, then backs off. Algorithms: CUBIC (Linux default), BBR (Google, latency-optimized).

Connection Teardown: Four-Way Handshake

Client → Server: FIN
Server → Client: ACK
Server → Client: FIN
Client → Server: ACK
── Connection closed ──

The actively closing endpoint enters TIME_WAIT long enough to prevent delayed packets from an old connection being mistaken for a new one. A client that repeatedly opens short-lived connections to the same destination can exhaust usable source tuples; connection pooling and bounded connection creation are the default remedies. SO_REUSEADDR controls listener bind/restart behavior under operating-system-specific rules. It does not expand the outbound ephemeral-port space.

Pitfalls

Nagle’s Algorithm Causing Latency

What goes wrong: small writes (e.g., sending a 10-byte command) are buffered by Nagle’s algorithm until the buffer fills or an ACK arrives. This adds 40–200ms latency for interactive protocols.

Why it happens: Nagle’s algorithm coalesces small packets to reduce network overhead. It’s enabled by default.

Mitigation: disable Nagle’s algorithm with TCP_NODELAY for latency-sensitive connections (database clients, real-time APIs). Most database drivers and HTTP clients do this automatically.

var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.NoDelay = true;  // disables Nagle's algorithm

TIME_WAIT Port Exhaustion

What goes wrong: a service opens and actively closes many short-lived outbound connections to the same destination. The OS cannot allocate another usable source tuple because too many recent connections remain in TIME_WAIT.

Why it happens: TCP retains recently closed connection identity so delayed segments cannot corrupt a later connection. The exact retention time and tuple-reuse rules are platform and kernel specific; NAT gateways can impose a separate translated-port limit.

Mitigation: reuse connections through HttpClient, database pools, or another protocol-aware pool, and bound connection churn. Confirm exhaustion with socket-state, source-port, and NAT telemetry before changing kernel behavior. Linux net.ipv4.tcp_tw_reuse semantics vary by kernel version and do not generalize to other platforms; treat it as a diagnosed expert action for a specific client workload, not a default mitigation. SO_REUSEADDR helps a listening server rebind under platform rules and does not solve outbound tuple exhaustion.

TCP vs UDP

TCPUDP
ConnectionConnection-oriented (handshake)Connectionless
ReliabilityRetransmits and orders bytes; reports failure if delivery cannot continueBest-effort, no ordering
OverheadHigher (headers, ACKs, retransmits)Lower
Latency behaviorA handshake and in-order retransmission can add delayNo transport handshake or retransmission, but application recovery, congestion control, and queueing can erase that advantage
Use casesHTTP, databases, file transferDNS, video streaming, gaming, QUIC

Decision rule: default to TCP when one reliable ordered byte stream fits the workload (web APIs, databases, file transfer), while still setting deadlines and handling connection failure. Use UDP when the application defines its own loss/recovery behavior, needs datagram boundaries or multicast, or builds a different transport such as QUIC.

Questions

References