Files
2026-08-12 16:34:22 -04:00

15 KiB

TCP vs. UDP: The Two Fundamental Methods of Sending Data Across the Internet

These are the two transport-layer protocols that sit directly above IP (Internet Protocol) in the network stack. They are the substrate — the physical/OS-level guarantees (or lack thereof) that every application protocol must build on.


1. TCP — Transmission Control Protocol

What it is: A connection-oriented, reliable, ordered byte stream between two computers.

How It Works

  1. Three-way handshake — Before any data flows, the sender and receiver exchange three packets:

    • SYN → (sender says "I want to talk")
    • SYN-ACK ← (receiver says "OK, I'm ready")
    • ACK → (sender says "Great, let's go")

    This creates state on both sides: sequence numbers, window sizes, buffers.

  2. Data transfer — Bytes are numbered with sequence numbers. The receiver sends ACK packets confirming receipt. If the sender doesn't get an ACK within a timeout, it retransmits.

  3. Flow control — The receiver tells the sender how much buffer space it has left. The sender slows down if the receiver is overwhelmed.

  4. Congestion control — TCP dynamically adjusts its sending rate based on inferred network congestion (packet loss = slow down, success = speed up).

  5. Connection teardown — A four-packet exchange (FIN, FIN-ACK, ACK) to close gracefully.

Key Properties

Property Description
Reliability Every byte arrives, in order, or the connection fails
Ordering Bytes arrive in the exact order sent
Connection-oriented Both sides maintain state
Stream-oriented No message boundaries — just a continuous stream of bytes
Detectable The SYN/SYN-ACK/ACK pattern is unmistakable to deep packet inspection

The Cost

  • Latency: One round trip before any data can be sent
  • State overhead: Both ends must maintain connection state
  • Head-of-line blocking: A lost packet stalls all subsequent data
  • Visible handshake: The SYN/SYN-ACK/ACK pattern can be fingerprinted and blocked

2. UDP — User Datagram Protocol

What it is: A connectionless, unreliable, message-oriented datagram service.

How It Works

  1. No handshake — The sender just puts bytes on the wire. No prior communication with the receiver. No state created on either side.

  2. Fire and forget — The sender calls sendto() and the OS fires the datagram out the network interface. There is no acknowledgment, no retransmission, no guarantee of delivery.

  3. Message boundaries preserved — Each sendto() call produces exactly one datagram. The receiver's recvfrom() returns exactly that datagram (or nothing). Messages are not split or merged.

  4. No flow control, no congestion control — The application is responsible for managing its own sending rate.

Key Properties

Property Description
Unreliability Packets may be lost, duplicated, or arrive out of order
No connection Zero state on either side
Message-oriented Datagram boundaries are preserved
Low latency Data can be sent immediately — zero round trips before the first byte
Hard to detect A single UDP datagram is just a blob of bytes — no handshake pattern

The Cost

No delivery guarantees. The application must handle loss, reordering, and duplication itself (if it cares).


The Critical Difference

Property TCP UDP
Handshake 3-way (SYN, SYN-ACK, ACK) None
State Connection state on both sides Stateless
Reliability Guaranteed delivery, in order Best effort, no guarantees
Message boundaries Stream (no boundaries) Datagram (boundaries preserved)
Latency before first byte 1 RTT minimum Zero
Detectable pattern Yes — unmistakable handshake No — indistinguishable from noise
Censorship resistance Low — handshake can be blocked High — no pattern to block
MTU awareness Fragments transparently Application must manage packet size

The MTU Boundary (Why 1472 Bytes Matters)

The Maximum Transmission Unit (MTU) for Ethernet is 1500 bytes. This is a physical/hardware constraint:

Ethernet frame:  1500 bytes  (hardware limit)
  - IP header:    20 bytes   (OS adds this)
  - UDP header:    8 bytes   (OS adds this)
  = UDP payload: 1472 bytes  (what your application can send)

If you send more than 1472 bytes over UDP, the IP layer fragments the datagram into multiple packets. The receiver's OS reassembles them before delivering to the application. But fragmentation:

  • Creates multiple packets that can be blocked individually
  • Makes the communication visible (fragmented packets have identifiable headers)
  • Fails if any fragment is lost (the entire datagram is lost)

