欢迎光临
我们一直在努力

ns-3仿真之应用层(二)

目录

2.3.2 贪婪模型:TCP分组产生器BulkSendApplication

BulkSendApplication常用的专属属性:

~examples/tcp/tcp-bulk-send.cc(见ns-3仿真之应用层(一))

~examples/tcp/tcp-variants-comparison.cc

程序功能和执行逻辑

2.3.3 ON/OFF模型:分组产生器OnOffApplication

ON/OFF模型有两种状态:

OnOffApplication专属属性

~example/routing/simple-global-rputing.cc(见ns-3仿真之应用层(一))

~examples/tcp/tcp-star-server.cc

程序功能和执行逻辑

~examples/wireless/wifi-ap.cc

程序功能和执行逻辑

~src/csma/examples/csma-packet-socket.cc(见ns-3仿真之应用层(一))


2.3.2 贪婪模型:TCP分组产生器BulkSendApplication

BulkSendApplication是专门部署在客户端的应用层协议,仅支持TCP,助手类是BulkSendHelper

BulkSendApplication是一个使用贪婪模型的分组产生器,会尽可能地发送分组以获取最大吞吐量。当一个TCP连接建立好时,BulkSendApplication开始持续地向TCP发送分组,直至TCP发送队列满或到达应用层最大发送字节数为止。对于前一种情况,每当TCP的发送队列有至少一个分组大小的空闲空间时,BulkSendApplication就重新开始发送分组。

  • BulkSendApplication常用的专属属性:

        SendSize:发送分组负载大小,单位B

        MaxBytes:能够发送的最大字总节数。默认值为0,即没有发送字节数上限

  • ~examples/tcp/tcp-bulk-send.cc(见ns-3仿真之应用层(一))
  • ~examples/tcp/tcp-variants-comparison.cc

/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2013 ResiliNets, ITTC, University of Kansas
*
* 用于比较不同 TCP 拥塞控制算法性能的模拟程序,支持多种 TCP 变体,并提供了详细的性能追踪功能
*
* ICST SIMUTools Workshop on ns-3 (WNS3), Cannes, France, March 2013
*/

#include <iostream>
#include <fstream>
#include <string>

#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/error-model.h"
#include "ns3/tcp-header.h"
#include "ns3/udp-header.h"
#include "ns3/enum.h"
#include "ns3/event-id.h"
#include "ns3/flow-monitor-helper.h"
#include "ns3/ipv4-global-routing-helper.h"
#include "ns3/traffic-control-module.h"

using namespace ns3;

NS_LOG_COMPONENT_DEFINE ("TcpVariantsComparison");

//状态追踪变量
static bool firstCwnd = true;
static bool firstSshThr = true;
static bool firstRtt = true;
static bool firstRto = true;

//输出流包装器:用于将不同指标的追踪数据写入文件
static Ptr<OutputStreamWrapper> cWndStream;
static Ptr<OutputStreamWrapper> ssThreshStream;
static Ptr<OutputStreamWrapper> rttStream;
static Ptr<OutputStreamWrapper> rtoStream;
static Ptr<OutputStreamWrapper> nextTxStream;
static Ptr<OutputStreamWrapper> nextRxStream;
static Ptr<OutputStreamWrapper> inFlightStream;
static uint32_t cWndValue;
static uint32_t ssThreshValue;

//拥塞窗口追踪器:追踪 TCP 拥塞窗口的变化,记录时间戳和窗口大小
static void
CwndTracer (uint32_t oldval, uint32_t newval)
{
if (firstCwnd)
{
*cWndStream->GetStream () << "0.0 " << oldval << std::endl;
firstCwnd = false;
}
*cWndStream->GetStream () << Simulator::Now ().GetSeconds () << " " << newval << std::endl;
cWndValue = newval;

if (!firstSshThr)
{
*ssThreshStream->GetStream () << Simulator::Now ().GetSeconds () << " " << ssThreshValue << std::endl;
}
}

//慢启动阈值追踪器:追踪慢启动阈值的变化
static void
SsThreshTracer (uint32_t oldval, uint32_t newval)
{
if (firstSshThr)
{
*ssThreshStream->GetStream () << "0.0 " << oldval << std::endl;
firstSshThr = false;
}
*ssThreshStream->GetStream () << Simulator::Now ().GetSeconds () << " " << newval << std::endl;
ssThreshValue = newval;

if (!firstCwnd)
{
*cWndStream->GetStream () << Simulator::Now ().GetSeconds () << " " << cWndValue << std::endl;
}
}

//RTT 和 RTO 追踪器:追踪往返时间和重传超时时间的变化
static void
RttTracer (Time oldval, Time newval)
{
if (firstRtt)
{
*rttStream->GetStream () << "0.0 " << oldval.GetSeconds () << std::endl;
firstRtt = false;
}
*rttStream->GetStream () << Simulator::Now ().GetSeconds () << " " << newval.GetSeconds () << std::endl;
}

static void
RtoTracer (Time oldval, Time newval)
{
if (firstRto)
{
*rtoStream->GetStream () << "0.0 " << oldval.GetSeconds () << std::endl;
firstRto = false;
}
*rtoStream->GetStream () << Simulator::Now ().GetSeconds () << " " << newval.GetSeconds () << std::endl;
}

//序列号追踪器:追踪发送序列号、接收序列号和飞行中的数据字节数
static void
NextTxTracer (SequenceNumber32 old, SequenceNumber32 nextTx)
{
NS_UNUSED (old);
*nextTxStream->GetStream () << Simulator::Now ().GetSeconds () << " " << nextTx << std::endl;
}

