741  Transport Protocols: Hands-On Lab

741.1 Learning Objectives

By the end of this lab, you will be able to:

  • Implement message sequencing: Use sequence numbers to detect missing, duplicate, and out-of-order messages
  • Build an acknowledgment system: Create ACK/NACK responses to confirm or reject message delivery
  • Design timeout and retry logic: Handle message loss with configurable timeouts and exponential backoff
  • Analyze transport layer statistics: Calculate throughput, loss rate, and retransmission overhead in real-time

741.2 Lab: Build a Reliable Message Transport System

This hands-on lab demonstrates core transport layer concepts by building a reliable message delivery system on an ESP32. You will implement the fundamental mechanisms that make protocols like TCP reliable: sequence numbers, acknowledgments, timeouts, and retransmissions.

741.2.1 Components Needed

Component Quantity Purpose
ESP32 DevKit 1 Microcontroller simulating sender and receiver
Green LED 1 ACK received indicator
Red LED 1 NACK/Timeout indicator
Yellow LED 1 Transmission in progress
Blue LED 1 Message received
220 ohm Resistors 4 Current limiting for LEDs
Breadboard 1 Circuit assembly
Jumper Wires Several Connections

741.2.2 Wokwi Simulator

Use the embedded simulator below to build and test your reliable transport system. Click β€œStart Simulation” to begin.

741.2.3 Circuit Diagram

%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#2C3E50', 'primaryTextColor': '#fff', 'primaryBorderColor': '#16A085', 'lineColor': '#E67E22', 'secondaryColor': '#16A085', 'tertiaryColor': '#E8F6F3', 'fontSize': '14px'}}}%%
graph LR
    subgraph ESP32["ESP32 DevKit"]
        GPIO2["GPIO 2"]
        GPIO4["GPIO 4"]
        GPIO5["GPIO 5"]
        GPIO18["GPIO 18"]
        GND["GND"]
    end

    subgraph LEDs["Transport State Indicators"]
        GREEN["Green LED<br/>(ACK Received)"]
        RED["Red LED<br/>(NACK/Timeout)"]
        YELLOW["Yellow LED<br/>(Transmitting)"]
        BLUE["Blue LED<br/>(Msg Received)"]
    end

    GPIO4 -->|"220 ohm"| GREEN
    GPIO2 -->|"220 ohm"| RED
    GPIO5 -->|"220 ohm"| YELLOW
    GPIO18 -->|"220 ohm"| BLUE

    GREEN --> GND
    RED --> GND
    YELLOW --> GND
    BLUE --> GND

    style ESP32 fill:#2C3E50,stroke:#16A085,color:#fff
    style LEDs fill:#E8F6F3,stroke:#16A085,color:#2C3E50
    style GREEN fill:#2ECC71,stroke:#27AE60,color:#fff
    style RED fill:#E74C3C,stroke:#C0392B,color:#fff
    style YELLOW fill:#F1C40F,stroke:#F39C12,color:#2C3E50
    style BLUE fill:#3498DB,stroke:#2980B9,color:#fff

Figure 741.1: Circuit diagram showing ESP32 connections to four LED indicators for transport layer state visualization

741.2.4 Complete Code

Copy this code into the Wokwi editor and upload to the ESP32:

/*
 * Reliable Message Transport System for ESP32
 *
 * This program demonstrates core transport layer concepts:
 * - Message sequencing with sequence numbers
 * - Acknowledgment system (ACK/NACK)
 * - Timeout and retry mechanism with exponential backoff
 * - Simulated packet loss for testing reliability
 * - Real-time throughput and loss statistics
 *
 * LED Indicators:
 * - Green (GPIO 4):  ACK received - message delivered successfully
 * - Red (GPIO 2):    NACK/Timeout - message delivery failed
 * - Yellow (GPIO 5): Transmitting - message in transit
 * - Blue (GPIO 18):  Received - new message arrived
 *
 * Transport Layer Concepts Demonstrated:
 * 1. Sequence Numbers: Each message has a unique ID for ordering
 * 2. Acknowledgments: Receiver confirms each message
 * 3. Timeouts: Sender waits limited time for ACK
 * 4. Retransmission: Failed messages are resent
 * 5. Statistics: Track throughput and reliability
 */