If you keep your message under 1472 bytes, it fits in exactly one Ethernet frame. This is the single-packet guarantee — the message is atomic at the hardware level.


The No-Handshake Property (Why It Matters for Censorship)

Protocol Handshake Round trips before data Detectable pattern
TCP SYN, SYN-ACK, ACK 1 Yes — unmistakable
TLS ClientHello, ServerHello+Cert, Finished 2+ Yes — certificate, cipher suites
HTTP/3 (QUIC) QUIC Initial + Handshake 1 Partially — Initial packet has structure
WireGuard Handshake initiation + response 1 Minimal but recognizable
Nostr over UDP None 0 — fire and forget No pattern at all

A Nostr event is self-validating — it carries its own signature. The signature replaces the handshake. The receiver validates the event using only the pubkey in the event. No prior relationship, no shared secrets, no session setup.

This means:

  • No handshake pattern to fingerprint — a single UDP datagram is just bytes on the wire
  • No active probing vulnerability — the adversary cannot distinguish a Nostr relay from any other UDP service by sending a probe, because there's no handshake to respond to
  • No amplification attack vector — the response (if any) is smaller than the request
  • Stateless relays — no connection state to exhaust with SYN floods

The Great Dichotomy Applied

From TheGreatDichomety.md: things that happen inside computers vs. outside computers.

  • TCP is a complex inside-computer construct. It creates an abstraction (a reliable byte stream) that doesn't exist in the physical network. The OS kernel maintains connection state, sequence numbers, timers, retransmission buffers — all patterns of bits inside the computer. The handshake is a negotiation between two computers' internal state machines.

  • UDP is closer to the outside-computer reality. It maps almost directly to what the network hardware does: fire packets at the wire and hope they arrive. No state, no negotiation, no abstraction. Just bytes.

The Nostr-over-UDP approach exploits this: by eliminating the handshake (the inside-computer negotiation), the communication becomes indistinguishable from the raw physical substrate. The adversary cannot distinguish your message from any other random noise on the wire, because at the physical level, that's exactly what it is.


Summary Diagram

flowchart LR
    subgraph Application
        A[Nostr Event<br/>self-validating]
    end
    subgraph Transport
        B[UDP<br/>no handshake<br/>no state]
        C[TCP<br/>3-way handshake<br/>connection state]
    end
    subgraph Network
        D[IP<br/>packets]
    end
    subgraph Physical
        E[Ethernet frames<br/>MTU: 1500 bytes]
    end

    A --> B
    A --> C
    B --> D
    C --> D
    D --> E

    style B fill:#4a4,color:#fff
    style C fill:#a44,color:#fff

The green path (UDP) is the one explored in this project — it leaves no trace, requires no negotiation, and exploits the physical properties of the substrate (MTU boundaries, no handshake pattern) to resist censorship.


References


3. Raw IP — Can You Skip TCP and UDP Entirely?

Short answer: Yes, you can send data directly over IP using raw sockets (SOCK_RAW). But there are serious practical restrictions that make it nearly useless on the open internet.

How It Works

The IP header has an 8-bit protocol field that identifies what transport protocol is carrying the payload:

Protocol Number Name Common Use
1 ICMP ping, traceroute, error reporting
2 IGMP Multicast group management
6 TCP Web, email, file transfer
17 UDP DNS, video, gaming, Nostr
132 SCTP Telephony, signaling
33 DCCP Streaming, congestion-controlled UDP

You can put any value in this field. If you write a raw socket program that sets protocol field to, say, 249, your packets will be sent out onto the wire with that protocol number. A receiver with a matching raw socket can read them.

Raw Socket Code (Linux)

import socket

# SOCK_RAW lets you craft your own IP packets
# IPPROTO_RAW (255) means you supply the entire IP header yourself
sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)

# Or use a custom protocol number (e.g., 249)
sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, 249)

The Three Levels of Socket

Application
    |
    |--- SOCK_STREAM (TCP)  -- You give bytes, OS handles everything
    |--- SOCK_DGRAM  (UDP)  -- You give datagrams, OS adds UDP + IP headers
    |--- SOCK_RAW    (IP)   -- You supply IP header (or let OS add it partially)
    |
Network interface (Ethernet)

The Problems with Raw IP

1. Root privileges required