static void
InFlightTracer (uint32_t old, uint32_t inFlight)
{
NS_UNUSED (old);
*inFlightStream->GetStream () << Simulator::Now ().GetSeconds () << " " << inFlight << std::endl;
}

static void
NextRxTracer (SequenceNumber32 old, SequenceNumber32 nextRx)
{
NS_UNUSED (old);
*nextRxStream->GetStream () << Simulator::Now ().GetSeconds () << " " << nextRx << std::endl;
}

//追踪设置函数
static void
TraceCwnd (std::string cwnd_tr_file_name)
{
AsciiTraceHelper ascii;
cWndStream = ascii.CreateFileStream (cwnd_tr_file_name.c_str ());
Config::ConnectWithoutContext ("/NodeList/1/$ns3::TcpL4Protocol/SocketList/0/CongestionWindow", MakeCallback (&CwndTracer));
}

static void
TraceSsThresh (std::string ssthresh_tr_file_name)
{
AsciiTraceHelper ascii;
ssThreshStream = ascii.CreateFileStream (ssthresh_tr_file_name.c_str ());
Config::ConnectWithoutContext ("/NodeList/1/$ns3::TcpL4Protocol/SocketList/0/SlowStartThreshold", MakeCallback (&SsThreshTracer));
}

static void
TraceRtt (std::string rtt_tr_file_name)
{
AsciiTraceHelper ascii;
rttStream = ascii.CreateFileStream (rtt_tr_file_name.c_str ());
Config::ConnectWithoutContext ("/NodeList/1/$ns3::TcpL4Protocol/SocketList/0/RTT", MakeCallback (&RttTracer));
}

static void
TraceRto (std::string rto_tr_file_name)
{
AsciiTraceHelper ascii;
rtoStream = ascii.CreateFileStream (rto_tr_file_name.c_str ());
Config::ConnectWithoutContext ("/NodeList/1/$ns3::TcpL4Protocol/SocketList/0/RTO", MakeCallback (&RtoTracer));
}

static void
TraceNextTx (std::string &next_tx_seq_file_name)
{
AsciiTraceHelper ascii;
nextTxStream = ascii.CreateFileStream (next_tx_seq_file_name.c_str ());
Config::ConnectWithoutContext ("/NodeList/1/$ns3::TcpL4Protocol/SocketList/0/NextTxSequence", MakeCallback (&NextTxTracer));
}

static void
TraceInFlight (std::string &in_flight_file_name)
{
AsciiTraceHelper ascii;
inFlightStream = ascii.CreateFileStream (in_flight_file_name.c_str ());
Config::ConnectWithoutContext ("/NodeList/1/$ns3::TcpL4Protocol/SocketList/0/BytesInFlight", MakeCallback (&InFlightTracer));
}

static void
TraceNextRx (std::string &next_rx_seq_file_name)
{
AsciiTraceHelper ascii;
nextRxStream = ascii.CreateFileStream (next_rx_seq_file_name.c_str ());
Config::ConnectWithoutContext ("/NodeList/2/$ns3::TcpL4Protocol/SocketList/1/RxBuffer/NextRxSequence", MakeCallback (&NextRxTracer));
}