// ============= PIN DEFINITIONS =============
#define LED_ACK       4   // Green LED - ACK received
#define LED_NACK      2   // Red LED - NACK/Timeout
#define LED_TRANSMIT  5   // Yellow LED - Transmission in progress
#define LED_RECEIVED  18  // Blue LED - Message received

// ============= TRANSPORT CONFIGURATION =============
#define INITIAL_TIMEOUT_MS    500   // Initial timeout for ACK (milliseconds)
#define MAX_TIMEOUT_MS        4000  // Maximum timeout after backoff
#define MAX_RETRIES           3     // Maximum retransmission attempts
#define PACKET_LOSS_PERCENT   20    // Simulated packet loss rate (0-100)
#define WINDOW_SIZE           1     // Stop-and-wait (1), increase for sliding window

// ============= MESSAGE TYPES =============
#define MSG_DATA    0x01  // Data message
#define MSG_ACK     0x02  // Positive acknowledgment
#define MSG_NACK    0x03  // Negative acknowledgment (request retransmit)

// ============= MESSAGE STRUCTURE =============
struct TransportMessage {
  uint8_t  type;           // MSG_DATA, MSG_ACK, or MSG_NACK
  uint16_t sequenceNum;    // Sequence number (0-65535)
  uint16_t ackNum;         // Acknowledgment number (for ACK/NACK)
  uint8_t  payloadLen;     // Length of payload
  char     payload[64];    // Message payload
  uint16_t checksum;       // Simple checksum for integrity
};

// ============= STATISTICS TRACKING =============
struct TransportStats {
  uint32_t messagesSent;
  uint32_t messagesReceived;
  uint32_t acksReceived;
  uint32_t nacksReceived;
  uint32_t timeouts;
  uint32_t retransmissions;
  uint32_t bytesTransmitted;
  uint32_t simulatedDrops;
  unsigned long startTime;
};

// ============= GLOBAL VARIABLES =============
uint16_t nextSequenceNum = 0;       // Next sequence number to use
uint16_t expectedSequenceNum = 0;   // Expected sequence number (receiver)
TransportStats stats;
bool verboseMode = true;            // Print detailed logs

// ============= FUNCTION PROTOTYPES =============
void initializeLEDs();
void setLED(int led, bool state);
void blinkLED(int led, int times, int delayMs);
uint16_t calculateChecksum(TransportMessage* msg);
bool verifyChecksum(TransportMessage* msg);
void createDataMessage(TransportMessage* msg, const char* data);
void createAckMessage(TransportMessage* ack, uint16_t ackNum);
void createNackMessage(TransportMessage* nack, uint16_t nackNum);
bool simulatePacketLoss();
bool sendWithReliability(const char* data);
void processReceivedMessage(TransportMessage* msg);
void printMessage(TransportMessage* msg, const char* direction);
void printStatistics();
float calculateThroughput();

// ============= SETUP =============
void setup() {
  Serial.begin(115200);
  delay(1000);

  // Initialize random seed for packet loss simulation
  randomSeed(analogRead(0));

  // Initialize LEDs
  initializeLEDs();

  // Initialize statistics
  memset(&stats, 0, sizeof(TransportStats));
  stats.startTime = millis();

  // Startup sequence
  Serial.println("\n");
  Serial.println("╔══════════════════════════════════════════════════════════╗");
  Serial.println("β•‘     RELIABLE MESSAGE TRANSPORT SYSTEM - ESP32 LAB        β•‘");
  Serial.println("β•‘                                                          β•‘");
  Serial.println("β•‘  Demonstrating Transport Layer Concepts:                 β•‘");
  Serial.println("β•‘  - Sequence Numbers    - Acknowledgments                 β•‘");
  Serial.println("β•‘  - Timeouts/Retries    - Packet Loss Handling            β•‘");
  Serial.println("β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•");
  Serial.println();

  // Show configuration
  Serial.println("=== CONFIGURATION ===");
  Serial.printf("Initial Timeout:    %d ms\n", INITIAL_TIMEOUT_MS);
  Serial.printf("Max Timeout:        %d ms\n", MAX_TIMEOUT_MS);
  Serial.printf("Max Retries:        %d\n", MAX_RETRIES);
  Serial.printf("Packet Loss Rate:   %d%%\n", PACKET_LOSS_PERCENT);
  Serial.printf("Window Size:        %d (Stop-and-Wait)\n", WINDOW_SIZE);
  Serial.println();

  // LED test
  Serial.println("=== LED TEST ===");
  Serial.println("Testing all LEDs...");
  blinkLED(LED_ACK, 2, 200);
  blinkLED(LED_NACK, 2, 200);
  blinkLED(LED_TRANSMIT, 2, 200);
  blinkLED(LED_RECEIVED, 2, 200);
  Serial.println("LED test complete.\n");

  delay(1000);
}

