COMPUTER NETWORKS / 3. TRANSPORT LAYER — TCP & UDP

Transport Layer — TCP & UDP

Reliable ordered delivery (TCP) vs fast fire-and-forget (UDP)


EXPLANATION

The Transport Layer provides end-to-end communication between applications on different hosts. While IP gets packets to the right machine, the transport layer gets them to the right application using port numbers.

Port numbers (0–65535):
• 0–1023: Well-known ports (root required on Linux): HTTP=80, HTTPS=443, SSH=22, FTP=21, DNS=53, SMTP=25
• 1024–49151: Registered ports (apps register with IANA): PostgreSQL=5432, MySQL=3306, MongoDB=27017
• 49152–65535: Ephemeral/dynamic ports — OS assigns these to client connections

TCP (Transmission Control Protocol) — reliable, ordered, connection-based:
• Connection-oriented: must establish a connection before sending data (3-way handshake)
• Reliable: every segment is acknowledged. Lost segments are retransmitted
• Ordered: segments arrive in the exact order sent (TCP reorders if needed)
• Flow control: receiver tells sender how much buffer it has (Window Size) — prevents overwhelming slow receivers
• Congestion control: slows down when the network is congested (CWND)
• Used by: HTTP, HTTPS, SSH, FTP, SMTP, database connections

3-Way Handshake (TCP connection establishment):
• SYN → client says "I want to connect, my starting sequence number is X"
• SYN-ACK → server says "OK, my starting sequence is Y, I acknowledge X"
• ACK → client says "I acknowledge Y" — connection established!
• 4-Way Termination: FIN → ACK → FIN → ACK (each side closes independently)

UDP (User Datagram Protocol) — fast, connectionless, unreliable:
• No handshake, no acknowledgment, no ordering guarantees
• Just send datagrams and hope they arrive
• Much lower latency — no round trips for setup or acknowledgments
• Used by: DNS (quick request/response), video streaming (a dropped frame is fine), VoIP, gaming, QUIC (HTTP/3 uses UDP underneath!)

TCP vs UDP choice: if losing a packet means corrupted data, use TCP. If losing a packet is recoverable (next video frame comes along, or you retry yourself), use UDP.

Sequence numbers and ACKs: TCP numbers every byte sent. The receiver ACKs the next byte it expects. If sender doesn't receive an ACK in time, it retransmits. This is how TCP achieves reliability.

DIAGRAM

TCP 3-WAY HANDSHAKE:
  Client                          Server
    │──── SYN (seq=100) ──────────→│   "I want to connect"
    │←─── SYN-ACK (seq=200,ack=101)│   "OK, ready"
    │──── ACK (ack=201) ──────────→│   "Connected!"
    │                              │
    │═══ Data transfer begins ════│

  TCP 4-WAY TERMINATION:
    │──── FIN ──→│   "I'm done sending"
    │←── ACK ────│   "OK"
    │←── FIN ────│   "I'm done too"
    │──── ACK ──→│   "Goodbye" → TIME_WAIT state

  TCP vs UDP:
  ┌─────────────────────┬──────────────────────┐
  │        TCP          │         UDP          │
  ├─────────────────────┼──────────────────────┤
  │ Connection required │ Connectionless       │
  │ Reliable (ACKs)     │ Unreliable (no ACKs) │
  │ Ordered             │ Unordered            │
  │ Slower (overhead)   │ Faster (minimal)     │
  │ HTTP, SSH, DB       │ DNS, Video, Games    │
  └─────────────────────┴──────────────────────┘

CODE

PYTHON
1import socket
2import threading
3import time
4
5# ── TCP Server ─────────────────────────────────────────
6def tcp_server():
7 server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
8 server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
9 server.bind(("localhost", 9000))
10 server.listen(5)
11 print("[TCP Server] Listening on port 9000...")
12
13 while True:
14 conn, addr = server.accept() # blocks until connection
15 print(f"[TCP Server] Connected: {addr}")
16 data = conn.recv(1024)
17 print(f"[TCP Server] Received: {data.decode()}")
18 conn.send(b"Hello from TCP server!")
19 conn.close()
20
21# ── TCP Client ─────────────────────────────────────────
22def tcp_client():
23 time.sleep(0.1) # let server start
24 client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
25 client.connect(("localhost", 9000)) # 3-way handshake happens here
26 client.send(b"Hello from client!")
27 response = client.recv(1024)
28 print(f"[TCP Client] Response: {response.decode()}")
29 client.close()
30
31# Run both
32t = threading.Thread(target=tcp_server, daemon=True)
33t.start()
34tcp_client()
35
36# ── UDP example ────────────────────────────────────────
37def udp_demo():
38 # UDP Server
39 server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
40 server.bind(("localhost", 9001))
41
42 # UDP Client — no connect(), just sendto()
43 client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
44 client.sendto(b"UDP ping", ("localhost", 9001))
45
46 data, addr = server.recvfrom(1024)
47 print(f"[UDP Server] Got: {data.decode()} from {addr}")
48
49 server.close()
50 client.close()
51
52# ── Check open ports ───────────────────────────────────
53def port_scan(host: str, ports: list[int]):
54 for port in ports:
55 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
56 s.settimeout(0.5)
57 result = s.connect_ex((host, port)) # 0 = open, non-zero = closed
58 status = "OPEN" if result == 0 else "closed"
59 print(f" Port {port:5d}: {status}")
60 s.close()
61
62print("Scanning localhost:")
63port_scan("localhost", [22, 80, 443, 3000, 5432, 8000, 9000])
← PREV2. Network Layer — IPNEXT →4. DNS — The Internet's Phone Book