//主函数
int main (int argc, char *argv[])
{

//默认参数设置
std::string transport_prot = "TcpWestwood";//TCP变体类型
double error_p = 0.0;//误码率
std::string bandwidth = "2Mbps";
std::string delay = "0.01ms";//瓶颈链路宽带和延迟
std::string access_bandwidth = "10Mbps";
std::string access_delay = "45ms";//访问链路宽带和延迟
bool tracing = false;//追踪选项
std::string prefix_file_name = "TcpVariantsComparison";
uint64_t data_mbytes = 0;
uint32_t mtu_bytes = 400;
uint16_t num_flows = 1;
double duration = 100.0;
uint32_t run = 0;
bool flow_monitor = false;
bool pcap = false;
bool sack = true;
std::string queue_disc_type = "ns3::PfifoFastQueueDisc";
std::string recovery = "ns3::TcpClassicRecovery";

//命令行参数解析
CommandLine cmd (__FILE__);
cmd.AddValue ("transport_prot", "Transport protocol to use: TcpNewReno, TcpLinuxReno, "
"TcpHybla, TcpHighSpeed, TcpHtcp, TcpVegas, TcpScalable, TcpVeno, "
"TcpBic, TcpYeah, TcpIllinois, TcpWestwood, TcpWestwoodPlus, TcpLedbat, "
"TcpLp, TcpDctcp, TcpCubic", transport_prot);
cmd.AddValue ("error_p", "Packet error rate", error_p);
cmd.AddValue ("bandwidth", "Bottleneck bandwidth", bandwidth);
cmd.AddValue ("delay", "Bottleneck delay", delay);
cmd.AddValue ("access_bandwidth", "Access link bandwidth", access_bandwidth);
cmd.AddValue ("access_delay", "Access link delay", access_delay);
cmd.AddValue ("tracing", "Flag to enable/disable tracing", tracing);
cmd.AddValue ("prefix_name", "Prefix of output trace file", prefix_file_name);
cmd.AddValue ("data", "Number of Megabytes of data to transmit", data_mbytes);
cmd.AddValue ("mtu", "Size of IP packets to send in bytes", mtu_bytes);
cmd.AddValue ("num_flows", "Number of flows", num_flows);
cmd.AddValue ("duration", "Time to allow flows to run in seconds", duration);
cmd.AddValue ("run", "Run index (for setting repeatable seeds)", run);
cmd.AddValue ("flow_monitor", "Enable flow monitor", flow_monitor);
cmd.AddValue ("pcap_tracing", "Enable or disable PCAP tracing", pcap);
cmd.AddValue ("queue_disc_type", "Queue disc type for gateway (e.g. ns3::CoDelQueueDisc)", queue_disc_type);
cmd.AddValue ("sack", "Enable or disable SACK option", sack);
cmd.AddValue ("recovery", "Recovery algorithm type to use (e.g., ns3::TcpPrrRecovery", recovery);
cmd.Parse (argc, argv);

transport_prot = std::string ("ns3::") + transport_prot;

//随机种子设置
SeedManager::SetSeed (1);
SeedManager::SetRun (run);

// User may find it convenient to enable logging
//LogComponentEnable("TcpVariantsComparison", LOG_LEVEL_ALL);
//LogComponentEnable("BulkSendApplication", LOG_LEVEL_INFO);
//LogComponentEnable("PfifoFastQueueDisc", LOG_LEVEL_ALL);

// TCP ADU 大小计算:计算 TCP 应用数据单元(ADU)的大小,考虑 MTU 和头部开销
Header* temp_header = new Ipv4Header ();
uint32_t ip_header = temp_header->GetSerializedSize ();
NS_LOG_LOGIC ("IP Header size is: " << ip_header);
delete temp_header;
temp_header = new TcpHeader ();
uint32_t tcp_header = temp_header->GetSerializedSize ();
NS_LOG_LOGIC ("TCP Header size is: " << tcp_header);
delete temp_header;
uint32_t tcp_adu_size = mtu_bytes – 20 – (ip_header + tcp_header);
NS_LOG_LOGIC ("TCP ADU size is: " << tcp_adu_size);

// Set the simulation start and stop time
double start_time = 0.1;
double stop_time = start_time + duration;

// TCP参数配置:2 MB ,设置 TCP 缓冲区大小和 SACK 选项
Config::SetDefault ("ns3::TcpSocket::RcvBufSize", UintegerValue (1 << 21));
Config::SetDefault ("ns3::TcpSocket::SndBufSize", UintegerValue (1 << 21));
Config::SetDefault ("ns3::TcpSocketBase::Sack", BooleanValue (sack));

Config::SetDefault ("ns3::TcpL4Protocol::RecoveryType",
TypeIdValue (TypeId::LookupByName (recovery)));
// TCP 变体选择:特别处理 TcpWestwoodPlus,其他 TCP 变体通过名称查找
if (transport_prot.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 (transport_prot, &tcpTid), "TypeId " << transport_prot << " not found");
Config::SetDefault ("ns3::TcpL4Protocol::SocketType", TypeIdValue (TypeId::LookupByName (transport_prot)));
}

// 网络拓扑创建
NodeContainer gateways;
gateways.Create (1);//1 个网关节点
NodeContainer sources;
sources.Create (num_flows);//num_flows 个源节点
NodeContainer sinks;
sinks.Create (num_flows);//num_flows 个目的节点

// 错误模型配置
// 创建基于速率的错误模型,模拟随机丢包
Ptr<UniformRandomVariable> uv = CreateObject<UniformRandomVariable> ();
uv->SetStream (50);
RateErrorModel error_model;
error_model.SetRandomVariable (uv);
error_model.SetUnit (RateErrorModel::ERROR_UNIT_PACKET);
error_model.SetRate (error_p);

//链路配置
PointToPointHelper UnReLink;// 瓶颈链路
UnReLink.SetDeviceAttribute ("DataRate", StringValue (bandwidth));
UnReLink.SetChannelAttribute ("Delay", StringValue (delay));
UnReLink.SetDeviceAttribute ("ReceiveErrorModel", PointerValue (&error_model));

InternetStackHelper stack;
stack.InstallAll ();

//队列管理配置
TrafficControlHelper tchPfifo;
tchPfifo.SetRootQueueDisc ("ns3::PfifoFastQueueDisc");

TrafficControlHelper tchCoDel;
tchCoDel.SetRootQueueDisc ("ns3::CoDelQueueDisc");

Ipv4AddressHelper address;
address.SetBase ("10.0.0.0", "255.255.255.0");

// Configure the sources and sinks net devices
// and the channels between the sources/sinks and the gateways
PointToPointHelper LocalLink;// 访问链路
LocalLink.SetDeviceAttribute ("DataRate", StringValue (access_bandwidth));
LocalLink.SetChannelAttribute ("Delay", StringValue (access_delay));

Ipv4InterfaceContainer sink_interfaces;

DataRate access_b (access_bandwidth);
DataRate bottle_b (bandwidth);
Time access_d (access_delay);
Time bottle_d (delay);

//队列大小计算
uint32_t size = static_cast<uint32_t>((std::min (access_b, bottle_b).GetBitRate () / 8) *
((access_d + bottle_d) * 2).GetSeconds ());

Config::SetDefault ("ns3::PfifoFastQueueDisc::MaxSize",
QueueSizeValue (QueueSize (QueueSizeUnit::PACKETS, size / mtu_bytes)));
Config::SetDefault ("ns3::CoDelQueueDisc::MaxSize",
QueueSizeValue (QueueSize (QueueSizeUnit::BYTES, size)));