// ============= MAIN LOOP =============
void loop() {
  // Demonstrate reliable message transmission
  static int messageCount = 0;
  char messageBuffer[64];

  // Create a test message
  snprintf(messageBuffer, sizeof(messageBuffer),
           "Sensor reading #%d: Temp=%.1fC",
           messageCount, 20.0 + (random(100) / 10.0));

  Serial.println("════════════════════════════════════════════════════════════");
  Serial.printf(">>> SENDING MESSAGE %d <<<\n", messageCount);
  Serial.println("════════════════════════════════════════════════════════════");

  // Send with reliability (handles retries, timeouts, etc.)
  bool success = sendWithReliability(messageBuffer);

  if (success) {
    Serial.println("Result: MESSAGE DELIVERED SUCCESSFULLY");
  } else {
    Serial.println("Result: MESSAGE DELIVERY FAILED (max retries exceeded)");
  }

  // Print statistics every 5 messages
  if (messageCount % 5 == 4) {
    printStatistics();
  }

  messageCount++;
  Serial.println();

  // Wait before next transmission
  delay(3000);
}

// ============= LED FUNCTIONS =============
void initializeLEDs() {
  pinMode(LED_ACK, OUTPUT);
  pinMode(LED_NACK, OUTPUT);
  pinMode(LED_TRANSMIT, OUTPUT);
  pinMode(LED_RECEIVED, OUTPUT);

  // All LEDs off initially
  digitalWrite(LED_ACK, LOW);
  digitalWrite(LED_NACK, LOW);
  digitalWrite(LED_TRANSMIT, LOW);
  digitalWrite(LED_RECEIVED, LOW);
}

void setLED(int led, bool state) {
  digitalWrite(led, state ? HIGH : LOW);
}

void blinkLED(int led, int times, int delayMs) {
  for (int i = 0; i < times; i++) {
    digitalWrite(led, HIGH);
    delay(delayMs);
    digitalWrite(led, LOW);
    delay(delayMs);
  }
}

// ============= CHECKSUM FUNCTIONS =============
uint16_t calculateChecksum(TransportMessage* msg) {
  uint16_t sum = 0;
  sum += msg->type;
  sum += msg->sequenceNum;
  sum += msg->ackNum;
  sum += msg->payloadLen;

  for (int i = 0; i < msg->payloadLen; i++) {
    sum += (uint8_t)msg->payload[i];
  }

  return ~sum;  // One's complement
}

bool verifyChecksum(TransportMessage* msg) {
  uint16_t expected = calculateChecksum(msg);
  return (msg->checksum == expected);
}

// ============= MESSAGE CREATION =============
void createDataMessage(TransportMessage* msg, const char* data) {
  msg->type = MSG_DATA;
  msg->sequenceNum = nextSequenceNum;
  msg->ackNum = 0;
  msg->payloadLen = strlen(data);

  if (msg->payloadLen > 63) msg->payloadLen = 63;
  strncpy(msg->payload, data, msg->payloadLen);
  msg->payload[msg->payloadLen] = '\0';

  msg->checksum = calculateChecksum(msg);
}

void createAckMessage(TransportMessage* ack, uint16_t ackNum) {
  ack->type = MSG_ACK;
  ack->sequenceNum = 0;
  ack->ackNum = ackNum;
  ack->payloadLen = 0;
  ack->payload[0] = '\0';
  ack->checksum = calculateChecksum(ack);
}

void createNackMessage(TransportMessage* nack, uint16_t nackNum) {
  nack->type = MSG_NACK;
  nack->sequenceNum = 0;
  nack->ackNum = nackNum;
  nack->payloadLen = 0;
  nack->payload[0] = '\0';
  nack->checksum = calculateChecksum(nack);
}

