Skip to main content

Performance Evaluation of TCP NewReno and TCP Cubic under Varying Round Trip Times Using NS-3

AIM

To analyze Packet Loss Ratio (PLR) and Packet Delivery Ratio (PDR) in a congested point-to-point router network with mixed TCP and UDP traffic using NS-3 simulation.

1. Introduction to Network Congestion

Network congestion is a fundamental challenge in packet-switched computer networks. It manifests when the aggregate demand for bandwidth exceeds the physical capacity of network resources, such as intermediary routers or transmission links. When traffic volume outpaces a router's processing speed or link bandwidth, the router's buffer (queue) begins to fill. Once this queue reaches maximum capacity, subsequent incoming packets are discarded—a process known as tail-dropping. These dropped packets result in severe queuing delays, wasted bandwidth, and degraded application-level performance.

2. Performance Metrics

To empirically evaluate network reliability and the severity of congestion during simulation, two primary performance metrics are analyzed:

  • Packet Delivery Ratio (PDR): This metric represents the percentage of data packets successfully received at the destination relative to the total number transmitted by the source. A higher PDR signifies a robust and reliable network connection.
  • Packet Loss Ratio (PLR): Conversely, the PLR quantifies network failure by measuring the percentage of packets discarded in transit due to buffer overflow or link errors.

3. Protocol Behavior Under Congestion: TCP vs. UDP

The impact of a congested link varies significantly depending on the transport layer protocol utilized by the active flows.

  • Transmission Control Protocol (TCP): As a connection-oriented protocol, TCP is designed for high reliability. It features an integrated, closed-loop congestion control mechanism. When TCP detects packet loss (an indicator of congestion), it employs algorithms such as slow start and Additive Increase Multiplicative Decrease (AIMD) to dynamically scale back its transmission window. By actively reducing its sending rate, TCP helps alleviate network strain, ultimately maintaining a high PDR, albeit at the cost of immediate throughput.
  • User Datagram Protocol (UDP): UDP is a connectionless protocol that lacks inherent congestion control or backoff mechanisms. It transmits datagrams continuously at the application's defined rate, completely blind to underlying network conditions. During a bottleneck event, TCP flows will politely reduce their traffic, while UDP flows will continue to flood the router. Because the router utilizes a strict tail-drop mechanism when its queue is full, the aggressive, unyielding nature of UDP results in a dramatically higher Packet Loss Ratio compared to TCP.

4. Simulation Scenario and Network Topology

To observe the interaction between competing protocols under stress, this experiment implements a classic "Dumbbell" network topology. This specific architecture is designed to force multiple high-capacity data streams through a single, constrained pathway.

The physical layout consists of six independent sender nodes—three operating TCP flows and three operating UDP flows. All six senders transmit data across high-speed access links to a central gateway router (R1). Traffic is then forwarded from R1 to a second central router (R2) across a heavily restricted bottleneck link. Finally, R2 distributes the packets to their respective receiver nodes. Because the aggregate data rate of the six senders vastly exceeds the bandwidth of the R1-R2 link, the router queue quickly fills, guaranteeing a congestion event.

5. Simulation Parameters

The experiment is modeled using the NS-3 discrete-event network simulator, with network traffic data captured and analyzed via the FlowMonitor module. The specific environmental constraints and protocol parameters are detailed in the table below:

Parameter Assigned Value
Simulator Environment NS-3
Active Network Flows 3 TCP (NewReno) & 3 UDP
Access Link Properties 10 Mbps Bandwidth, 2 ms Delay
Bottleneck Link Properties 1 Mbps Bandwidth, 10 ms Delay
Router Queue Size 10 Packets (Tail-Drop Mechanism)
UDP Transmission Rate 2000 packets/sec
Total Simulation Time 20 seconds
Monitoring Tool FlowMonitor

SOURCE CODE