// 网络连接和 IP 地址分配
for (uint32_t i = 0; i < num_flows; i++)
{
NetDeviceContainer devices;
// 连接源节点到网关
devices = LocalLink.Install (sources.Get (i), gateways.Get (0));
tchPfifo.Install (devices);
address.NewNetwork ();// IP 地址分配
Ipv4InterfaceContainer interfaces = address.Assign (devices);

// 连接网关到目的节点
devices = UnReLink.Install (gateways.Get (0), sinks.Get (i));
if (queue_disc_type.compare ("ns3::PfifoFastQueueDisc") == 0)
{
tchPfifo.Install (devices);
}
else if (queue_disc_type.compare ("ns3::CoDelQueueDisc") == 0)
{
tchCoDel.Install (devices);
}
else
{
NS_FATAL_ERROR ("Queue not recognized. Allowed values are ns3::CoDelQueueDisc or ns3::PfifoFastQueueDisc");
}
address.NewNetwork ();// IP 地址分配
interfaces = address.Assign (devices);
sink_interfaces.Add (interfaces.Get (1));
}

NS_LOG_INFO ("Initialize Global Routing.");
Ipv4GlobalRoutingHelper::PopulateRoutingTables ();//路由配置

uint16_t port = 50000;
Address sinkLocalAddress (InetSocketAddress (Ipv4Address::GetAny (), port));
PacketSinkHelper sinkHelper ("ns3::TcpSocketFactory", sinkLocalAddress);

//应用程序安装
for (uint16_t i = 0; i < sources.GetN (); i++)
{
AddressValue remoteAddress (InetSocketAddress (sink_interfaces.GetAddress (i, 0), port));
Config::SetDefault ("ns3::TcpSocket::SegmentSize", UintegerValue (tcp_adu_size));
BulkSendHelper ftp ("ns3::TcpSocketFactory", Address ());
ftp.SetAttribute ("Remote", remoteAddress);
ftp.SetAttribute ("SendSize", UintegerValue (tcp_adu_size));
ftp.SetAttribute ("MaxBytes", UintegerValue (data_mbytes * 1000000));

// 安装发送应用
ApplicationContainer sourceApp = ftp.Install (sources.Get (i));
sourceApp.Start (Seconds (start_time * i));
sourceApp.Stop (Seconds (stop_time – 3));

// 安装接收应用
sinkHelper.SetAttribute ("Protocol", TypeIdValue (TcpSocketFactory::GetTypeId ()));
ApplicationContainer sinkApp = sinkHelper.Install (sinks.Get (i));
sinkApp.Start (Seconds (start_time * i));
sinkApp.Stop (Seconds (stop_time));
}

// Set up tracing if enabled
if (tracing)
{
std::ofstream ascii;
Ptr<OutputStreamWrapper> ascii_wrap;
ascii.open ((prefix_file_name + "-ascii").c_str ());
ascii_wrap = new OutputStreamWrapper ((prefix_file_name + "-ascii").c_str (),
std::ios::out);
stack.EnableAsciiIpv4All (ascii_wrap);

Simulator::Schedule (Seconds (0.00001), &TraceCwnd, prefix_file_name + "-cwnd.data");
Simulator::Schedule (Seconds (0.00001), &TraceSsThresh, prefix_file_name + "-ssth.data");
Simulator::Schedule (Seconds (0.00001), &TraceRtt, prefix_file_name + "-rtt.data");
Simulator::Schedule (Seconds (0.00001), &TraceRto, prefix_file_name + "-rto.data");
Simulator::Schedule (Seconds (0.00001), &TraceNextTx, prefix_file_name + "-next-tx.data");
Simulator::Schedule (Seconds (0.00001), &TraceInFlight, prefix_file_name + "-inflight.data");
Simulator::Schedule (Seconds (0.1), &TraceNextRx, prefix_file_name + "-next-rx.data");
}

if (pcap)
{
UnReLink.EnablePcapAll (prefix_file_name, true);
LocalLink.EnablePcapAll (prefix_file_name, true);
}

// Flow monitor
FlowMonitorHelper flowHelper;
if (flow_monitor)
{
flowHelper.InstallAll ();
}

Simulator::Stop (Seconds (stop_time));
Simulator::Run ();

if (flow_monitor)
{
flowHelper.SerializeToXmlFile (prefix_file_name + ".flowmonitor", true, true);
}

Simulator::Destroy ();
return 0;
}

程序功能和执行逻辑

1、网络拓扑结构:

源节点 1 —-+
|
源节点 2 —-+—- 网关 —- 目的节点 1
| |
源节点 3 —-+ +—- 目的节点 2
|
+—- 目的节点 3

2、支持的 TCP 变体:

  • TcpNewReno

  • TcpLinuxReno

  • TcpHybla

  • TcpHighSpeed

  • TcpHtcp

  • TcpVegas

  • TcpScalable

  • TcpVeno

  • TcpBic

  • TcpYeah

  • TcpIllinois

  • TcpWestwood

  • TcpWestwoodPlus

  • TcpLedbat

  • TcpLp

  • TcpDctcp

  • TcpCubic

3、执行流程:

  • 初始化阶段(0-0.1s)

        创建节点和网络拓扑

        安装协议栈和配置参数

        建立路由表

  • 连接建立阶段(0.1s开始)

        每个流在不同时间启动(交错启动)

        TCP三次握手建立连接

  • 数据传输阶段(0.1-约100s)

        多个TCP流并发传输数据

        每个流传输指定数据量的数据(data_mbytes)

        模拟网络拥塞、丢包等条件

  • 连接关闭阶段(结束前)

        流依次完成数据传输

        TCP连接正常关闭

4、性能指标追踪:

  • cwnd.data: 拥塞窗口随时间变化

  • ssth.data: 慢启动阈值随时间变化

  • rtt.data: 往返时间随时间变化

  • rto.data: 重传超时时间随时间变化

  • next-tx.data: 下一个发送序列号

  • inflight.data: 飞行中的字节数

  • next-rx.data: 下一个期望接收序列号

5、输出文件:

  • ASCII 追踪文件: 前缀-ascii

  • 性能数据文件: 前缀-cwnd.data, 前缀-ssth.data 等

  • PCAP 文件(可选): 网络抓包数据

  • 流量监控文件(可选): 前缀.flowmonitor

6、关键特性

  • 支持多种队列管理:

        PFIFO: 传统的 FIFO 队列

        CoDel: 主动队列管理算法

  • 灵活的链路配置

        瓶颈链路和访问链路可独立配置

        支持不同的带宽和延迟组合

  • 错误模拟

           可配置的误码率

           基于速率的随机丢包

  • 多流支持

        可配置并发流数量

        交错启动减少同步效应

2.3.3 ON/OFF模型:分组产生器OnOffApplication

OnOffApplication是部署在客户端的应用层协议,以ON/OFF模型发送分组并支持五种下层协议(TCP、UDP、IPv4、IPv6、NetDevice),其助手类是OnOffHelper

  • ON/OFF模型有两种状态:

        ON状态:OnOffApplication以固定码率(Constant BitRate,CBR)发送分组

        OFF状态:OnOffApplication停止发送分组

        ON状态和OFF状态的持续时间分别通过OnTime与OffTime属性定义,单位为s,这两个属性通常被设置为随机变量

client.SetAttribute(
"OnTime", //ON状态时间,单位为s
StringValue(
"ns3::ConstantRandomVariable[Cinstant=1]"));
client.SetAttribute(
"OffTime", //OFF状态时间,单位为s
StringValue(
"ns3::ConstantRandomVariable[Cinstant=0]"));

  • OnOffApplication专属属性

        PacketSize:发送分组负载大小,单位B

        DataRate:分组发送码率

        MaxBytes:能够发送的最大字总节数,默认值为0

  • ~example/routing/simple-global-rputing.cc(见ns-3仿真之应用层(一))
  • ~examples/tcp/tcp-star-server.cc

/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* 星型拓扑的 TCP 服务器模拟程序,中心节点作为服务器,外围节点作为客户端向服务器发送 TCP 数据
*
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/

// 拓扑结构:1个中心节点(n0)作为服务器,8个外围节点(n1-n8)作为客户端
/*
n2 n3 n4
\\ | /
\\|/
n1—n0—n5
/| \\
/ | \\
n8 n7 n6
*/
// – CBR Traffic goes from the star "arms" to the "hub"
// – Tracing of queues and packet receptions to file
// "tcp-star-server.tr"
// – pcap traces also generated in the following files
// "tcp-star-server-$n-$i.pcap" where n and i represent node and interface
// numbers respectively
// Usage examples for things you might want to tweak:
// ./waf –run="tcp-star-server"
// ./waf –run="tcp-star-server –nNodes=25"
// ./waf –run="tcp-star-server –ns3::OnOffApplication::DataRate=10000"
// ./waf –run="tcp-star-server –ns3::OnOffApplication::PacketSize=500"
// See the ns-3 tutorial for more info on the command line:
// http://www.nsnam.org/tutorials.html

#include <iostream>
#include <fstream>
#include <string>
#include <cassert>

#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/ipv4-global-routing-helper.h"

using namespace ns3;

NS_LOG_COMPONENT_DEFINE ("TcpServer");

