TCP vs UDP — Network Protocol Guide - MarekBykowski/readme GitHub Wiki

Quick Summary

Property TCP UDP
Connection connection-oriented (3-way handshake) connectionless (just send)
Reliability guaranteed delivery, every byte best effort — may drop
Ordering always in order may arrive out of order
Retransmission yes — on timeout/loss none
Flow control yes — window size none
Congestion control yes — CUBIC, BBR, Reno none
Header size 20–60 bytes 8 bytes only
State stateful (seq, ack, window) stateless
Latency higher minimal
Head-of-line blocking yes — stalls on packet loss no
Drone use firmware upload, config, logs telemetry, MAVLink, video
MAVLink port 14550 (GCS), 14540 (MAVSDK)

TCP — 3-Way Handshake

CLIENT                          SERVER
  │                               │
  │ ──── SYN (seq=100) ─────────► │   "I want to connect"
  │                               │
  │ ◄─── SYN-ACK (seq=200,ack=101)│   "OK, I'm ready"
  │                               │
  │ ──── ACK (ack=201) ─────────► │   "acknowledged"
  │                               │
  │ ════════ DATA FLOWS ═════════ │
  │                               │
  │ ──── FIN ───────────────────► │   "I'm done"
  │ ◄─── FIN-ACK ─────────────── │
  │ ──── ACK ───────────────────► │
  │                               │
             CLOSED

Why 3 steps? Both sides must agree on starting sequence numbers. SYN = synchronise. Without this, there's no way to detect lost or reordered packets.


TCP — Retransmission and Head-of-Line Blocking

SENDER                          RECEIVER
  │                               │
  │ ──── seq=1 ────────────────► │  ✓ received
  │ ◄─── ACK=2 ──────────────── │
  │                               │
  │ ──── seq=2 ────────────────► │  ✗ LOST
  │                               │
  │       ... timeout 200ms ...   │
  │                               │
  │ ──── seq=3 ────────────────► │  buffered — not delivered yet!
  │ ──── seq=4 ────────────────► │  buffered — not delivered yet!
  │                               │
  │ ──── seq=2 (RETRANSMIT) ───► │  ✓ received
  │ ◄─── ACK=5 ──────────────── │  seq 2,3,4 all delivered in order

⚠️ Head-of-line blocking: seq=3 and seq=4 arrive but are held back until seq=2 is retransmitted. All subsequent packets stall. Fatal for real-time telemetry.


TCP State Machine

CLOSED
  │  send SYN
  ▼
SYN_SENT
  │  recv SYN-ACK, send ACK
  ▼
ESTABLISHED  ◄─── data flows here
  │  send FIN
  ▼
FIN_WAIT_1
  │  recv FIN-ACK
  ▼
FIN_WAIT_2
  │  recv FIN
  ▼
TIME_WAIT  ──── 2×MSL timeout (~60s) ────► CLOSED

💡 TIME_WAIT: waits to ensure remote end received final ACK. Prevents old packets from corrupting new connections on the same port.


TCP Header

 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 │  Res  │U│A│P│R│S│F│         Window Size            │
│        │       │R│C│S│S│Y│I│                                 │
├────────┴───────┴─┴─┴─┴─┴─┴─┴───────────────────────────────┤
│          Checksum             │        Urgent Pointer         │
└───────────────────────────────┴───────────────────────────────┘
Minimum: 20 bytes

UDP — Fire and Forget

SENDER                          RECEIVER
  │                               │
  │ ──── dgram #1 (t=0ms) ─────► │  ✓ received — delivered immediately
  │ ──── dgram #2 (t=100ms) ───► │  ✗ lost     — gone, no retransmit
  │ ──── dgram #3 (t=200ms) ───► │  ✓ received — delivered immediately
  │ ──── dgram #4 (t=300ms) ───► │  ✓ received — delivered immediately
  │                               │

Packet #2 is lost — #3 and #4 are delivered immediately with no stall. Latency unaffected by loss. This is why MAVLink uses UDP.


UDP Header

 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          │