/*
 * NS-3 Simulation: Packet Loss Ratio and PDR in a Congested
 * Point-to-Point Router with Mixed TCP + UDP Traffic
 *
 * Register Number: [Your ID Redacted]
 * Topic: Analyze packet loss ratio and PDR in a congested
 *        point-to-point router with mixed TCP+UDP traffic.
 *
 * Prompt used (Claude Sonnet 4):
 * "Write a complete NS-3 simulation in C++ that:
 *  1. Creates a dumbbell topology: 3 TCP senders and 3 UDP senders
 *     connected to a bottleneck router R1, which connects to R2,
 *     which connects to 3 TCP receivers and 3 UDP receivers.
 *  2. Sets a small queue size on the R1-R2 bottleneck link (10 packets)
 *     to simulate congestion.
 *  3. Uses OnOffApplication for TCP (BulkSendApplication) and
 *     UdpClientServer for UDP.
 *  4. Runs for 20 seconds and outputs:
 *     - Flow monitor XML for PDR and packet loss analysis
 *     - Ascii trace for animation with PyViz or custom Python script
 *  5. Prints per-flow stats: TX packets, RX packets, lost packets,
 *     PDR (%), and PLR (%) to stdout."
 *
 * Compile & Run:
 *   ./ns3 run scratch/24bps1xxx.cc
 */

#include "ns3/core-module.h"
#include "ns3/network-module.h"
#include "ns3/internet-module.h"
#include "ns3/point-to-point-module.h"
#include "ns3/applications-module.h"
#include "ns3/flow-monitor-module.h"
#include "ns3/ipv4-global-routing-helper.h"
#include "ns3/netanim-module.h"
#include <iomanip>

using namespace ns3;

NS_LOG_COMPONENT_DEFINE("MixedTrafficCongestion");