// ============= PACKET LOSS SIMULATION =============
bool simulatePacketLoss() {
  int roll = random(100);
  bool lost = (roll < PACKET_LOSS_PERCENT);

  if (lost) {
    stats.simulatedDrops++;
    if (verboseMode) {
      Serial.printf("  [SIMULATED] Packet lost! (roll=%d < %d%%)\n",
                    roll, PACKET_LOSS_PERCENT);
    }
  }
  return lost;
}

// ============= RELIABLE SEND WITH RETRIES =============
bool sendWithReliability(const char* data) {
  TransportMessage msg;
  TransportMessage response;
  int retryCount = 0;
  int currentTimeout = INITIAL_TIMEOUT_MS;

  // Create the data message
  createDataMessage(&msg, data);

  printMessage(&msg, "SEND");

  while (retryCount <= MAX_RETRIES) {
    // Turn on transmit LED
    setLED(LED_TRANSMIT, true);

    if (verboseMode) {
      if (retryCount > 0) {
        Serial.printf("\n--- RETRY %d/%d (timeout=%dms) ---\n",
                      retryCount, MAX_RETRIES, currentTimeout);
        stats.retransmissions++;
      }
      Serial.printf("  [TX] Sending SEQ=%d, waiting for ACK...\n",
                    msg.sequenceNum);
    }

    stats.messagesSent++;
    stats.bytesTransmitted += sizeof(TransportMessage);

    // Simulate network transmission delay
    delay(50);

    // Turn off transmit LED
    setLED(LED_TRANSMIT, false);

    // Simulate the "receiver" processing
    // Check if packet was "lost" in transit
    if (simulatePacketLoss()) {
      // Packet lost - wait for timeout
      if (verboseMode) {
        Serial.printf("  [WAIT] Waiting %dms for ACK (packet was lost)...\n",
                      currentTimeout);
      }
      delay(currentTimeout);

      // Timeout occurred
      setLED(LED_NACK, true);
      delay(200);
      setLED(LED_NACK, false);

      stats.timeouts++;
      if (verboseMode) {
        Serial.println("  [TIMEOUT] No ACK received - will retry");
      }

      // Exponential backoff
      retryCount++;
      currentTimeout = min(currentTimeout * 2, MAX_TIMEOUT_MS);
      continue;
    }

    // Packet arrived at receiver
    setLED(LED_RECEIVED, true);
    stats.messagesReceived++;

    if (verboseMode) {
      Serial.printf("  [RX] Message received at destination\n");
    }

    // Receiver processes message and sends ACK
    processReceivedMessage(&msg);

    // Simulate receiver creating ACK
    // Small chance of ACK being lost too
    if (simulatePacketLoss()) {
      setLED(LED_RECEIVED, false);
      if (verboseMode) {
        Serial.println("  [SIMULATED] ACK was lost in transit!");
        Serial.printf("  [WAIT] Waiting %dms for ACK...\n", currentTimeout);
      }
      delay(currentTimeout);

      setLED(LED_NACK, true);
      delay(200);
      setLED(LED_NACK, false);

      stats.timeouts++;
      retryCount++;
      currentTimeout = min(currentTimeout * 2, MAX_TIMEOUT_MS);
      continue;
    }

    // ACK received successfully
    createAckMessage(&response, msg.sequenceNum);
    printMessage(&response, "RECV");

    setLED(LED_RECEIVED, false);
    setLED(LED_ACK, true);
    stats.acksReceived++;

    if (verboseMode) {
      Serial.printf("  [ACK] Received ACK for SEQ=%d\n", response.ackNum);
    }

    delay(300);
    setLED(LED_ACK, false);

    // Success! Increment sequence number for next message
    nextSequenceNum++;
    return true;
  }

  // Max retries exceeded
  Serial.println("  [FAIL] Max retries exceeded!");
  blinkLED(LED_NACK, 3, 150);
  return false;
}

