This session sends UDP traffic across the wireless network from Session 4 and records what arrives, both as a plot of bytes over time and as a pcap trace of the receiver’s Wi-Fi interface.
Objectives
Do not copy. Read for understanding and the viva- Complete questions 10 to 12 of the manual: udp over wi-fi with tracing and pcap
- Prepare the deliverable before the lab and finish it during the session
- Be ready to explain every step in the viva
Questions Covered
Do not copy. Read for understanding and the viva| Question | Requirement | Status |
|---|---|---|
| Q10 | Create a UDP client on a node n1 and a UDP server on a node n2 | Complete |
| Q11 | Send packets to node n2 from node n1 and plot the number of bytes received with… | Complete |
| Q12 | Show the pcap traces at node n2’s Wi-Fi interface | Complete |
Preparation
Do not copy. Read for understanding and the viva- Attach a trace to the sink’s
Rxcallback and accumulate bytes per second into a file; plot with gnuplot or matplotlib. YansWifiPhyHelper::EnablePcap("node2", devices.Get(1))writes the Wi-Fi pcap for node n2 only.- In Wireshark, filter on
udpand use Statistics, IO Graph to cross-check your plot.
Question 10
Problem Statement
Write in lab recordCreate a UDP client on a node n1 and a UDP server on a node n2.
Solution
Write in lab recordQuestions 10, 11 and 12 share one program, wifi_udp_trace.cc: question 10 is the network and the two applications, question 11 the Rx trace and the plot, question 12 the pcap. The code indices 0 and 1 are the manual’s n1 and n2. Positions are fixed 50 m apart so the plot is repeatable; the OLSR routing from Session 4 stays in place.
Steps
- Save the program as
scratch/wifi_udp_trace.ccand run./ns3 run scratch/wifi_udp_trace. - Check the last two console lines: 900 packets received, 0 lost, 921600 bytes.
- Rate check with the formula sheet: 1024 bytes every 10 ms is , well under the 11 Mbit/s channel.
- Try
--interval=1(1 ms, 8.19 Mbit/s) and watchlostbecome non-zero as the channel saturates.
Program
/*
* MCSL-223 Section 1, Session 5, Questions 10, 11 and 12
* UDP client on n1, UDP server on n2 over the Session 4 ad-hoc Wi-Fi.
* Writes bytes-received-versus-time to rx-bytes.dat and a pcap of n2's
* Wi-Fi interface.
*
* Build: copy to ns-3.36+/scratch/ and run
* ./ns3 run scratch/wifi_udp_trace
* gnuplot rx_bytes.plt (plot)
* wireshark wifi-udp-1-0.pcap (n2 trace)
*
* n1 (10.1.1.1, UdpClient) ---- 50 m ---- n2 (10.1.1.2, UdpServer port 9)
* Node index 0 and 1 in the code = n1 and n2 in the manual.
* Positions are fixed so the plot is repeatable; the Session 4 OLSR
* setup is kept so a third node could forward if added.
*/
#include "ns3/core-module.h"
#include "ns3/network-module.h"
#include "ns3/internet-module.h"
#include "ns3/wifi-module.h"
#include "ns3/mobility-module.h"
#include "ns3/olsr-module.h"
#include "ns3/applications-module.h"
#include <fstream>
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("WifiUdpTrace");
static uint64_t g_rxBytes = 0; // bytes received at n2 so far
static std::ofstream g_traceFile; // rx-bytes.dat
/*
* RxTrace: called by the UdpServer "Rx" trace source for every packet
* that reaches the application on n2. Adds its size to the running total.
*/
static void
RxTrace(Ptr<const Packet> packet)
{
g_rxBytes += packet->GetSize();
}
/*
* SampleRx: writes "time cumulative_bytes" once per sample interval so
* gnuplot can draw bytes received against time.
*/
static void
SampleRx(double interval)
{
g_traceFile << Simulator::Now().GetSeconds() << "\t" << g_rxBytes << std::endl;
Simulator::Schedule(Seconds(interval), &SampleRx, interval);
}
/*
* main: builds the two-node ad-hoc Wi-Fi link (Q10), installs UdpServer on
* n2 and UdpClient on n1 at 1024 B every 10 ms, records bytes over time
* (Q11) and a pcap on n2's Wi-Fi device (Q12).
*/
int
main(int argc, char* argv[])
{
double simTime = 10.0;
uint32_t packetSize = 1024;
double intervalMs = 10.0; // 1024 B / 10 ms = 819.2 kbit/s
double sample = 0.5; // trace sample period in seconds
double distance = 50.0;
CommandLine cmd(__FILE__);
cmd.AddValue("simTime", "Simulation length in seconds", simTime);
cmd.AddValue("packetSize", "UDP payload in bytes", packetSize);
cmd.AddValue("interval", "Client send interval in ms", intervalMs);
cmd.AddValue("distance", "Distance between n1 and n2 in metres", distance);
cmd.Parse(argc, argv);
LogComponentEnable("UdpServer", LOG_LEVEL_INFO);
// ---- Q10: two nodes on an ad-hoc Wi-Fi channel ----
NodeContainer nodes;
nodes.Create(2);
YansWifiChannelHelper channel = YansWifiChannelHelper::Default();
YansWifiPhyHelper phy;
phy.SetChannel(channel.Create());
WifiHelper wifi;
wifi.SetStandard(WIFI_STANDARD_80211b);
wifi.SetRemoteStationManager("ns3::ConstantRateWifiManager",
"DataMode", StringValue("DsssRate11Mbps"),
"ControlMode", StringValue("DsssRate11Mbps"));
WifiMacHelper mac;
mac.SetType("ns3::AdhocWifiMac");
NetDeviceContainer devices = wifi.Install(phy, mac, nodes);
// Fixed positions: n1 at the origin, n2 `distance` metres along x.
Ptr<ListPositionAllocator> positions = CreateObject<ListPositionAllocator>();
positions->Add(Vector(0.0, 0.0, 0.0));
positions->Add(Vector(distance, 0.0, 0.0));
MobilityHelper mobility;
mobility.SetPositionAllocator(positions);
mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
mobility.Install(nodes);
OlsrHelper olsr;
InternetStackHelper stack;
stack.SetRoutingHelper(olsr);
stack.Install(nodes);
Ipv4AddressHelper address;
address.SetBase("10.1.1.0", "255.255.255.0");
Ipv4InterfaceContainer interfaces = address.Assign(devices);
// UDP server on n2 (port 9) and UDP client on n1.
uint16_t port = 9;
UdpServerHelper server(port);
ApplicationContainer serverApp = server.Install(nodes.Get(1));
serverApp.Start(Seconds(0.5));
serverApp.Stop(Seconds(simTime));
UdpClientHelper client(interfaces.GetAddress(1), port);
client.SetAttribute("MaxPackets", UintegerValue(4294967295u)); // until Stop
client.SetAttribute("Interval", TimeValue(MicroSeconds(static_cast<uint64_t>(intervalMs * 1000))));
client.SetAttribute("PacketSize", UintegerValue(packetSize));
ApplicationContainer clientApp = client.Install(nodes.Get(0));
clientApp.Start(Seconds(1.0));
clientApp.Stop(Seconds(simTime));
// ---- Q11: bytes received versus time ----
Ptr<UdpServer> udpServer = DynamicCast<UdpServer>(serverApp.Get(0));
udpServer->TraceConnectWithoutContext("Rx", MakeCallback(&RxTrace));
g_traceFile.open("rx-bytes.dat");
g_traceFile << "# time(s)\tbytes_received_at_n2" << std::endl;
Simulator::Schedule(Seconds(0.0), &SampleRx, sample);
// ---- Q12: pcap on n2's Wi-Fi interface only -> wifi-udp-1-0.pcap ----
phy.SetPcapDataLinkType(WifiPhyHelper::DLT_IEEE802_11_RADIO);
phy.EnablePcap("wifi-udp", devices.Get(1));
Simulator::Stop(Seconds(simTime));
Simulator::Run();
std::cout << "Packets received at n2: " << udpServer->GetReceived()
<< ", lost: " << udpServer->GetLost() << std::endl;
std::cout << "Bytes received at n2 : " << g_rxBytes << " in " << simTime - 1.0
<< " s = " << g_rxBytes * 8.0 / (simTime - 1.0) / 1e3 << " kbit/s" << std::endl;
g_traceFile.close();
Simulator::Destroy();
return 0;
}Output
Expected console output. UdpServer logs one line per packet; the delay per packet is the Wi-Fi hop delay computed in Session 4 (about 1.3 ms for a 1024-byte frame), so time stamps are illustrative.
TraceDelay: RX 1024 bytes from 10.1.1.1 Sequence Number: 0 Uid: 6 TXtime: +1s RXtime: +1.00134s Delay: +0.00134s
TraceDelay: RX 1024 bytes from 10.1.1.1 Sequence Number: 1 Uid: 8 TXtime: +1.01s RXtime: +1.01134s Delay: +0.00134s
TraceDelay: RX 1024 bytes from 10.1.1.1 Sequence Number: 2 Uid: 10 TXtime: +1.02s RXtime: +1.02134s Delay: +0.00134s
...
TraceDelay: RX 1024 bytes from 10.1.1.1 Sequence Number: 899 Uid: 1804 TXtime: +9.99s RXtime: +9.99134s Delay: +0.00134s
Packets received at n2: 900, lost: 0
Bytes received at n2 : 921600 in 9 s = 819.2 kbit/sExplanation
UdpServerHelper(9) installs a UdpServer that binds UDP port 9, counts packets by the sequence number UdpClient puts in a 12-byte SeqTsHeader, and reports lost ones from gaps in the sequence. UdpClientHelper on n1 sends PacketSize bytes every Interval; MaxPackets is set to the maximum so the Stop time ends the flow. Unlike the echo pair of Session 1 this is one-way traffic, which is what the plot in question 11 needs. The two nodes are one hop apart, so OLSR only fills in the direct route.
Question 11
Problem Statement
Write in lab recordSend packets to node n2 from node n1 and plot the number of bytes received with respect to time at node n2.
Solution
Write in lab recordSteps
- In the program,
RxTraceis connected to the server’sRxtrace source withTraceConnectWithoutContext; it adds each packet size to a counter. SampleRxruns every 0.5 s and appendstime bytestorx-bytes.dat.- After the run, plot with
gnuplot rx_bytes.plt; it writesrx-bytes.png. - Paste the plot in the record with the axis labels and the two numbers it must agree with: 0 bytes until 1 s, 921600 bytes at 10 s.
Program
The trace part of wifi_udp_trace.cc:
static void RxTrace(Ptr<const Packet> packet) { g_rxBytes += packet->GetSize(); }
static void SampleRx(double interval)
{
g_traceFile << Simulator::Now().GetSeconds() << "\t" << g_rxBytes << std::endl;
Simulator::Schedule(Seconds(interval), &SampleRx, interval);
}
Ptr<UdpServer> udpServer = DynamicCast<UdpServer>(serverApp.Get(0));
udpServer->TraceConnectWithoutContext("Rx", MakeCallback(&RxTrace));The gnuplot script:
# gnuplot script: bytes received at n2 against time
# usage: gnuplot rx_bytes.plt (reads rx-bytes.dat written by wifi_udp_trace)
set terminal pngcairo size 800,500
set output "rx-bytes.png"
set title "UDP bytes received at n2 (Session 5)"
set xlabel "Time (s)"
set ylabel "Cumulative bytes received"
set grid
set key left top
plot "rx-bytes.dat" using 1:2 with linespoints lw 2 pt 7 title "bytes at n2"Output
Expected rx-bytes.dat (every packet arrives 1.34 ms after it is sent, so the sample at each half second counts 50 packets more than the previous one; the sample at 10 s is not written because the simulator stops first):
# time(s) bytes_received_at_n2
0 0
0.5 0
1 0
1.5 51200
2 102400
2.5 153600
3 204800
3.5 256000
4 307200
4.5 358400
5 409600
5.5 460800
6 512000
6.5 563200
7 614400
7.5 665600
8 716800
8.5 768000
9 819200
9.5 870400The plot is a straight line from (1 s, 0) to (10 s, 921600) with slope , the offered rate. A flat section would mean the channel was busy or the nodes moved out of range.
Explanation
NS-3 tracing separates the event source from the consumer. UdpServer fires its Rx trace source for every packet it receives; the script attaches a plain C++ function to it with TraceConnectWithoutContext (the same mechanism Config::ConnectWithoutContext uses with a path string, as in Session 8 for the congestion window). Sampling on a timer instead of writing per packet keeps the file small and gives evenly spaced points, which gnuplot draws directly with using 1:2. The throughput formula from the sheet is the slope of this line: bytes received divided by elapsed time, times 8.
Question 12
Problem Statement
Write in lab recordShow the pcap traces at node n2’s Wi-Fi interface.
Solution
Write in lab recordSteps
- The program calls
phy.EnablePcap("wifi-udp", devices.Get(1)), which captures only n2’s Wi-Fi device and writeswifi-udp-1-0.pcap(node 1, device 0). SetPcapDataLinkType(WifiPhyHelper::DLT_IEEE802_11_RADIO)adds a radiotap header so Wireshark shows the data rate and signal strength of every frame.- Open it:
wireshark wifi-udp-1-0.pcap. - Apply the filter
udp.dstport == 9to see only the client’s datagrams;wlan.fc.type_subtype == 0x1dshows the 802.11 ACKs n2 sent back;olsrshows the routing traffic. - Statistics, I/O Graph with the filter
udp.dstport == 9and Y axis set to Bytes reproduces the plot from question 11 straight from the capture.
Output
Expected Wireshark packet list for wifi-udp-1-0.pcap with filter udp.dstport == 9 (900 frames displayed):
No. Time Source Destination Protocol Length Info
12 1.001340 10.1.1.1 10.1.1.2 UDP 1112 49153 → 9 Len=1024
14 1.011340 10.1.1.1 10.1.1.2 UDP 1112 49153 → 9 Len=1024
16 1.021340 10.1.1.1 10.1.1.2 UDP 1112 49153 → 9 Len=1024
...
1810 9.991340 10.1.1.1 10.1.1.2 UDP 1112 49153 → 9 Len=1024Frame length 1112 is 1024 payload + 8 UDP + 20 IP + 8 LLC/SNAP + 24 802.11 header + 4 FCS = 1088 bytes on air, plus the radiotap header NS-3 writes (24 bytes here; the exact size varies by version). Without the filter the list also contains OLSR HELLO messages every 2 s from both nodes and the 802.11 ACK n2 returns after each data frame. Wireshark’s Statistics, Capture File Properties shows about 921600 bytes of UDP payload and an average of 100 packets per second.
Explanation
YansWifiPhyHelper::EnablePcap hooks the phy’s MonitorSnifferRx and MonitorSnifferTx trace sources on the given device only, so the file holds every frame n2’s radio sent or received, including frames that were not for it. That is the wireless equivalent of a network tap and is why the manual asks for the trace at n2 rather than n1: it shows what arrived, not what was sent. The radiotap link type is worth enabling because the plain 802.11 format drops the rate and signal fields. The three views from this session agree with each other: the server’s packet count, the trace file’s slope and the pcap byte total are the same 921600 bytes measured at the application, the trace and the radio.
Viva Questions
Do not copy. Read for understanding and the viva- Q: What is the difference between
UdpClientandUdpEchoClient? A: UdpClient sends one way with a sequence and time stamp header; UdpEchoClient expects a reply. - Q: How does
UdpServerknow a packet was lost? A: From a gap in the sequence numbers carried in theSeqTsHeader. - Q: What is a trace source and a trace sink? A: The source is the event NS-3 fires, the sink is the function you attach with
TraceConnect. - Q: Why sample every 0.5 s instead of logging each packet? A: Fewer, evenly spaced points; the plot is the same line.
- Q: What does the slope of the bytes-versus-time line mean? A: Throughput in bytes per second; times 8 gives bit/s.
- Q: Why does the pcap file name contain 1-0? A: Node index 1 (n2), device index 0 on that node.
- Q: What does the radiotap header add? A: Per-frame data rate, channel and signal strength for Wireshark to display.
- Q: Why do OLSR packets appear in the trace when only UDP data was sent? A: OLSR broadcasts HELLO and TC messages periodically regardless of data traffic.
Common Mistakes
Do not copy. Read for understanding and the viva- Connecting the
Rxtrace before the server application exists, or with a wrongConfigpath, so the counter stays at zero. - Passing a fraction to
MilliSeconds; it truncates to an integer, so 0.5 becomes 0 ms and the client floods the channel. UseMicroSeconds. - Leaving
MaxPacketsat its default of 100, so the flow stops after one second and the plot goes flat. - Calling
EnablePcapondevices(all nodes) and then opening n1’s file to show n2’s interface. - Forgetting the
#header line in the data file and getting a parse error from gnuplot on the label row. - Reporting the pcap byte count (with headers) as the received bytes instead of the application count.
Formula Sheet
Do not copy. Read for understanding and the vivaThroughput
Bytes received at the sink divided by the time the flow was active, converted to bits per second:
Bandwidth-delay product
The amount of data in flight on a link of capacity and round-trip time . A TCP window smaller than this cannot fill the link:
For the Session 10 defaults, and one-way delay give and .
Transmission and propagation delay
where is packet size in bits, link rate, distance and signal speed (about in copper or fibre).
Constant bit rate traffic
An OnOff application with packet size bytes and rate bit/s sends one packet every
TCP congestion window
Slow start doubles the window every RTT until the threshold; congestion avoidance adds one segment per RTT; a loss halves it (TCP NewReno):
The maximum throughput of one TCP flow is bounded by
Packet loss
Session Summary
Write in lab recordwifi_udp_trace.cclisting, theUdpServerlog and the final packet and byte countsrx-bytes.dat,rx_bytes.pltand the bytes-versus-time plot with its slope- Wireshark view of
wifi-udp-1-0.pcapfiltered onudp.dstport == 9and the I/O Graph