int main(int argc, char *argv[])
{
    // ─── Simulation parameters ─────────────────────────────────────────────
    uint32_t nTCP        = 3;        // Number of TCP sender/receiver pairs
    uint32_t nUDP        = 3;        // Number of UDP sender/receiver pairs
    double   simTime     = 20.0;     // Simulation duration (seconds)
    uint32_t queueSize   = 10;       // Bottleneck queue size (packets) — causes congestion
    std::string bottleneckBW  = "1Mbps";
    std::string bottleneckDel = "10ms";
    std::string accessBW      = "10Mbps";
    std::string accessDel     = "2ms";

    CommandLine cmd;
    cmd.AddValue("nTCP",      "Number of TCP flows",          nTCP);
    cmd.AddValue("nUDP",      "Number of UDP flows",          nUDP);
    cmd.AddValue("simTime",   "Simulation duration (s)",      simTime);
    cmd.AddValue("queueSize", "Bottleneck queue depth (pkts)", queueSize);
    cmd.Parse(argc, argv);

    // ─── Topology ──────────────────────────────────────────────────────────
    //
    //  TCP_S[0..2]  ──┐                    ┌── TCP_R[0..2]
    //                 ├── R1 ──(bottleneck)── R2 ──┤
    //  UDP_S[0..2]  ──┘                    └── UDP_R[0..2]
    //

    NodeContainer tcpSenders, udpSenders;
    NodeContainer tcpReceivers, udpReceivers;
    NodeContainer routers;

    tcpSenders.Create(nTCP);
    udpSenders.Create(nUDP);
    tcpReceivers.Create(nTCP);
    udpReceivers.Create(nUDP);
    routers.Create(2);   // R1, R2

    Ptr<Node> R1 = routers.Get(0);
    Ptr<Node> R2 = routers.Get(1);

    // ─── P2P Helpers ───────────────────────────────────────────────────────
    PointToPointHelper accessLink, bottleneckLink;

    accessLink.SetDeviceAttribute("DataRate", StringValue(accessBW));
    accessLink.SetChannelAttribute("Delay",   StringValue(accessDel));

    bottleneckLink.SetDeviceAttribute("DataRate", StringValue(bottleneckBW));
    bottleneckLink.SetChannelAttribute("Delay",   StringValue(bottleneckDel));
    bottleneckLink.SetQueue("ns3::DropTailQueue",
                            "MaxSize", StringValue(std::to_string(queueSize) + "p"));

    // ─── Install Internet Stack ────────────────────────────────────────────
    InternetStackHelper internet;
    internet.Install(tcpSenders);
    internet.Install(udpSenders);
    internet.Install(tcpReceivers);
    internet.Install(udpReceivers);
    internet.Install(routers);

    // ─── Assign IP addresses ───────────────────────────────────────────────
    Ipv4AddressHelper ipv4;
    std::vector<Ipv4InterfaceContainer> tcpSenderIfaces(nTCP), udpSenderIfaces(nUDP);
    std::vector<Ipv4InterfaceContainer> tcpRecvIfaces(nTCP),   udpRecvIfaces(nUDP);

    // TCP senders → R1
    for (uint32_t i = 0; i < nTCP; i++) {
        ipv4.SetBase(("10.1." + std::to_string(i + 1) + ".0").c_str(), "255.255.255.0");
        NetDeviceContainer d = accessLink.Install(tcpSenders.Get(i), R1);
        tcpSenderIfaces[i]   = ipv4.Assign(d);
    }

    // UDP senders → R1
    for (uint32_t i = 0; i < nUDP; i++) {
        ipv4.SetBase(("10.2." + std::to_string(i + 1) + ".0").c_str(), "255.255.255.0");
        NetDeviceContainer d = accessLink.Install(udpSenders.Get(i), R1);
        udpSenderIfaces[i]   = ipv4.Assign(d);
    }

    // R1 ↔ R2 bottleneck
    ipv4.SetBase("10.3.1.0", "255.255.255.0");
    NetDeviceContainer bottleneckDevs = bottleneckLink.Install(R1, R2);
    Ipv4InterfaceContainer bottleneckIfaces = ipv4.Assign(bottleneckDevs);

    // R2 → TCP receivers
    for (uint32_t i = 0; i < nTCP; i++) {
        ipv4.SetBase(("10.4." + std::to_string(i + 1) + ".0").c_str(), "255.255.255.0");
        NetDeviceContainer d = accessLink.Install(R2, tcpReceivers.Get(i));
        tcpRecvIfaces[i]     = ipv4.Assign(d);
    }

    // R2 → UDP receivers
    for (uint32_t i = 0; i < nUDP; i++) {
        ipv4.SetBase(("10.5." + std::to_string(i + 1) + ".0").c_str(), "255.255.255.0");
        NetDeviceContainer d = accessLink.Install(R2, udpReceivers.Get(i));
        udpRecvIfaces[i]     = ipv4.Assign(d);
    }

    Ipv4GlobalRoutingHelper::PopulateRoutingTables();

    // ─── Applications ──────────────────────────────────────────────────────
    uint16_t tcpPort = 9000;
    uint16_t udpPort = 8000;

    // TCP: BulkSend (sender) + PacketSink (receiver)
    for (uint32_t i = 0; i < nTCP; i++) {
        PacketSinkHelper sinkHelper("ns3::TcpSocketFactory",
            InetSocketAddress(Ipv4Address::GetAny(), tcpPort + i));
        ApplicationContainer sinkApp = sinkHelper.Install(tcpReceivers.Get(i));
        sinkApp.Start(Seconds(0.5));
        sinkApp.Stop(Seconds(simTime));

        BulkSendHelper bulkHelper("ns3::TcpSocketFactory",
            InetSocketAddress(tcpRecvIfaces[i].GetAddress(1), tcpPort + i));
        bulkHelper.SetAttribute("MaxBytes", UintegerValue(0)); // unlimited
        ApplicationContainer sendApp = bulkHelper.Install(tcpSenders.Get(i));
        sendApp.Start(Seconds(1.0));
        sendApp.Stop(Seconds(simTime));
    }

    // UDP: UdpClient (sender) + UdpServer (receiver)
    for (uint32_t i = 0; i < nUDP; i++) {
        UdpServerHelper udpServer(udpPort + i);
        ApplicationContainer serverApp = udpServer.Install(udpReceivers.Get(i));
        serverApp.Start(Seconds(0.5));
        serverApp.Stop(Seconds(simTime));

        UdpClientHelper udpClient(udpRecvIfaces[i].GetAddress(1), udpPort + i);
        udpClient.SetAttribute("MaxPackets",  UintegerValue(1000000));
        udpClient.SetAttribute("Interval",    TimeValue(MicroSeconds(500)));  // 2000 pkt/s
        udpClient.SetAttribute("PacketSize",  UintegerValue(1024));
        ApplicationContainer clientApp = udpClient.Install(udpSenders.Get(i));
        clientApp.Start(Seconds(1.0));
        clientApp.Stop(Seconds(simTime));
    }

    AsciiTraceHelper ascii;
    bottleneckLink.EnableAsciiAll(ascii.CreateFileStream("24bps1xxx-bottleneck.tr"));
    bottleneckLink.EnablePcapAll("24bps1xxx-bottleneck");

    AnimationInterface anim("24bps1xxx-anim.xml");

    for (uint32_t i = 0; i < nTCP; i++)
        anim.SetConstantPosition(tcpSenders.Get(i), 0, (double)i * 3);
    for (uint32_t i = 0; i < nUDP; i++)
        anim.SetConstantPosition(udpSenders.Get(i), 0, (double)(nTCP + i) * 3);
    anim.SetConstantPosition(R1, 10, (double)(nTCP + nUDP - 1) * 1.5);
    anim.SetConstantPosition(R2, 20, (double)(nTCP + nUDP - 1) * 1.5);
    for (uint32_t i = 0; i < nTCP; i++)
        anim.SetConstantPosition(tcpReceivers.Get(i), 30, (double)i * 3);
    for (uint32_t i = 0; i < nUDP; i++)
        anim.SetConstantPosition(udpReceivers.Get(i), 30, (double)(nTCP + i) * 3);

    FlowMonitorHelper flowMonHelper;
    Ptr<FlowMonitor> flowMon = flowMonHelper.InstallAll();

    Simulator::Stop(Seconds(simTime));
    Simulator::Run();

    flowMon->CheckForLostPackets();
    Ptr<Ipv4FlowClassifier> classifier =
        DynamicCast<Ipv4FlowClassifier>(flowMonHelper.GetClassifier());

    FlowMonitor::FlowStatsContainer stats = flowMon->GetFlowStats();

    std::cout << "\n=======================================================\n";
    std::cout << "  Mixed TCP+UDP Congestion Simulation — Flow Statistics\n";
    std::cout << "  Bottleneck: " << bottleneckBW << ", Queue: "
              << queueSize << " pkts\n";
    std::cout << "=======================================================\n";
    std::cout << std::left
              << std::setw(8)  << "FlowID"
              << std::setw(10) << "Proto"
              << std::setw(12) << "TX Pkts"
              << std::setw(12) << "RX Pkts"
              << std::setw(12) << "Lost Pkts"
              << std::setw(10) << "PDR (%)"
              << std::setw(10) << "PLR (%)"
              << "\n";
    std::cout << std::string(74, '-') << "\n";

    uint64_t totalTx = 0, totalRx = 0, totalLost = 0;
    for (auto &kv : stats) {
        Ipv4FlowClassifier::FiveTuple t = classifier->FindFlow(kv.first);
        std::string proto = (t.protocol == 6) ? "TCP" : "UDP";

        uint64_t tx   = kv.second.txPackets;
        uint64_t rx   = kv.second.rxPackets;
        uint64_t lost = kv.second.lostPackets;
        double   pdr  = (tx > 0) ? (100.0 * rx / tx)   : 0.0;
        double   plr  = (tx > 0) ? (100.0 * lost / tx) : 0.0;

        std::cout << std::left
                  << std::setw(8)  << kv.first
                  << std::setw(10) << proto
                  << std::setw(12) << tx
                  << std::setw(12) << rx
                  << std::setw(12) << lost
                  << std::fixed << std::setprecision(2)
                  << std::setw(10) << pdr
                  << std::setw(10) << plr
                  << "\n";
        totalTx   += tx;
        totalRx   += rx;
        totalLost += lost;
    }

    double overallPDR = (totalTx > 0) ? (100.0 * totalRx / totalTx) : 0.0;
    double overallPLR = (totalTx > 0) ? (100.0 * totalLost / totalTx) : 0.0;
    std::cout << std::string(74, '-') << "\n";
    std::cout << std::left
              << std::setw(8)  << "TOTAL"
              << std::setw(10) << "ALL"
              << std::setw(12) << totalTx
              << std::setw(12) << totalRx
              << std::setw(12) << totalLost
              << std::fixed << std::setprecision(2)
              << std::setw(10) << overallPDR
              << std::setw(10) << overallPLR
              << "\n";
    std::cout << "=======================================================\n\n";

    flowMon->SerializeToXmlFile("24bps1xxx-flowmon.xml", true, true);
    std::cout << "Flow monitor data saved to: 24bps1xxx-flowmon.xml\n";
    std::cout << "Animation XML saved to:     24bps1xxx-anim.xml\n\n";

    Simulator::Destroy();
    return 0;
}