// ============= RECEIVER MESSAGE PROCESSING =============
void processReceivedMessage(TransportMessage* msg) {
  if (verboseMode) {
    // Check sequence number
    if (msg->sequenceNum == expectedSequenceNum) {
      Serial.printf("  [RX] Sequence OK (expected=%d, got=%d)\n",
                    expectedSequenceNum, msg->sequenceNum);
      expectedSequenceNum++;
    } else if (msg->sequenceNum < expectedSequenceNum) {
      Serial.printf("  [RX] DUPLICATE detected (expected=%d, got=%d)\n",
                    expectedSequenceNum, msg->sequenceNum);
    } else {
      Serial.printf("  [RX] OUT OF ORDER (expected=%d, got=%d)\n",
                    expectedSequenceNum, msg->sequenceNum);
    }

    // Verify checksum
    if (verifyChecksum(msg)) {
      Serial.println("  [RX] Checksum verified OK");
    } else {
      Serial.println("  [RX] CHECKSUM FAILED - would send NACK");
      stats.nacksReceived++;
    }
  }
}

// ============= MESSAGE PRINTING =============
void printMessage(TransportMessage* msg, const char* direction) {
  if (!verboseMode) return;

  Serial.println();
  Serial.printf("  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ %s MESSAGE ───────────────┐\n", direction);
  Serial.printf("  β”‚ Type:        %-28s β”‚\n",
                msg->type == MSG_DATA ? "DATA" :
                msg->type == MSG_ACK ? "ACK" : "NACK");
  Serial.printf("  β”‚ Sequence:    %-28d β”‚\n", msg->sequenceNum);
  Serial.printf("  β”‚ Ack Number:  %-28d β”‚\n", msg->ackNum);
  Serial.printf("  β”‚ Payload Len: %-28d β”‚\n", msg->payloadLen);

  if (msg->payloadLen > 0) {
    Serial.printf("  β”‚ Payload:     %-28s β”‚\n", msg->payload);
  }

  Serial.printf("  β”‚ Checksum:    0x%04X                       β”‚\n", msg->checksum);
  Serial.println("  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜");
}

// ============= STATISTICS =============
void printStatistics() {
  unsigned long elapsed = (millis() - stats.startTime) / 1000;
  float throughput = calculateThroughput();
  float lossRate = stats.messagesSent > 0 ?
                   (float)(stats.simulatedDrops * 100) / stats.messagesSent : 0;
  float retxRate = stats.messagesSent > 0 ?
                   (float)(stats.retransmissions * 100) / stats.messagesSent : 0;

  Serial.println();
  Serial.println("╔══════════════════════════════════════════════════════════╗");
  Serial.println("β•‘              TRANSPORT LAYER STATISTICS                  β•‘");
  Serial.println("╠══════════════════════════════════════════════════════════╣");
  Serial.printf("β•‘  Messages Sent:       %-10lu                         β•‘\n",
                stats.messagesSent);
  Serial.printf("β•‘  Messages Received:   %-10lu                         β•‘\n",
                stats.messagesReceived);
  Serial.printf("β•‘  ACKs Received:       %-10lu                         β•‘\n",
                stats.acksReceived);
  Serial.printf("β•‘  Timeouts:            %-10lu                         β•‘\n",
                stats.timeouts);
  Serial.printf("β•‘  Retransmissions:     %-10lu                         β•‘\n",
                stats.retransmissions);
  Serial.printf("β•‘  Simulated Drops:     %-10lu                         β•‘\n",
                stats.simulatedDrops);
  Serial.println("╠══════════════════════════════════════════════════════════╣");
  Serial.printf("β•‘  Elapsed Time:        %-10lu seconds                 β•‘\n",
                elapsed);
  Serial.printf("β•‘  Throughput:          %-10.2f bytes/sec              β•‘\n",
                throughput);
  Serial.printf("β•‘  Loss Rate:           %-10.1f%%                       β•‘\n",
                lossRate);
  Serial.printf("β•‘  Retransmission Rate: %-10.1f%%                       β•‘\n",
                retxRate);
  Serial.println("β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•");
}

float calculateThroughput() {
  unsigned long elapsed = millis() - stats.startTime;
  if (elapsed == 0) return 0;
  return (float)stats.bytesTransmitted / (elapsed / 1000.0);
}

741.2.5 Step-by-Step Instructions

Step 1: Set Up the Circuit

  1. Open the Wokwi simulator above (or visit wokwi.com/projects/new/esp32)
  2. Add four LEDs to the breadboard (green, red, yellow, blue)
  3. Connect each LED with a 220 ohm resistor
  4. Wire the connections as shown in the circuit diagram:
    • GPIO 4 to Green LED (ACK indicator)
    • GPIO 2 to Red LED (NACK/Timeout indicator)
    • GPIO 5 to Yellow LED (Transmitting indicator)
    • GPIO 18 to Blue LED (Received indicator)
    • All LED cathodes to GND