├─────────────────────────────┼─────────────────────────────────┤
│           Length            │           Checksum              │
├─────────────────────────────┴─────────────────────────────────┤
│                           Data                                │
└───────────────────────────────────────────────────────────────┘
Header: 8 bytes only — 4 fields, no state, no seq numbers

MAVLink over UDP

FLIGHT CONTROLLER               GCS / COMPANION COMPUTER
  │                               │
  │ ── HEARTBEAT (1Hz) ─────────► │  sysid=1, compid=1
  │ ── HEARTBEAT (1Hz) ─────────► │  "I'm alive"
  │                               │
  │ ── GLOBAL_POSITION (10Hz) ──► │  lat, lon, alt, heading
  │ ── ATTITUDE (10Hz) ─────────► │  roll, pitch, yaw
  │ ── SYS_STATUS (1Hz) ────────► │  battery, sensors
  │                               │
  │ ◄── COMMAND_LONG ─────────── │  MAV_CMD_NAV_TAKEOFF
  │ ── COMMAND_ACK ─────────────► │  result=ACCEPTED
  │                               │

Standard ports:

Port Use
14550 GCS (QGroundControl, Mission Planner)
14540 MAVSDK / offboard control
14580 MAVLink router

Why UDP Beats TCP for Drone Telemetry

TCP — retransmit stalls everything:

  t=0ms    pos ──────────────────────► ✓
  t=100ms  pos ──────────────────────► ✗ LOST
  t=200ms  pos  (BLOCKED — waiting)   ⊘
  t=300ms  pos  (BLOCKED — waiting)   ⊘
  t=400ms  retransmit t=100ms ────────► ✓ but 300ms stale!
  t=400ms  pos t=200ms ───────────────► ✓ but still delayed


UDP — drop and move on:

  t=0ms    pos ──────────────────────► ✓ fresh
  t=100ms  pos ──────────────────────► ✗ dropped, forgotten
  t=200ms  pos ──────────────────────► ✓ fresh, no stall
  t=300ms  pos ──────────────────────► ✓ fresh

⚠️ Stale telemetry = wrong control decisions. A dropped packet is better than a 300ms delayed packet. MAVLink includes its own sequence number (seq field in header) to detect drops at the application layer — no TCP needed.


When TCP Wins over UDP

FIRMWARE UPLOAD — reliability critical, latency irrelevant:

SENDER                          RECEIVER
  │                               │
  │ ──── chunk #1 (0-1023B) ───► │  ✓ ACK
  │ ──── chunk #2 (1024-2047B) ► │  ✗ LOST
  │ ──── chunk #2 (RETRANSMIT) ► │  ✓ ACK
  │ ──── chunk #3 (2048-3071B) ► │  ✓ ACK
  │                               │

Use TCP for:

  • Firmware upload — corruption = crashed drone
  • Parameter get/set — must not corrupt
  • Log file download — integrity required
  • SSH to companion computer

Use UDP for:

  • MAVLink telemetry — latency critical
  • Video streaming — stale frame worse than dropped frame
  • Position/attitude at 10–50Hz — freshness beats reliability

Drone Network Stack (OSI layers)

L7  Application   MAVLink, MAVSDK, ROS2, HTTP (config)
L6  Presentation  (absorbed into application in practice)
L5  Session       (absorbed into application in practice)
─────────────────────────────────────────────────────────
L4  Transport     UDP (telemetry)  /  TCP (firmware, SSH)
─────────────────────────────────────────────────────────
L3  Network       IP (192.168.x.x), ICMP
─────────────────────────────────────────────────────────
L2  Data Link     WiFi 802.11 (companion ↔ GCS)
                  900MHz radio link (telemetry, long range)
                  Ethernet (bench testing)
─────────────────────────────────────────────────────────
L1  Physical      RF signal, antenna
                  2.4 / 5.8 GHz (WiFi, RC control)
                  868 / 915 MHz  (telemetry radio, long range)

Generated during prep session · Swarmer Integration Engineer · 2026