On Linux, creating a raw socket requires CAP_NET_RAW capability or root. Normal user programs cannot use SOCK_RAW. This alone makes it impractical for most applications.

2. Firewalls block non-standard protocols by default

Almost every firewall, NAT gateway, and router on the internet is configured to pass only TCP (protocol 6) and UDP (protocol 17). Everything else is dropped. Some also pass ICMP (protocol 1) for diagnostics.

If you send packets with protocol 249, they will be dropped by the first router that doesn't recognize the protocol. Your packets never reach the destination.

3. NAT requires TCP or UDP

Network Address Translation (NAT) — used by virtually every home router and corporate firewall — works by tracking ports. TCP and UDP have 16-bit port numbers (0-65535) that NAT uses to map internal addresses to external ones.

Raw IP has no port concept. There is no way for a NAT to track which internal computer a raw IP packet belongs to. The NAT will either drop the packet or misroute it.

This is the killer problem. Even if you could get raw IP packets through firewalls, they cannot traverse NAT.

4. OS network stacks are not designed for it

  • No demultiplexing: The OS uses the protocol field to decide which socket gets the packet. If no socket is listening for protocol 249, the packet is dropped silently.
  • No buffering: Raw sockets don't get the same buffer management as TCP/UDP.
  • No standard API: Raw socket behavior varies between Linux, BSD, and Windows.

5. Middleboxes interfere

Deep packet inspection (DPI) devices, intrusion detection systems, and enterprise firewalls are trained on TCP and UDP. They may drop or flag packets with unusual protocol numbers as anomalous or malicious.

The Practical Reality

Method Traverses NAT? Traverses Firewalls? Works on open internet?
TCP Yes Yes (common ports) Yes
UDP Yes Yes (common ports) Yes
ICMP (ping) Sometimes Sometimes (often rate-limited) Limited
Raw IP (custom protocol) No No No

What About Protocols Like QUIC?

QUIC (HTTP/3) runs over UDP, not raw IP. It uses UDP port 443. This is why it works — it inherits all the NAT and firewall traversal properties of UDP. The "QUIC runs over UDP" design was a deliberate choice to ensure it would work on the existing internet.

The Great Dichotomy Applied

  • TCP and UDP are inside-computer abstractions that the OS provides to applications. They are implemented in the kernel. But they are also the only protocols that the outside-computer network infrastructure (routers, NATs, firewalls) has been built to handle.

  • Raw IP is closer to the wire — you're speaking the language of routers directly. But the network infrastructure has evolved to expect TCP or UDP on top. The outside-computer world has standardized on these two protocols.

The asymmetry: you can send raw IP packets from your computer, but the network between you and the destination will almost certainly drop them. The internet is not a neutral packet-delivery service — it is a TCP/UDP delivery service with everything else as second-class citizens.


Summary: The Full Protocol Stack

flowchart TD
    subgraph "Application Layer"
        HTTP[DNS / HTTP / Nostr / etc.]
    end
    subgraph "Transport Layer"
        TCP[TCP<br/>protocol 6<br/>reliable, ordered, connection]
        UDP[UDP<br/>protocol 17<br/>unreliable, stateless, datagram]
        RAW[Raw IP<br/>protocol any<br/>you craft everything]
    end
    subgraph "Network Layer"
        IP[IP<br/>packets with protocol field]
    end
    subgraph "Link Layer"
        ETH[Ethernet / WiFi<br/>frames with MTU 1500]
    end

    HTTP --> TCP
    HTTP --> UDP
    HTTP -.-> RAW
    TCP --> IP
    UDP --> IP
    RAW --> IP
    IP --> ETH

    style RAW fill:#a4a,color:#fff
    style TCP fill:#a44,color:#fff
    style UDP fill:#4a4,color:#fff
  • Green path (UDP): Works on the open internet. Used by Nostr-over-UDP, DNS, QUIC, video streaming.
  • Red path (TCP): Works on the open internet. Used by HTTP, email, SSH. Has detectable handshake.
  • Purple path (Raw IP): Works only on controlled networks (LAN, VPN, data center). Cannot traverse NAT. Blocked by firewalls. Requires root.

For censorship-resistant communication, UDP is the sweet spot: it works on the real internet, has no handshake pattern, and can be made to look like random noise.


References