int
main (int argc, char *argv[])
{
// Users may find it convenient to turn on explicit debugging
// for selected modules; the below lines suggest how to do this

//LogComponentEnable ("TcpServer", LOG_LEVEL_INFO);
//LogComponentEnable ("TcpL4Protocol", LOG_LEVEL_ALL);
//LogComponentEnable ("TcpSocketImpl", LOG_LEVEL_ALL);
//LogComponentEnable ("PacketSink", LOG_LEVEL_ALL);

// 默认参数配置
//数据包大小:250字节
Config::SetDefault ("ns3::OnOffApplication::PacketSize", UintegerValue (250));
//数据速率:5Kbps
Config::SetDefault ("ns3::OnOffApplication::DataRate", StringValue ("5kb/s"));
//节点总数:9(1个服务器+8个客户端)
uint32_t N = 9; //number of nodes in the star

// 支持通过命令行参数修改节点数
// Config::SetDefault()s at run-time, via command-line arguments
CommandLine cmd (__FILE__);
cmd.AddValue ("nNodes", "Number of nodes to place in the star", N);
cmd.Parse (argc, argv);

// 创建节点
NS_LOG_INFO ("Create nodes.");
NodeContainer serverNode;
NodeContainer clientNodes;
//创建1个服务器节点
serverNode.Create (1);
//创建N-1个客户端节点
clientNodes.Create (N-1);
//将所有节点放入一个容器
NodeContainer allNodes = NodeContainer (serverNode, clientNodes);

// 所有节点安装TCP/IP协议栈
InternetStackHelper internet;
internet.Install (allNodes);

//创建星型拓扑连接
std::vector<NodeContainer> nodeAdjacencyList (N-1);
for(uint32_t i=0; i<nodeAdjacencyList.size (); ++i)
{
nodeAdjacencyList[i] = NodeContainer (serverNode, clientNodes.Get (i));
}

// 配置和创建点对点链路
NS_LOG_INFO ("Create channels.");
PointToPointHelper p2p;
//链路带宽:5Mbps
p2p.SetDeviceAttribute ("DataRate", StringValue ("5Mbps"));
//链路迟延:2ms
p2p.SetChannelAttribute ("Delay", StringValue ("2ms"));
std::vector<NetDeviceContainer> deviceAdjacencyList (N-1);
for(uint32_t i=0; i<deviceAdjacencyList.size (); ++i)
{
deviceAdjacencyList[i] = p2p.Install (nodeAdjacencyList[i]);
}//为每个客户端-服务器对创建独立的点对点连接

// 分配 IP 地址
NS_LOG_INFO ("Assign IP Addresses.");
Ipv4AddressHelper ipv4;
std::vector<Ipv4InterfaceContainer> interfaceAdjacencyList (N-1);
for(uint32_t i=0; i<interfaceAdjacencyList.size (); ++i)
{
std::ostringstream subnet;
subnet<<"10.1."<<i+1<<".0";
ipv4.SetBase (subnet.str ().c_str (), "255.255.255.0");
interfaceAdjacencyList[i] = ipv4.Assign (deviceAdjacencyList[i]);
}

//由于每个客户端在不同的子网,需要通过路由表让服务器知道如何到达每个客户端
Ipv4GlobalRoutingHelper::PopulateRoutingTables ();

// 创建服务器应用(Packet Sink)
uint16_t port = 50000;//服务器监听50000端口
Address sinkLocalAddress (InetSocketAddress (Ipv4Address::GetAny (), port));
PacketSinkHelper sinkHelper ("ns3::TcpSocketFactory", sinkLocalAddress);//使用 TCP 套接字
ApplicationContainer sinkApp = sinkHelper.Install (serverNode);//在任意IP地址上监听
sinkApp.Start (Seconds (1.0));
sinkApp.Stop (Seconds (10.0));//运行时间1.0-10.0秒

// 创建客户端应用(OnOff)
OnOffHelper clientHelper ("ns3::TcpSocketFactory", Address ());//使用 TCP 套接字
clientHelper.SetAttribute ("OnTime", StringValue ("ns3::ConstantRandomVariable[Constant=1]"));//OnTime设置为常数1(一直发送)
clientHelper.SetAttribute ("OffTime", StringValue ("ns3::ConstantRandomVariable[Constant=0]"));//OffTime设置为常数0(从不停止)

//为每个客户端配置连接
ApplicationContainer clientApps;
for(uint32_t i=0; i<clientNodes.GetN (); ++i)
{
AddressValue remoteAddress
(InetSocketAddress (interfaceAdjacencyList[i].GetAddress (0), port));
//interfaceAdjacencyList[i].GetAddress(0) 获取服务器在子网 i 中的 IP 地址
clientHelper.SetAttribute ("Remote", remoteAddress);
clientApps.Add (clientHelper.Install (clientNodes.Get (i)));
}//每个客户端连接到服务器在对应子网的 IP 地址
clientApps.Start (Seconds (1.0));
clientApps.Stop (Seconds (10.0));//所有客户端同时启动(1.0秒)和停止(10.0秒)

//configure tracing
AsciiTraceHelper ascii;
p2p.EnableAsciiAll (ascii.CreateFileStream ("tcp-star-server.tr"));
p2p.EnablePcapAll ("tcp-star-server");

NS_LOG_INFO ("Run Simulation.");
Simulator::Run ();
Simulator::Destroy ();
NS_LOG_INFO ("Done.");

return 0;
}

程序功能和执行逻辑

1、网络拓扑:

服务器节点 (n0):
– 拥有8个网络接口,每个接口在不同的子网
– IP地址:10.1.1.1, 10.1.2.1, …, 10.1.8.1

客户端节点 (n1-n8):
– 每个客户端只有一个网络接口
– IP地址:10.1.1.2, 10.1.2.2, …, 10.1.8.2

链路特性:
– 每个链路:5 Mbps带宽,2ms延迟
– 总连接数:8条点对点链路

2、TCP连接建立过程:

        每个客户端独立建立 TCP 连接:

  • 1.0秒:所有客户端同时发起TCP连接
  • 1.0-1.01秒:TCP三次握手完成(2×2ms延迟 + 处理时间)

3、数据传输分析:

  • 单个客户端的数据速率

客户端配置:
– 数据包大小:250字节
– 数据速率:5 Kbps = 0.625 KB/s

每个数据包的传输时间:
数据包大小 = 250字节 + TCP头部(20) + IP头部(20) = 290字节
传输时间 = 290字节 × 8 bits/byte ÷ 5 Mbps = 464微秒
每秒数据包数 ≈ 2154个

  • 服务器端的总流量

8个客户端 × 5 Kbps = 40 Kbps
服务器总接收速率:40 Kbps = 5 KB/s

  • 链路带宽利用率

每个链路容量:5 Mbps = 625 KB/s
单个客户端利用率:5 Kbps ÷ 5 Mbps = 0.1%
总利用率:40 Kbps ÷ 5 Mbps = 0.8%

结论:链路带宽远未饱和

4、执行时间线:

时间(秒) 事件
0.0 模拟开始,路由表建立
1.0 所有服务器开始监听,所有客户端开始发送数据
1.0-1.01 所有TCP连接建立
1.01-9.99 数据传输阶段
10.0 所有应用停止
10.0+ TCP连接关闭

