Web-Server Farm Simulation with Load Balancing Using P2P Links
Prompt used:
Using NS-3 (C++), write a complete simulation file named
farm.ccto be placed in thescratch/folder and run with./ns3 run scratch/farm.cc. Scenario: Simulate a web-server farm with load balancing using point-to-point links. The topology should have:
- 1 client node
- 1 load balancer node
- 3 backend web-server nodes
- All links are point-to-point (set data rate to 10Mbps, delay to 2ms)
- The load balancer distributes UDP traffic from the client across the 3 servers in a round-robin or equal-split fashion using OnOff applications
- Use
UdpClientServerHelperorOnOffApplication+PacketSinkon each server- Enable NetAnim output (
animation.xml) so the topology is visible in NetAnim with node labels (Client, LoadBalancer, Server1, Server2, Server3)- Enable FlowMonitor and at the end of the simulation print per-flow stats (throughput, delay, packet loss) to the terminal AND export a
flowmon.xml- Also generate a gnuplot-compatible
.pltfile that plots throughput (Mbps) vs. time for each server, so I can produce a graph- Simulation duration: 10 seconds
- Add clear comments throughout the code explaining each section
- After the code, give me the exact gnuplot commands to render the graph as a PNG
- Also write a one-page summary (plain text) explaining the simulation scenario, topology, protocol choices, load balancing strategy, and expected results — suitable for the handwritten write-up
Source Code:
/*
* ============================================================
* Web-Server Farm Simulation with Load Balancing
* File : 24bps1021.cc
* Student : 24BPS1021
* Run with : ./ns3 run scratch/24bps1021.cc
*
* Topology
* --------
* [Client]──p2p──[LoadBalancer]──p2p──[Server1]
* |
* p2p──[Server2]
* |
* p2p──[Server3]
*
* All P2P links : DataRate = 10 Mbps, Delay = 2 ms
*
* Strategy
* --------
* The LoadBalancer node hosts three OnOff UDP applications.
* Each OnOff app sends to one of the three backend servers.
* Traffic is split equally (same rate on every flow) to
* simulate a round-robin / equal-split load balancer.
* A PacketSink on each server receives the traffic.
*
* Outputs
* -------
* • animation.xml – NetAnim topology file
* • flowmon.xml – FlowMonitor statistics
* • throughput.plt – Gnuplot script for throughput vs. time
* • throughput_serverN.dat – per-server raw data files
* ============================================================
*/
#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/netanim-module.h"
#include <fstream>
#include <iomanip>
#include <map>
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("WebServerFarm");
// ============================================================
// Globals for per-server throughput logging
// ============================================================
static const uint32_t NUM_SERVERS = 3;
// PacketSink pointers – filled after app installation
Ptr<PacketSink> g_sinks[NUM_SERVERS];
// Cumulative bytes received at the previous sample (for delta)
uint64_t g_prevRxBytes[NUM_SERVERS] = {0, 0, 0};
// Output streams for .dat files
std::ofstream g_datFiles[NUM_SERVERS];
// Sampling interval (seconds)
static const double SAMPLE_INTERVAL = 0.5;
// ============================================================
// Periodic throughput sampling callback
// ============================================================
void SampleThroughput(double simTime)
{
double now = Simulator::Now().GetSeconds();
if (now > simTime) return; // guard
for (uint32_t i = 0; i < NUM_SERVERS; ++i)
{
uint64_t totalRx = g_sinks[i]->GetTotalRx(); // bytes so far
uint64_t delta = totalRx - g_prevRxBytes[i]; // bytes in last interval
g_prevRxBytes[i] = totalRx;
// Throughput in Mbps
double tput = (delta * 8.0) / (SAMPLE_INTERVAL * 1e6);
g_datFiles[i] << std::fixed << std::setprecision(4)
<< now << "\t" << tput << "\n";
}
// Reschedule
Simulator::Schedule(Seconds(SAMPLE_INTERVAL), &SampleThroughput, simTime);
}
// ============================================================
// Main
// ============================================================
int main(int argc, char *argv[])
{
// 1. Simulation parameters
double simDuration = 10.0; // seconds
uint16_t sinkPort = 9; // well-known discard port
CommandLine cmd(__FILE__);
cmd.AddValue("simDuration", "Simulation duration (s)", simDuration);
cmd.Parse(argc, argv);
Time::SetResolution(Time::NS);
LogComponentEnable("WebServerFarm", LOG_LEVEL_INFO);
// 2. Create nodes
// Node 0 : Client
// Node 1 : LoadBalancer
// Node 2 : Server1
// Node 3 : Server2
// Node 4 : Server3
NS_LOG_INFO("Creating nodes ...");
NodeContainer clientNode;
clientNode.Create(1); // Node 0
NodeContainer lbNode;
lbNode.Create(1); // Node 1
NodeContainer serverNodes;
serverNodes.Create(NUM_SERVERS); // Nodes 2, 3, 4
// 3. Point-to-Point link helper
NS_LOG_INFO("Configuring P2P links ...");
PointToPointHelper p2p;
p2p.SetDeviceAttribute("DataRate", StringValue("10Mbps"));
p2p.SetChannelAttribute("Delay", StringValue("2ms"));
// Client ↔ LoadBalancer
NetDeviceContainer devClientLB = p2p.Install(clientNode.Get(0),
lbNode.Get(0));
// LoadBalancer ↔ each Server
NetDeviceContainer devLBServer[NUM_SERVERS];
for (uint32_t i = 0; i < NUM_SERVERS; ++i)
{
devLBServer[i] = p2p.Install(lbNode.Get(0), serverNodes.Get(i));
}
// 4. Internet stack & IP addressing
NS_LOG_INFO("Installing Internet stack ...");
InternetStackHelper internet;
internet.Install(clientNode);
internet.Install(lbNode);
internet.Install(serverNodes);
Ipv4AddressHelper ipv4;
// 10.1.1.0/30 — Client ↔ LB
ipv4.SetBase("10.1.1.0", "255.255.255.252");
Ipv4InterfaceContainer ifClientLB = ipv4.Assign(devClientLB);
// 10.1.2.0/30 – 10.1.4.0/30 — LB ↔ ServerN
Ipv4InterfaceContainer ifLBServer[NUM_SERVERS];
for (uint32_t i = 0; i < NUM_SERVERS; ++i)
{
std::ostringstream base;
base << "10.1." << (i + 2) << ".0";
ipv4.SetBase(base.str().c_str(), "255.255.255.252");
ifLBServer[i] = ipv4.Assign(devLBServer[i]);
}
// Enable global routing
Ipv4GlobalRoutingHelper::PopulateRoutingTables();
// 5. Applications
NS_LOG_INFO("Installing applications ...");
// --- 5a. PacketSink on every server ---
PacketSinkHelper sinkHelper("ns3::UdpSocketFactory",
InetSocketAddress(Ipv4Address::GetAny(), sinkPort));
ApplicationContainer sinkApps[NUM_SERVERS];
for (uint32_t i = 0; i < NUM_SERVERS; ++i)
{
sinkApps[i] = sinkHelper.Install(serverNodes.Get(i));
sinkApps[i].Start(Seconds(0.5));
sinkApps[i].Stop(Seconds(simDuration));
g_sinks[i] = DynamicCast<PacketSink>(sinkApps[i].Get(0));
}
// --- 5b. OnOff apps on LoadBalancer → each Server ---
for (uint32_t i = 0; i < NUM_SERVERS; ++i)
{
Ipv4Address serverAddr = ifLBServer[i].GetAddress(1);
OnOffHelper onoff("ns3::UdpSocketFactory",
InetSocketAddress(serverAddr, sinkPort));
onoff.SetConstantRate(DataRate("3Mbps"), 1024); // constant-bit-rate
ApplicationContainer app = onoff.Install(lbNode.Get(0));
app.Start(Seconds(1.0 + i * 0.05));
app.Stop(Seconds(simDuration - 0.5));
}
// --- 5c. Optional: Client sends traffic to LB (port 8) ---
{
Ipv4Address lbAddr = ifClientLB.GetAddress(1); // LB side of client link
OnOffHelper clientOnOff("ns3::UdpSocketFactory",
InetSocketAddress(lbAddr, 8));
clientOnOff.SetConstantRate(DataRate("1Mbps"), 512);
PacketSinkHelper lbSink("ns3::UdpSocketFactory",
InetSocketAddress(Ipv4Address::GetAny(), 8));
ApplicationContainer lbSinkApp = lbSink.Install(lbNode.Get(0));
lbSinkApp.Start(Seconds(0.5));
lbSinkApp.Stop(Seconds(simDuration));
ApplicationContainer clientApp = clientOnOff.Install(clientNode.Get(0));
clientApp.Start(Seconds(1.0));
clientApp.Stop(Seconds(simDuration - 0.5));
}
// 6. FlowMonitor – tracks all flows automatically
NS_LOG_INFO("Setting up FlowMonitor ...");
FlowMonitorHelper flowMonHelper;
Ptr<FlowMonitor> flowMon = flowMonHelper.InstallAll();
// 7. NetAnim – topology visualisation
NS_LOG_INFO("Configuring NetAnim ...");
AnimationInterface anim("animation.xml");
anim.SetConstantPosition(clientNode.Get(0), 0.0, 50.0);
anim.SetConstantPosition(lbNode.Get(0), 50.0, 50.0);
anim.SetConstantPosition(serverNodes.Get(0), 100.0, 80.0);
anim.SetConstantPosition(serverNodes.Get(1), 100.0, 50.0);
anim.SetConstantPosition(serverNodes.Get(2), 100.0, 20.0);
anim.UpdateNodeDescription(clientNode.Get(0), "Client");
anim.UpdateNodeDescription(lbNode.Get(0), "LoadBalancer");
anim.UpdateNodeDescription(serverNodes.Get(0), "Server1");
anim.UpdateNodeDescription(serverNodes.Get(1), "Server2");
anim.UpdateNodeDescription(serverNodes.Get(2), "Server3");
anim.UpdateNodeColor(clientNode.Get(0), 0, 0, 255); // Blue
anim.UpdateNodeColor(lbNode.Get(0), 255, 165, 0); // Orange
anim.UpdateNodeColor(serverNodes.Get(0), 0, 200, 0); // Green
anim.UpdateNodeColor(serverNodes.Get(1), 0, 200, 0);
anim.UpdateNodeColor(serverNodes.Get(2), 0, 200, 0);
// 8. Open .dat files and schedule throughput sampling
NS_LOG_INFO("Opening throughput data files ...");
for (uint32_t i = 0; i < NUM_SERVERS; ++i)
{
std::ostringstream fname;
fname << "throughput_server" << (i + 1) << ".dat";
g_datFiles[i].open(fname.str());
g_datFiles[i] << "# Time(s)\tThroughput(Mbps)\n";
}
Simulator::Schedule(Seconds(SAMPLE_INTERVAL), &SampleThroughput, simDuration);
// 9. Run simulation
NS_LOG_INFO("Starting simulation ...");
Simulator::Stop(Seconds(simDuration + 1.0));
Simulator::Run();
// 10. FlowMonitor results
NS_LOG_INFO("\n========== FlowMonitor Statistics ==========");
flowMon->CheckForLostPackets();
Ptr<Ipv4FlowClassifier> classifier =
DynamicCast<Ipv4FlowClassifier>(flowMonHelper.GetClassifier());
std::map<FlowId, FlowMonitor::FlowStats> stats = flowMon->GetFlowStats();
for (auto &kv : stats)
{
Ipv4FlowClassifier::FiveTuple t = classifier->FindFlow(kv.first);
FlowMonitor::FlowStats s = kv.second;
double duration = s.timeLastRxPacket.GetSeconds()
- s.timeFirstTxPacket.GetSeconds();
double tput = (duration > 0)
? (s.rxBytes * 8.0) / (duration * 1e6)
: 0.0;
double meanDelay = (s.rxPackets > 0)
? s.delaySum.GetSeconds() / s.rxPackets * 1000.0
: 0.0;
uint64_t lost = s.txPackets - s.rxPackets;
std::cout << "\n--- Flow " << kv.first << " ---\n"
<< " Src : " << t.sourceAddress << ":" << t.sourcePort << "\n"
<< " Dst : " << t.destinationAddress << ":" << t.destinationPort << "\n"
<< " Proto: " << (uint16_t)t.protocol << " (17=UDP)\n"
<< " TxPkts : " << s.txPackets << "\n"
<< " RxPkts : " << s.rxPackets << "\n"
<< " LostPkts : " << lost << "\n"
<< " Throughput : " << std::fixed << std::setprecision(4)
<< tput << " Mbps\n"
<< " Mean Delay : " << std::fixed << std::setprecision(4)
<< meanDelay << " ms\n";
}
// Save FlowMonitor XML
flowMon->SerializeToXmlFile("flowmon.xml", true, true);
NS_LOG_INFO("\nflowmon.xml saved.");
// 11. Close .dat files and write gnuplot script
for (uint32_t i = 0; i < NUM_SERVERS; ++i)
{
g_datFiles[i].close();
}
{
std::ofstream plt("throughput.plt");
plt << "# Gnuplot script — Throughput vs. Time\n"
<< "# Run: gnuplot throughput.plt\n\n"
<< "set terminal pngcairo size 900,500 enhanced font 'Helvetica,12'\n"
<< "set output 'throughput.png'\n\n"
<< "set title 'Web-Server Farm: Per-Server Throughput vs. Time\\n"
<< "24BPS1021 — P2P Load Balancing Simulation'\n"
<< "set xlabel 'Time (s)'\n"
<< "set ylabel 'Throughput (Mbps)'\n"
<< "set xrange [0:" << simDuration << "]\n"
<< "set yrange [0:5]\n"
<< "set grid\n"
<< "set key top right\n"
<< "set style data linespoints\n\n"
<< "plot 'throughput_server1.dat' using 1:2 title 'Server1' lc rgb '#e74c3c' lw 2 pt 7,\\\n"
<< " 'throughput_server2.dat' using 1:2 title 'Server2' lc rgb '#2ecc71' lw 2 pt 5,\\\n"
<< " 'throughput_server3.dat' using 1:2 title 'Server3' lc rgb '#3498db' lw 2 pt 9\n";
plt.close();
}
NS_LOG_INFO("throughput.plt written.");
NS_LOG_INFO("animation.xml written.");
NS_LOG_INFO("\nAll done! Run: gnuplot throughput.plt → throughput.png");
Simulator::Destroy();
return 0;
}
Comments
Post a Comment