Execution Command:

./ns3 run scratch/24bps1089.cc

Execution terminal output

Description: The animation shows packet flow from multiple senders to receivers through routers R1 and R2. The bottleneck link becomes congested, leading to packet drops at the router queue.

GRAPH (PDR / PLR / Throughput)

PDR PLR Throughput Graph Description:
  • TCP flows show high PDR (~96–98%) due to congestion control
  • UDP flows show low PDR (~50–60%) due to continuous transmission
  • Packet loss is significantly higher for UDP traffic
  • Increasing queue size improves PDR

WIRESHARK ANALYSIS

Wireshark Capture 1 Wireshark Capture 2

Wireshark was used to inspect packet-level transmission using PCAP files generated by NS-3.

RESULT

The simulation successfully demonstrated congestion effects in a mixed TCP and UDP network.

  • TCP achieved high reliability with minimal packet loss
  • UDP suffered significant packet loss due to lack of congestion control

INFERENCE

  • TCP is adaptive and reliable under congestion
  • UDP is fast but unreliable in congested networks
  • Queue size plays a critical role in network performance
  • Mixed traffic environments require traffic shaping or QoS mechanisms

TOOLS USED

  • NS-3 Simulator
  • FlowMonitor
  • NetAnim / Python Visualization
  • Gnuplot / Matplotlib
  • Wireshark (Optional)