5、服务器端的多连接处理:

服务器需要同时处理8个TCP连接:

  • 每个连接使用不同的源端口号

  • 服务器通过四元组(源IP、源端口、目的IP、目的端口)区分连接

  • 所有连接共享服务器的处理资源

6、路由表配置:

由于每个客户端在不同的子网,路由表需要包含:

服务器路由表:
目的网络 下一跳 接口
10.1.1.0/24 直接连接 接口1
10.1.2.0/24 直接连接 接口2
10.1.8.0/24 直接连接 接口8
客户端路由表(以客户端1为例):
目的网络 下一跳 接口
10.1.1.0/24 直接连接 接口1
0.0.0.0/24 10.1.1.1 接口1(默认路由到服务器)

7、输出文件:

  • tcp-star-server.tr:ASCII跟踪文件
  • tcp-star-server-0-1.pcap 等:PCAP文件
  • ~examples/wireless/wifi-ap.cc

/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2005,2006,2007 INRIA
*
* 模拟了一个 WiFi 网络环境,包含一个接入点(AP)和两个站点(STA)
*其中一个站点向另一个站点发送数据,同时接入点在模拟过程中移动位置
*
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
*/

#include "ns3/command-line.h"// 命令行参数处理
#include "ns3/config.h"// 配置管理
#include "ns3/boolean.h" // 布尔值类型
#include "ns3/string.h"// 字符串类型
#include "ns3/yans-wifi-helper.h" // YANS WiFi 助手
#include "ns3/ssid.h"// SSID(服务集标识符)
#include "ns3/mobility-helper.h"// 移动性助手
#include "ns3/on-off-helper.h"// OnOff应用助手
#include "ns3/yans-wifi-channel.h"// YANS WiFi信道
#include "ns3/mobility-model.h" // 移动模型
#include "ns3/packet-socket-helper.h"// 原始数据包套接字助手
#include "ns3/packet-socket-address.h"// 原始数据包套接字地址
#include "ns3/athstats-helper.h"// Athstats统计助手

using namespace ns3;

static bool g_verbose = true;// 控制是否输出详细跟踪信息

//设备层跟踪
void
DevTxTrace (std::string context, Ptr<const Packet> p)
{
if (g_verbose)
{
std::cout << " TX p: " << *p << std::endl;// 设备发送数据包
}
}
void
DevRxTrace (std::string context, Ptr<const Packet> p)
{
if (g_verbose)
{
std::cout << " RX p: " << *p << std::endl; // 设备接收数据包
}
}

//物理层跟踪
void
PhyRxOkTrace (std::string context, Ptr<const Packet> packet, double snr, WifiMode mode, WifiPreamble preamble)
{
if (g_verbose)
{
std::cout << "PHYRXOK mode=" << mode << " snr=" << snr << " " << *packet << std::endl;
}
}
void
PhyRxErrorTrace (std::string context, Ptr<const Packet> packet, double snr)
{
if (g_verbose)
{
std::cout << "PHYRXERROR snr=" << snr << " " << *packet << std::endl;
}
}
void
PhyTxTrace (std::string context, Ptr<const Packet> packet, WifiMode mode, WifiPreamble preamble, uint8_t txPower)
{
if (g_verbose)
{
std::cout << "PHYTX mode=" << mode << " " << *packet << std::endl;
}
}
void
PhyStateTrace (std::string context, Time start, Time duration, WifiPhyState state)
{
if (g_verbose)
{
std::cout << " state=" << state << " start=" << start << " duration=" << duration << std::endl;
}
}

//移动性辅助函数
static void
SetPosition (Ptr<Node> node, Vector position)
{
Ptr<MobilityModel> mobility = node->GetObject<MobilityModel> ();
mobility->SetPosition (position);
}

static Vector
GetPosition (Ptr<Node> node)
{
Ptr<MobilityModel> mobility = node->GetObject<MobilityModel> ();
return mobility->GetPosition ();
}

static void
AdvancePosition (Ptr<Node> node)
{
Vector pos = GetPosition (node);
pos.x += 5.0;// 每次向右移动5米
if (pos.x >= 210.0)
{
return;
}
SetPosition (node, pos);

Simulator::Schedule (Seconds (1.0), &AdvancePosition, node);// 每隔1秒移动一次
}

//主函数
int main (int argc, char *argv[])
{
CommandLine cmd (__FILE__);
cmd.AddValue ("verbose", "Print trace information if true", g_verbose);
cmd.Parse (argc, argv);

Packet::EnablePrinting ();// 启用数据包打印功能

//创建网络节点
WifiHelper wifi;
MobilityHelper mobility;
NodeContainer stas;
NodeContainer ap;
NetDeviceContainer staDevs;
PacketSocketHelper packetSocket;

stas.Create (2);// 创建2个站点
ap.Create (1);// 创建1个接入点

// 安装原始数据包套接字到所有节点
packetSocket.Install (stas);
packetSocket.Install (ap);

//配置WiFi网络
//物理层配置
WifiMacHelper wifiMac;
YansWifiPhyHelper wifiPhy;
YansWifiChannelHelper wifiChannel = YansWifiChannelHelper::Default ();
wifiPhy.SetChannel (wifiChannel.Create ());
Ssid ssid = Ssid ("wifi-default");// 设置SSID
wifi.SetRemoteStationManager ("ns3::ArfWifiManager");// 使用ARF速率适配算法

//MAC层配置 – 站点(STA)
wifiMac.SetType ("ns3::StaWifiMac",
"ActiveProbing", BooleanValue (true),// 启用主动探测
"Ssid", SsidValue (ssid)); // 设置要连接的SSID
staDevs = wifi.Install (wifiPhy, wifiMac, stas);// 在站点上安装WiFi设备

//MAC层配置 – 接入点(AP)
wifiMac.SetType ("ns3::ApWifiMac",
"Ssid", SsidValue (ssid));// 设置广播的SSID
wifi.Install (wifiPhy, wifiMac, ap);// 在接入点上安装WiFi设备

// 配置移动性
mobility.Install (stas); // 为站点安装移动模型(默认位置:0,0,0)
mobility.Install (ap);// 为接入点安装移动模型

// 安排接入点从1.0秒开始移动
Simulator::Schedule (Seconds (1.0), &AdvancePosition, ap.Get (0));

//配置应用程序
PacketSocketAddress socket;
socket.SetSingleDevice (staDevs.Get (0)->GetIfIndex ());// 使用站点0的设备
socket.SetPhysicalAddress (staDevs.Get (1)->GetAddress ());目标:站点1的MAC地址
socket.SetProtocol (1);// 协议号1

OnOffHelper onoff ("ns3::PacketSocketFactory", Address (socket));
onoff.SetConstantRate (DataRate ("500kb/s")); // 500 Kbps恒定速率

ApplicationContainer apps = onoff.Install (stas.Get (0)); // 安装在站点0
apps.Start (Seconds (0.5)); // 0.5秒后开始
apps.Stop (Seconds (43.0));// 43.0秒后停止

Simulator::Stop (Seconds (44.0));

//配置跟踪连接
Config::Connect ("/NodeList/*/DeviceList/*/Mac/MacTx", MakeCallback (&DevTxTrace));
Config::Connect ("/NodeList/*/DeviceList/*/Mac/MacRx", MakeCallback (&DevRxTrace));
Config::Connect ("/NodeList/*/DeviceList/*/Phy/State/RxOk", MakeCallback (&PhyRxOkTrace));
Config::Connect ("/NodeList/*/DeviceList/*/Phy/State/RxError", MakeCallback (&PhyRxErrorTrace));
Config::Connect ("/NodeList/*/DeviceList/*/Phy/State/Tx", MakeCallback (&PhyTxTrace));
Config::Connect ("/NodeList/*/DeviceList/*/Phy/State/State", MakeCallback (&PhyStateTrace));

// 启用Athstats统计
AthstatsHelper athstats;
athstats.EnableAthstats ("athstats-sta", stas);// 为站点生成athstats
athstats.EnableAthstats ("athstats-ap", ap);// 为接入点生成athstats

Simulator::Run ();

Simulator::Destroy ();

return 0;
}

程序功能和执行逻辑

1、网络拓扑:

初始位置:
STA1 (0,0,0) —(无线)— AP (0,0,0) —(无线)— STA2 (0,0,0)

移动过程:
AP 从1.0秒开始,每隔1秒向右移动5米
直到到达(210,0,0)位置停止

2、WiFi网络参数:

  • SSID: "wifi-default"

  • 速率适配算法: ARF (Adaptive Rate Fallback)

  • 信道模型: YANS默认信道

  • 物理层: 802.11默认配置

  • MAC层:

    • AP: 使用ApWifiMac,广播SSID

    • STA: 使用StaWifiMac,主动探测AP

3、通信流程:

  • 关联过程 (0.0秒开始)

        STA1和STA2主动扫描AP

        发现AP广播的SSID "wifi-default"

        完成802.11关联过程

  • 数据传输 (0.5-43.0秒)

        STA1向STA2发送数据,速率500 Kbps

        数据通过AP转发(因为使用基础架构模式)

        使用原始数据包套接字,不经过IP层

  • AP移动 (1.0-42.0秒)

        AP每隔1秒向右移动5米

        总共移动距离:约205米

        移动次数:约41次

4、执行时间线:

时间(秒) 事件
0.0 模拟开始,WiFi关联过程
0.5 STA1开始向STA2发送数据
1.0 AP第一次移动(位置:5,0,0)
2.0 AP第二次移动(位置:10,0,0)
AP继续移动,每秒5米
42.0 AP最后一次移动(位置:210,0,0)
43.0 STA1停止发送数据
44.0 模拟结束

5、数据转发路径:

STA1 -> 无线信道 -> AP -> 无线信道 -> STA2

由于使用基础架构模式,所有站点间的通信都必须通过AP

6、关键特性:

  • 使用原始数据包套接字

        不依赖TCP/IP协议栈

        直接在MAC层进行通信

        适合研究底层无线协议

  • ARF速率适配

        自适应速率回退算法

        根据信道条件动态调整传输速率

        提高无线网络吞吐量

  • 移动性模拟

        AP在模拟过程中连续移动

        研究移动对无线通信的影响

        可观察信号强度变化、切换等效应

  • 全面跟踪

        MAC层跟踪:发送/接收

        PHY层跟踪:发送/成功接收/错误接收

        物理层状态跟踪:空闲/CCA忙/接收/发送

7、Athstats输出

Athstats生成类似Linux athstats工具的输出:

        athstats-sta-0-0.txt # 站点0的统计

        athstats-sta-1-0.txt # 站点1的统计

        athstats-ap-0-0.txt # 接入点的统计

  • ~src/csma/examples/csma-packet-socket.cc(见ns-3仿真之应用层(一))

赞(0)
未经允许不得转载:171主机测评 » ns-3仿真之应用层(二)
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址