Skip to main content

Web Server Farm Simulation with Load Balancing Using P2P Links

Web-Server Farm Simulation with Load Balancing Using P2P Links

Prompt used:

Using NS-3 (C++), write a complete simulation file named farm.cc to be placed in the scratch/ 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 UdpClientServerHelper or OnOffApplication + PacketSink on 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 .plt file 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;
}

GNUPLOT GRAPH:

Gnuplot Web-Server Farm Throughput Graph

NETANIM VISUALIZATION:

NetAnim Web-Server Farm Topology

FLOWMONITOR RESULT:

FlowMonitor Console Output

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.