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
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)
- 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 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
Post a Comment