Step 2: Upload and Run the Code

  1. Copy the complete code into the Wokwi code editor
  2. Click β€œStart Simulation” to begin
  3. Open the Serial Monitor (set to 115200 baud) to see detailed output

Step 3: Observe Transport Behavior

Watch the LEDs and Serial Monitor to understand each phase:

LED Meaning Transport Layer Concept
Yellow ON Message being transmitted Data segment in transit
Blue ON Message arrived at receiver Successful delivery
Green ON ACK received by sender Acknowledgment mechanism
Red ON Timeout or NACK Retransmission trigger

Step 4: Analyze the Statistics

After 5 messages, the system prints statistics. Look for:

  • Retransmission Rate: Higher than packet loss rate? Why? (ACK loss doubles retransmissions)
  • Throughput: How does packet loss affect effective throughput?
  • Timeout Count: Each timeout represents wasted waiting time

741.2.6 Understanding the Code

Key Transport Layer Concepts in the Code:

Tip1. Sequence Numbers (Lines 48-49)
uint16_t sequenceNum;    // Unique ID for each message
uint16_t ackNum;         // Which message is being acknowledged

Sequence numbers enable: - Ordering: Receiver knows correct order even if packets arrive shuffled - Duplicate detection: Same sequence number = retransmission, not new data - Loss detection: Gap in sequence numbers = missing packet

Tip2. Acknowledgment System (Lines 212-229)
void createAckMessage(TransportMessage* ack, uint16_t ackNum) {
  ack->type = MSG_ACK;
  ack->ackNum = ackNum;  // "I received message #ackNum"
}

ACK/NACK enables: - Confirmation: Sender knows message arrived - Error feedback: NACK requests specific retransmission - Flow control: Receiver controls sender’s pace

Tip3. Timeout and Retry with Exponential Backoff (Lines 251-295)
int currentTimeout = INITIAL_TIMEOUT_MS;  // Start at 500ms
// On timeout:
currentTimeout = min(currentTimeout * 2, MAX_TIMEOUT_MS);  // Double it

Timeouts handle: - Lost packets: No ACK after timeout triggers retry - Lost ACKs: Sender cannot distinguish from lost data - Exponential backoff: Prevents network congestion during problems

Tip4. Packet Loss Simulation (Lines 232-244)
bool simulatePacketLoss() {
  return (random(100) < PACKET_LOSS_PERCENT);  // 20% default
}

Simulating loss helps understand: - Why reliability matters: See real impact of unreliable networks - Overhead cost: Retransmissions consume bandwidth and time - Trade-offs: More aggressive timeouts vs. unnecessary retries

741.2.7 Experiments to Try

Experiment 1: Vary Packet Loss Rate

Change PACKET_LOSS_PERCENT to different values:

Loss Rate Expected Behavior
0% All messages succeed first try
20% Some retransmissions, most succeed
50% Many retries, some failures
80% Most messages fail after max retries

Experiment 2: Adjust Timeout Values

Setting Effect
Lower timeout (100ms) Faster failure detection, but more false retries
Higher timeout (2000ms) Fewer false retries, but slower recovery
No backoff Network stays congested during problems

Experiment 3: Remove Reliability Features

Comment out different sections to see what breaks: - Remove checksums: Corrupted data goes undetected - Remove sequence numbers: Duplicates and reordering cause chaos - Remove retries: Any packet loss means data loss

741.2.8 Challenge Exercises

WarningChallenge 1: Implement Sliding Window Protocol

Modify the code to send multiple messages before waiting for ACKs:

#define WINDOW_SIZE 4  // Send up to 4 messages before blocking

// Track which messages are awaiting ACK
bool awaitingAck[WINDOW_SIZE];
uint16_t windowBase = 0;  // First unacknowledged sequence number

// Send messages until window is full
while (nextSeq < windowBase + WINDOW_SIZE) {
  sendMessage(nextSeq++);
}

// On ACK, slide window forward
windowBase = ackNum + 1;

Goal: Improve throughput by not waiting for each ACK individually.

