一、学习工具:
1. 参考资源:
a.《ns-3网络模拟器基础及应用》
b. NS-3中文手册
c. NS3官网 https://www.nsnam.org/ ,下载ns-3 manual/ns-3 tutorial,
在线ns-3 doxygen查找需要的函数跳转. https://www.nsnam.org/docs/release/3.29/doxygen/
2. 代码阅读撰写:
a. Windows下使用 source insight阅读,编写代码可以自动补全以及提示错误;
b. Linux下使用 eclipse进行代码阅读,debug调试等;
c. 两者结合使用效果更佳。
二、学习内容:NS3目录下/examples/routing/dynamic-global-routing.cc
1. 代码实现内容(可查看本文件注释,解释的很详细):
该动态路由实现了点对点(point-to-point)和csma信道传输,在特定时间传输UDP数据流,并设置节点ipv4接口的开关来实现动态计算路由。
a. 拓扑结构如下:
b. 事件顺序:
1s-16s 第一个CBR/UDP数据流从n1-n6,10s-16s第二个CBR/UDP数据流从n1-n6;在准备阶段,会预先计算全局路由,n1-n6的点对点最短路径;
1s:第一个n1-n6的数据流开启;
2s:n1节点关闭down,n1-n6链路断开,n1和n6重新计算路径,n1-n2-n5-n6;
4s:n1节点打开up,n1-n6链路重新连接,n1和n6重新计算路径,n1-n6;
6s:n6节点关闭down,n1-n6链路断开,n1和n6重新计算路径,n1-n2-n5-n6;
8s:n6节点打开up,n1-n6链路重新连接,n1和n6重新计算路径,n1-n6;
10s:第一个数据流停止传输;
11s:第二个数据流开始传输;
12s:n1节点关闭down,n1-n6链路断开,n1和n6重新计算路径,n1-n2-n5-n6;
14s:n1节点打开up,n1-n6链路重新连接,n1和n6重新计算路径,n1-n6;
16s:第二个数据流停止传输。
2. 代码详解,主函数部分
//设置全局路由能够根据接口状态触发全局路由重新计算,默认值为假,即关闭,在这里需要设置为真开启。
Config::SetDefault("ns3::Ipv4GlobalRouting::RespondToInterfaceEvents", BooleanValue(true));
//日志,创建节点。
NS_LOG_INFO("Create nodes.");
//使用NodeContainer 类创建七个节点,节点编号从0开始。
//点对点信道的两个节点组成一个节点容器,本例中,csma信道的四个节点还会组成一个节点容器,一共五个容器。
NodeContainer c;
c.Create (7);
NodeContainer n0n2= NodeContainer (c.Get (0), c.Get (2));
NodeContainer n1n2= NodeContainer (c.Get (1), c.Get (2));
NodeContainer n5n6= NodeContainer (c.Get (5), c.Get (6));
NodeContainer n1n6= NodeContainer (c.Get (1), c.Get (6));
NodeContainern2345 = NodeContainer (c.Get (2), c.Get (3), c.Get (4), c.Get (5));
//为创建的每个节点安装协议栈,InternetStackHelper类会为每个节点安装一个网络协议栈,主要是IP层。
InternetStackHelper internet;
internet.Install (c);
//日志,创建信道
NS_LOG_INFO ("Create channels.");
//使用PointToPointHelper类帮助创建p2p信道,即类+信道名称。
PointToPointHelper p2p;
//调用PointToPointHelper类中的设置设备属性函数SetDeviceAttribute 设置数据传输速率为5Mbps(这里的数据传输速率我个人认为应该是带宽吧..),调用设置信道属性函数SetChannelAttribute设置信道传输时延为2ms(参考ns3.PointToPointHelper.h)。
p2p.SetDeviceAttribute ("DataRate", StringValue("5Mbps"));
p2p.SetChannelAttribute ("Delay", StringValue("2ms"));
//应用PointToPointHelper类中的NetDeviceContainer Install (NodeContainer c);函数将以上设置的属性应用于节点和信道中,并将对应节点和信道放入NetDeviceContainer,即对应的一个信道的两个节点设置对应的NetDevice,然后放入容器中。
NetDeviceContainer d0d2 = p2p.Install (n0n2);
//csma信道同理,调用CsmaHelper类进行相应设置操作。
CsmaHelper csma;
csma.SetChannelAttribute ("DataRate",StringValue ("5Mbps"));
csma.SetChannelAttribute ("Delay", StringValue("2ms"));
NetDeviceContainer d2345 = csma.Install (n2345);
//设置IP地址
NS_LOG_INFO("Assign IP Addresses.");
//调用 Ipv4AddressHelper类分配地址,唯一用户可见的API是通过设置基IP地址和子网掩码分配完成的,如下从10.1.1.0开始以子网掩码为
255.255.255.0分配地址,地址默认是从1开始单调增长。
Ipv4AddressHelperipv4;
ipv4.SetBase("10.1.1.0", "255.255.255.0");
//然后为上述设置的NetDevice容器中的每个节点设置一个地址,如下,节点n0对应的设备d0的ip地址为10.1.1.1,节点n2对应的设备d2的ip地址为10.1.1.2,若还有其他设备依次按照顺序分配地址。
//完整函数为Ipv4InterfaceContainer中的Ipv4AddressHelper::Assign
(const NetDeviceContainer &c),使用Ipv4接口容器对象将一个IP地址同一个设备关联起来。
//每个NetDevice都有一个关联的IPv4接口,一个ipv4接口可以有一个或者与之相关的ipv4地址。
ipv4.Assign(d0d2);
ipv4.SetBase("10.1.3.0", "255.255.255.0");
Ipv4InterfaceContaineri5i6 = ipv4.Assign (d5d6);
//调用Ipv4GlobalRoutingHelper类中的PopulateRoutingTables
()成员函数,主要是建立路由数据库然后对仿真中的所有节点初始化路由表,让仿真中所有节点都会带有一个路由器。
Ipv4GlobalRoutingHelper::PopulateRoutingTables();
/*在ipv4-global-routing-helper.cc文件中对PopulateRoutingTables
()函数定义如下,实则是调用建立全局路由数据库和初始化路由表的函数。
void
Ipv4GlobalRoutingHelper::PopulateRoutingTables (void)
{
GlobalRouteManager::BuildGlobalRoutingDatabase ();
GlobalRouteManager::InitializeRoutes ();
}
*/
//日志,开始创建应用,
NS_LOG_INFO ("Create Applications.");
//指定端口
uint16_t port = 9; // Discard port (RFC 863)
//应用 OnOffHelper类中的OnOffHelper (std::string protocol, Address address); 函数帮助创建数据流
//参考inetsocketaddress.h中 InetSocketAddress (Ipv4Address ipv4, uint16_t
port);
函数得到OnOffHelper上述函数中的地址参数Address
/*参考ipv4-interferce-container.cc中如下函数得到,i5i6对应的ipv4接口容器中第一个接口(0代表i5,1代表n6节点的接口)的ipv4地址
Ipv4Address
Ipv4InterfaceContainer::GetAddress (uint32_t i, uint32_tj) const
*/
OnOffHelper onoff ("ns3::UdpSocketFactory",
InetSocketAddress (i5i6.GetAddress (1), port));
//设置应用属性,即这里的UDP数据流,数据传输速率和数据包大小。还可以设置其他属性,包括开关时间等。
onoff.SetConstantRate (DataRate ("2kbps"));
onoff.SetAttribute ("PacketSize", UintegerValue(50));
onoff2.SetAttribute ("OnTime", StringValue("ns3::ConstantRandomVariable[Constant=1]"));
onoff2.SetAttribute ("OffTime", StringValue("ns3::ConstantRandomVariable[Constant=0]"));
//为节点设置应用并一起放置与ApplicationContainer这个容器中,参考on-off-helper.h中原型函数:
ApplicationContainer Install (Ptr<Node> node)
const;这里应该是使用了c++中的隐式转换,
利用NodeContainer中的Get()函数得到节点指针,这里是为节点1设置application应用。
//然后设置开始时间,结束时间。
ApplicationContainer apps = onoff.Install (c.Get (1));
apps.Start (Seconds (1.0));
apps.Stop (Seconds (10.0));
//利用packetsinkhelper帮助创建一个用于接收数据的对象,原函数为packet-sink-helper.h中的:
PacketSinkHelper (std::string protocol, Address address);
//Ipv4Address::GetAny (),返回值为0.0.0.0
//在创建的applicationcontainer应用容器中,将接收数据对象绑定在节点n6上,意味着n6在1s-10s接收网络中的数据。
PacketSinkHelper sink ("ns3::UdpSocketFactory",
Address (InetSocketAddress (Ipv4Address::GetAny (), port)));
apps = sink.Install (c.Get (6));
apps.Start (Seconds (1.0));
apps.Stop (Seconds (10.0));
//对节点n1设置接口开关,先从nodecontainer获取节点转化为指针n1
Ptr<Node> n1
= c.Get (1);
//inline
Ptr<T> GetObject (void) const;将参数转化为一个指针
Ptr<Ipv4>
ipv41 = n1->GetObject<Ipv4> ();
uint32_t
ipv4ifIndex1 = 2;
//Simulator::Schedule (Time const &delay, const
Ptr<EventImpl> &event),设置事件,包括发生时间和事件类型,
//原型函数参考simulator.h中函数:staticEventId Schedule (Time const &delay, MEM mem_ptr, OBJ obj, T1 a1);
// MEM mem_ptr成员方法指针,OBJ obj应用成员方法的对象指针,T1 a1调用的对象。
//这里是对n1节点的接口设置开关状态
Simulator::Schedule (Seconds(2),&Ipv4::SetDown,ipv41, ipv4ifIndex1);
Simulator::Schedule (Seconds (4),&Ipv4::SetUp,ipv41,ipv4ifIndex1);
//该部分就是计算计算路由,生成路由表等,关于路由下次详细解释。
Ipv4GlobalRoutingHelper g;
Ptr routingStream =Create ("dynamic-global-routing.routes",std::ios::out);
g.PrintRoutingTableAllAt (Seconds (12), routingStream);
//设置仿真参数,代码主函数完成。
NS_LOG_INFO ("Run Simulation.");
Simulator::Run ();
Simulator::Destroy ();
NS_LOG_INFO ("Done.");