// n0 ---------- n1 ---------- n2 ---------- n3
// 10 Mbps 10 Mbps 10 Mbps
// 1 ms 1 ms 1 ms
//
// - TCP flow from n0 to n3 using BulkSendApplication.
// - The following simulation output is stored in results/ in ns-3 top-level directory:
// - cwnd traces are stored in cwndTraces folder
// - queue length statistics are stored in queue-size.dat file
// - pcaps are stored in pcap folder
// - queueTraces folder contain the drop statistics at queue
// - queueStats.txt file contains the queue stats and config.txt file contains
// the simulation configuration.
// - The cwnd and queue length traces obtained from this example were tested against
// the respective traces obtained from Linux Reno by using ns-3 Direct Code Execution.
// See internet/doc/tcp.rst for more details.
#include <iostream>
#include <string>
#include <fstream>
#include <sys/stat.h>
#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/traffic-control-module.h"
#include "ns3/flow-monitor-module.h"
using namespace ns3;
std::string dir = "results/7/";
Time stopTime = Seconds (50);
uint32_t segmentSize = 524;
// Function to trace change in cwnd at n0
static void
CwndChange (uint32_t oldCwnd, uint32_t newCwnd)
{
std::ofstream fPlotQueue (dir + "cwndTraces/n7.dat", std::ios::out | std::ios::app);
fPlotQueue << Simulator::Now ().GetSeconds () << " " << newCwnd / segmentSize << std::endl;
fPlotQueue.close ();
}
// Trace Function for cwnd
void
TraceCwnd (uint32_t node, uint32_t cwndWindow,
Callback <void, uint32_t, uint32_t> CwndTrace)
{
Config::ConnectWithoutContext ("/NodeList/" + std::to_string (node) + "/$ns3::TcpL4Protocol/SocketList/" + std::to_string (cwndWindow) + "/CongestionWindow", CwndTrace);
}
// Function to install BulkSend application
void InstallBulkSend (Ptr<Node> node, Ipv4Address address, uint16_t port, std::string socketFactory,
uint32_t nodeId, uint32_t cwndWindow,
Callback <void, uint32_t, uint32_t> CwndTrace)
{
BulkSendHelper source (socketFactory, InetSocketAddress (address, port));
source.SetAttribute ("MaxBytes", UintegerValue (0));
ApplicationContainer sourceApps = source.Install (node);
sourceApps.Start (Seconds (0.0));
Simulator::Schedule (Seconds (0.0) + Seconds (0.001), &TraceCwnd, nodeId, cwndWindow, CwndTrace);
sourceApps.Stop (stopTime);
}
// Function to install sink application
void InstallPacketSink (Ptr<Node> node, uint16_t port, std::string socketFactory)
{
PacketSinkHelper sink (socketFactory, InetSocketAddress (Ipv4Address::GetAny (), port));
ApplicationContainer sinkApps = sink.Install (node);
sinkApps.Start (Seconds (0.0));
sinkApps.Stop (stopTime);
}
int main (int argc, char *argv[])
{
uint32_t stream = 1;
std::string socketFactory = "ns3::TcpSocketFactory";
std::string tcpTypeId = "ns3::TcpBbr";
std::string qdiscTypeId = "ns3::FifoQueueDisc";
bool isSack = true;
uint32_t delAckCount = 1;
std::string recovery = "ns3::TcpClassicRecovery";
// 启用日志组件,以便打印 INFO 级别的日志
//LogComponentEnable("TcpLargeTransfer", LOG_LEVEL_INFO);
CommandLine cmd;
cmd.AddValue ("tcpTypeId", "TCP variant to use (e.g., ns3::TcpNewReno, ns3::TcpLinuxReno, etc.)", tcpTypeId);
cmd.AddValue ("qdiscTypeId", "Queue disc for gateway (e.g., ns3::CoDelQueueDisc)", qdiscTypeId);
cmd.AddValue ("segmentSize", "TCP segment size (bytes)", segmentSize);
cmd.AddValue ("delAckCount", "Delayed ack count", delAckCount);
cmd.AddValue ("enableSack", "Flag to enable/disable sack in TCP", isSack);
cmd.AddValue ("stopTime", "Stop time for applications / simulation time will be stopTime", stopTime);
cmd.AddValue ("recovery", "Recovery algorithm type to use (e.g., ns3::TcpPrrRecovery", recovery);
cmd.Parse (argc, argv);
TypeId qdTid;
NS_ABORT_MSG_UNLESS (TypeId::LookupByNameFailSafe (qdiscTypeId, &qdTid), "TypeId " << qdiscTypeId << " not found");
// Set recovery algorithm and TCP variant
Config::SetDefault ("ns3::TcpL4Protocol::RecoveryType", TypeIdValue (TypeId::LookupByName (recovery)));
if (tcpTypeId.compare ("ns3::TcpWestwoodPlus") == 0)
{
// TcpWestwoodPlus is not an actual TypeId name; we need TcpWestwood here
Config::SetDefault ("ns3::TcpL4Protocol::SocketType", TypeIdValue (TcpWestwood::GetTypeId ()));
// the default protocol type in ns3::TcpWestwood is WESTWOOD
Config::SetDefault ("ns3::TcpWestwood::ProtocolType", EnumValue (TcpWestwood::WESTWOODPLUS));
}
else
{
TypeId tcpTid;
NS_ABORT_MSG_UNLESS (TypeId::LookupByNameFailSafe (tcpTypeId, &tcpTid), "TypeId " << tcpTypeId << " not found");
Config::SetDefault ("ns3::TcpL4Protocol::SocketType", TypeIdValue (TypeId::LookupByName (tcpTypeId)));
}
// Create nodes
NodeContainer leftNodes, rightNodes, routers;
routers.Create (2);
leftNodes.Create (1);
rightNodes.Create (1);
std::vector <NetDeviceContainer> leftToRouter;
std::vector <NetDeviceContainer> routerToRight;
// Create the point-to-point link helpers and connect two router nodes
PointToPointHelper pointToPointRouter;
pointToPointRouter.SetDeviceAttribute ("DataRate", StringValue ("10Mbps"));
pointToPointRouter.SetChannelAttribute ("Delay", StringValue ("1ms"));
NetDeviceContainer r1r2ND = pointToPointRouter.Install (routers.Get (0), routers.Get (1));
// Create the point-to-point link helpers and connect leaf nodes to router
PointToPointHelper pointToPointLeaf;
pointToPointLeaf.SetDeviceAttribute ("DataRate", StringValue ("10Mbps"));
pointToPointLeaf.SetChannelAttribute ("Delay", StringValue ("1ms"));
leftToRouter.push_back (pointToPointLeaf.Install (leftNodes.Get (0), routers.Get (0)));
routerToRight.push_back (pointToPointLeaf.Install (routers.Get (1), rightNodes.Get (0)));
InternetStackHelper internetStack;
internetStack.Install (leftNodes);
internetStack.Install (rightNodes);
internetStack.Install (routers);
// Assign IP addresses to all the network devices
Ipv4AddressHelper ipAddresses ("10.0.0.0", "255.255.255.0");
Ipv4InterfaceContainer r1r2IPAddress = ipAddresses.Assign (r1r2ND);
ipAddresses.NewNetwork ();
std::vector <Ipv4InterfaceContainer> leftToRouterIPAddress;
leftToRouterIPAddress.push_back (ipAddresses.Assign (leftToRouter [0]));
ipAddresses.NewNetwork ();
std::vector <Ipv4InterfaceContainer> routerToRightIPAddress;
routerToRightIPAddress.push_back (ipAddresses.Assign (routerToRight [0]));
Ipv4GlobalRoutingHelper::PopulateRoutingTables ();
// Set default sender and receiver buffer size as 1MB
Config::SetDefault ("ns3::TcpSocket::SndBufSize", UintegerValue (1 << 20));
Config::SetDefault ("ns3::TcpSocket::RcvBufSize", UintegerValue (1 << 20));
// Set default initial congestion window as 10 segments
Config::SetDefault ("ns3::TcpSocket::InitialCwnd", UintegerValue (10));
// Set default delayed ack count to a specified value
Config::SetDefault ("ns3::TcpSocket::DelAckCount", UintegerValue (delAckCount));
// Set default segment size of TCP packet to a specified value
Config::SetDefault ("ns3::TcpSocket::SegmentSize", UintegerValue (segmentSize));
// Enable/Disable SACK in TCP
Config::SetDefault ("ns3::TcpSocketBase::Sack", BooleanValue (isSack));
// Create directories to store dat files
struct stat buffer;
[[maybe_unused]] int retVal;
if ((stat (dir.c_str (), &buffer)) == 0)
{
std::string dirToRemove = "rm -rf " + dir;
retVal = system (dirToRemove.c_str ());
NS_ASSERT_MSG (retVal == 0, "Error in return value");
}
std::string dirToSave = "mkdir -p " + dir;
retVal = system (dirToSave.c_str ());
NS_ASSERT_MSG (retVal == 0, "Error in return value");
retVal = system ((dirToSave + "/pcap/").c_str ());
NS_ASSERT_MSG (retVal == 0, "Error in return value");
retVal = system ((dirToSave + "/queueTraces/").c_str ());
NS_ASSERT_MSG (retVal == 0, "Error in return value");
retVal = system ((dirToSave + "/cwndTraces/").c_str ());
NS_ASSERT_MSG (retVal == 0, "Error in return value");
// Set default parameters for queue discipline
Config::SetDefault (qdiscTypeId + "::MaxSize", QueueSizeValue (QueueSize ("100p")));
// 在 main 函数中添加以下代码来设置 BurstErrorModel
// 创建一个 BurstErrorModel 对象
Ptr<BurstErrorModel> burstErrorModel = CreateObject<BurstErrorModel>();
// 设置 BurstErrorModel 的 ErrorRate 属性,即错误发生的频率
burstErrorModel->SetAttribute ("ErrorRate", DoubleValue (0.5)); // 例如,设置为10%的错误率
// 创建一个 RandomVariableStream 对象来设置 BurstSize,即每次错误爆发时丢包的数量
Ptr<UniformRandomVariable> burstSizeVariable = CreateObject<UniformRandomVariable>();
burstSizeVariable->SetAttribute ("Min", DoubleValue(5)); // 突发开始时的最小丢包数
burstSizeVariable->SetAttribute ("Max", DoubleValue(15)); // 突发开始时的最大丢包数
// 将 BurstSize 属性设置为我们创建的 RandomVariableStream 对象
burstErrorModel->SetAttribute ("BurstSize", PointerValue(burstSizeVariable));
// 将 BurstErrorModel 应用到指定的 NetDevice 上,这里以 router 1 为例
Ptr<NetDevice> device = routers.Get (0)->GetDevice (0);
device->SetAttribute ("ReceiveErrorModel", PointerValue(burstErrorModel));
// Install queue discipline on router
TrafficControlHelper tch;
tch.SetRootQueueDisc (qdiscTypeId);
QueueDiscContainer qd;
tch.Uninstall (routers.Get (0)->GetDevice (0));
qd.Add (tch.Install (routers.Get (0)->GetDevice (0)).Get (0));
// Enable BQL
tch.SetQueueLimits ("ns3::DynamicQueueLimits");
AsciiTraceHelper asciiTraceHelper;
Ptr<OutputStreamWrapper> streamWrapper;
// Install packet sink at receiver side
uint16_t port = 50000;
InstallPacketSink (rightNodes.Get (0), port, "ns3::TcpSocketFactory");
// Install BulkSend application
InstallBulkSend (leftNodes.Get (0), routerToRightIPAddress [0].GetAddress (1), port, socketFactory, 2, 0, MakeCallback (&CwndChange));
// Enable PCAP on all the point to point interfaces
pointToPointLeaf.EnablePcapAll (dir + "pcap/ns-3", true);
// Install the FlowMonitor
Ptr<FlowMonitor> flowMonitor;
FlowMonitorHelper flowHelper;
flowMonitor = flowHelper.InstallAll();
Simulator::Stop (stopTime);
// 打印模拟开始前的当前模拟时间
//NS_LOG_INFO ("Simulator start at " << Simulator::Now ().GetSeconds () << " seconds");
Simulator::Run ();
// 打印模拟运行期间的某个时间点(例如,模拟开始后100秒)
//NS_LOG_INFO ("Simulator running at " << Simulator::Now ().GetSeconds () << " seconds");
// Print throughput
flowMonitor->CheckForLostPackets();
Ptr<Ipv4FlowClassifier> classifier = DynamicCast<Ipv4FlowClassifier> (flowHelper.GetClassifier());
FlowMonitor::FlowStatsContainer stats = flowMonitor->GetFlowStats();
for (FlowId flowId = 1; flowId <= stats.size(); ++flowId)
{
FlowMonitor::FlowStats flow = stats[flowId];
Ipv4FlowClassifier::FiveTuple t = classifier->FindFlow (flowId);
std::cout << "Flow " << flowId << " (" << t.sourceAddress << " -> " << t.destinationAddress << ")\n";
std::cout << " Tx Bytes: " << flow.txBytes << " Rx Bytes: " << flow.rxBytes << "\n";
std::cout << " Throughput: " << flow.rxBytes * 8.0 / (stopTime.GetSeconds()) / 1024 / 1024 << " Mbps\n";
}
// Store queue stats in a file
std::ofstream myfile;
myfile.open (dir + "queueStats.txt", std::fstream::in | std::fstream::out | std::fstream::app);
myfile << std::endl;
myfile << "Stat for Queue 1";
myfile << qd.Get (0)->GetStats ();
myfile.close ();
// Store configuration of the simulation in a file
myfile.open (dir + "config.txt", std::fstream::in | std::fstream::out | std::fstream::app);
myfile << "qdiscTypeId " << qdiscTypeId << "\n";
myfile << "stream " << stream << "\n";
myfile << "segmentSize " << segmentSize << "\n";
myfile << "delAckCount " << delAckCount << "\n";
myfile << "stopTime " << stopTime.As (Time::S) << "\n";
myfile.close ();
Simulator::Destroy ();
return 0;
}
Flow 1 (10.0.1.1 -> 10.0.2.2)
Tx Bytes: 280 Rx Bytes: 280
Throughput: 4.27246e-05 Mbps
Flow 2 (10.0.2.2 -> 10.0.1.1)
Tx Bytes: 448 Rx Bytes: 0
Throughput: 0 Mbps