WarningChallenge 2: Add Congestion Control

Implement basic congestion control that reduces send rate when losses occur:

int congestionWindow = 4;  // Start with window of 4

// On successful ACK
if (congestionWindow < MAX_WINDOW) {
  congestionWindow++;  // Additive increase
}

// On timeout (congestion signal)
congestionWindow = max(1, congestionWindow / 2);  // Multiplicative decrease

Goal: Prevent overwhelming the network when congestion is detected.

WarningChallenge 3: Implement Selective Repeat

Instead of retransmitting from the lost packet onward (Go-Back-N), only retransmit the specific lost packets:

// Receiver tracks received packets with bitmap
uint32_t receivedBitmap = 0;

// On receiving packet with seq N
receivedBitmap |= (1 << (N - windowBase));

// Send selective NACK for missing packets
for (int i = 0; i < WINDOW_SIZE; i++) {
  if (!(receivedBitmap & (1 << i))) {
    sendNack(windowBase + i);
  }
}

Goal: Reduce retransmission overhead by only resending what was actually lost.

741.2.9 Key Transport Layer Concepts Summary

This lab demonstrates the fundamental mechanisms that make reliable transport possible:

Concept What It Solves How It Works
Sequence Numbers Ordering, duplicates, loss detection Each message gets unique incremental ID
Acknowledgments Delivery confirmation Receiver sends ACK/NACK for each message
Timeouts Lost packet detection Sender waits limited time for ACK
Retransmission Lost data recovery Resend unacknowledged messages
Checksums Data corruption Mathematical verification of integrity
Exponential Backoff Congestion avoidance Double timeout on each failure
NoteConnection to Real Protocols

The mechanisms in this lab directly map to real transport protocols:

  • TCP uses all these mechanisms plus sliding windows, congestion control, and flow control
  • QUIC (used by HTTP/3) adds encryption and multiplexing
  • MQTT QoS 1 uses sequence numbers and ACKs similar to this lab
  • CoAP Confirmable messages use the same timeout/retry approach

Understanding these fundamentals helps you make informed protocol choices for IoT applications.

741.3 Original Source Figures (Alternative Views)

Transport protocols diagram comparing TCP (connection-oriented, reliable, ordered delivery with acknowledgments) and UDP (connectionless, best-effort, no guarantees) with their key characteristics and use cases

Transport layer protocols showing TCP and UDP characteristics

Source: CP IoT System Design Guide, Chapter 4 - Networking Fundamentals

Data transportation diagram showing how application data flows through transport layer (TCP/UDP), network layer (IP), and data link layer with appropriate headers added at each stage

Data transportation through network layers

Source: CP IoT System Design Guide, Chapter 4 - Networking Fundamentals

UDP datagram structure showing the 8-byte header with source port, destination port, length, and checksum fields followed by the payload data

UDP datagram structure and header fields

Source: CP IoT System Design Guide, Chapter 4 - Networking Fundamentals

Data link layer diagram showing its position between network and physical layers, with responsibilities including framing, addressing (MAC), error detection (CRC), and media access control

Data link layer functions and frame structure

Data link layer diagram showing its position between network and physical layers, with responsibilities including framing, addressing (MAC), error detection (CRC), and media access control - scalable vector format

Data link layer functions and frame structure

Source: CP IoT System Design Guide, Chapter 4 - Networking Fundamentals

741.4 Summary

This hands-on lab demonstrated the core transport layer mechanisms through practical implementation:

  • Sequence numbers enable ordering, duplicate detection, and loss detection
  • Acknowledgments confirm delivery and trigger retransmissions when missing
  • Timeouts with exponential backoff handle lost packets without overwhelming the network
  • Checksums catch data corruption before it causes problems
  • Statistics tracking reveals the true cost of reliability overhead

The challenge exercises provide opportunities to implement advanced mechanisms like sliding windows, congestion control, and selective repeat.

741.5 What’s Next

Continue your transport protocol learning:

NoteRelated Topics

Prerequisites (read these first):

  • Layered Network Models - Understanding OSI and TCP/IP models is crucial since transport protocols operate at Layer 4
  • Networking Basics - Knowledge of packets, headers, and ports provides the foundation for transport protocols
  • Networking Fundamentals - Core networking concepts including IP addressing and protocol stacks

See Also: