目录
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仿真之应用层(一))