Comments

Popular posts from this blog

How to Create Ubuntu 24.04 Bootable USB Using Rufus [Step-by-Step Guide]

How to Create Ubuntu 24.04 Bootable USB Using Rufus [Step-by-Step Guide] Are you planning to install or try Ubuntu 24.04 LTS ? The easiest and most reliable method is to create a bootable USB drive using Rufus on a Windows system. This detailed guide will help you create a Ubuntu 24.04 USB bootloader using Rufus with easy-to-follow steps and screenshots (optional). Here is the complete video of the bootloader creation and OS installation in Windows 11. 🧰 Requirements A USB flash drive (minimum 8GB recommended) A Windows PC Ubuntu 24.04 LTS ISO file Rufus USB creation tool 🧾 Steps to Create a Ubuntu 24.04 Bootable USB Using Rufus ✅ Step 1: Download Ubuntu 24.04 ISO Visit the official Ubuntu website and download the Ubuntu 24.04 LTS ISO file . ✅ Step 2: Download and Run Rufus Head to Rufus official site and download the latest version. Open the executable file (no installation required). ✅ Step 3: Insert USB Drive Plug in your USB drive. Rufus ...

Installing ns3 in Ubuntu 22.04 | Complete Instructions

In this post, we are going to see how to install ns-3.36.1 in Ubuntu 22.04. You can follow the video for complete details Tools used in this simulation: NS3 version ns-3.36.1  OS Used: Ubuntu 22.04 LTS Installation of NS3 (ns-3.36.1) There are some changes in the ns3 installation procedure and the dependencies. So open a terminal and issue the following commands Step 1:  Prerequisites $ sudo apt update In the following packages, all the required dependencies are taken care and you can install all these packages for the complete use of ns3. $ sudo apt install g++ python3 python3-dev pkg-config sqlite3 cmake python3-setuptools git qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools gir1.2-goocanvas-2.0 python3-gi python3-gi-cairo python3-pygraphviz gir1.2-gtk-3.0 ipython3 openmpi-bin openmpi-common openmpi-doc libopenmpi-dev autoconf cvs bzr unrar gsl-bin libgsl-dev libgslcblas0 wireshark tcpdump sqlite sqlite3 libsqlite3-dev  libxml2 libxml2-dev libc6-dev libc6-dev-i386 libc...

NS2 (NS-2.35) Installation in Ubuntu 11.10

This post will help you in installing Network Simulator 2 version NS2.35 in Ubuntu 11.10 Instructions Install Ubuntu Download NS-2.35 ( http://sourceforge.net/projects/nsnam/files/allinone/ns-allinone-2.35/ns-allinone-2.35.tar.gz/download ) Unzip or untar it to any folder (recommended is /home/ loginname) using the following commands one by one sudo apt-get update sudo apt-get install build-essential autoconf automake libxmu-dev tar zxvf ns-allinone-2.35.tar.gz cd ns-allinone-2.35 ./install Once installed the PATH information will be provided to you. Copy the PATH and LD_LIBRARY_PATH Variable to .bashrc (see a dot in the beginning) Input the path information in .bashrc file like this export PATH=$PATH:<Place your paths here> export LD_LIBRARY_PATH=$LD_LIBRARY_PATH: <place the LD_LIBRARY_PATHS> here. Once done, save the file and close execute the command source .bashrc try ns or nam to see whether your installation succeeded.