天天看点

NS-3入门

什么是NS-3?

离散事件驱动网络模拟器。看看官方的定义:(from http://www.nsnam.org/)

ns-3 is a discrete-event network simulator for Internet systems, targeted primarily for research and educational use. ns-3 is free software, licensed under the GNU GPLv2 license, and is publicly available for research, development, and use.

ns-3 is intended as an eventual replacement for the popular ns-2 simulator. The project acronym “nsnam” derives historically from the concatenation of ns (network simulator) and nam (network animator).

NS-3 vs NS-2

NS-3虽然冠以一个“3”,但事实上跟它广泛流行的前任NS-2并非一脉相承,或者从使用角度上说,仅仅继承了一个名称而已。NS-3基本上是一个新的模拟器,不支持NS-2的API。NS-3是完全用C++编写的(也有可选的Python接口),而NS-2一部分模块使用C++而另一部分使用OTcl。因而NS-3最大的特点就是脚本可以C++或Python语言,而在NS-2中,我们使用的是OTcl。

NS-3的功能仍旧在开发中,因此它远没有NS-2完善(当然NS-2的维护也在进行中)。NS-3并不包含目前所有NS-2的功能,但它具有某些新的特性:正确的多网卡处理、IP寻址策略的使用、更详细的802.11模块等等。

(出于最后的这句话,我们这次作业大胆地采用了NS-3进行仿真——此是后话。)

Latest stable release: ns-3.2.1 (November 20, 2008)

结构:

据说NS-3的架构看起来比NS-2清晰得多,从NS-3 Tutorial看起来确实是这样。NS-3中把网络构件分为四类:

  ·Node:终端节点,能够添加应用、协议、外部接口等。

  ·NetDevice:网卡及其驱动,有各种不同类型的网卡:CsmaNetDevice、PointToPointNetDevice、WifiNetDevice。

  ·Channel:通道,有各种不同类型的介质通道:CsmaChannel、PointToPointChannel、WifiChannel。

  ·Application:应用程序,包括UdpEchoClientApplication、UdpServerApplication等。

此外,NS-3中提供了一类称为Topology Helper的模块,对应每种拓扑连接有不同的Helper(例如CsmaNetHelper等),使用这些类来模拟现实中的安装网卡、连接、配置链路等过程,来简化工作。

NS-3对我来说也是一个小火星环境,因此也有许多火星文需要学习:

【名词解释】

POSIX:Portable Operating System Interface

一组操作系统API的协议/标准族,最开始为了Unix系统上的可移植性而开发的,也适用于其他操作系统。

Doxygen:Documentation Generator

支持C++、C、Java、Objective-C、Python、IDL、Fortran、VHDL、PHP、C#等各种语言的文档生成器,用于从源代码中生成说明文档。(类似于我之前使用过的Sandcastle,貌似更加强大些,有必要得学习一下。)

nam:Network Animator

基于Tcl/TK的网络动画演示工具,能提供拓扑和包级别的动画以及数据流观察。(参考http://www.isi.edu/nsnam/nam/)

Mercurial

NS-3代码维护使用的源码版本控制管理系统

Waf

NS-3项目使用的新一代的基于Python的构建系统(Build System)

WireShark

一种GUI包嗅探器。由于NS-3能生成.pcap文件,因此可以使用类似于WireShark的软件对数据进行分析

tcpdump

另一种包嗅探器。在Linux下使用CLI进行数据分析

╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬

一、环境支持

如上文(NS-3入门[1]概念引入)所述,编译/运行NS-3脚本需要保证Linux环境的设置(gcc、waf、tcpdump等),详细的必要软件包安装过程参见http://www.nsnam.org/wiki/index.php/Installation

二、NS-3C++脚本的编写

如前所述,NS-3的脚本使用C++语言(也支持python),使用四种类型的网络构件(Node、NetDevice、Channel、Application)。一个简单的脚本一般有以下步骤:

1、创建节点Node(使用类NodeContainer::Create()方法)

2、使用链路Helper类来帮助设置链路(包括PointToPointHelper、CsmaHelper、WifiHelper等类型)。Helper类虽然不属于上述四类的网络构件,但它却极大地方便了拓扑的搭建,它可以帮助我们处理实际中诸如在两个终端安装网卡、连网线、Modern、配置上网方式、链路属性等底层工作,简化了仿真过程,使我们可以更专注于仿真的目的

3、安装IP协议栈(使用类InternetStackHelper::Install()方法)

4、设置IP地址(使用类Ipv4AddressHelper::SetBase()/Assign()方法)

5、在节点Node上安装应用程序(目前支持UdpServerServer、UdpEchoClient、PacketSink等)

6、设置仿真时间、启动仿真

===================================

一个简单的脚本(来自NS-3 Tutorial)及其解释

=========================================================================

#include "ns3/core-module.h"

#include "ns3/simulator-module.h

#include "ns3/node-module.h"

#include "ns3/helper-module.h"

using namespace ns3;

NS_LOG_COMPONENT_DEFINE ("Example");   //定义名称为“Example”的日志模块

int

main (int argc, char *argv[])

{

   //以下两个语句启用UdpEcho应用程序的日志记录,其级别为LOG_LEVEL_INFO。关于NS-3的日志系统将在后续篇章进行介绍。

  LogComponentEnable("UdpEchoClientApplication", LOG_LEVEL_INFO);

  LogComponentEnable("UdpEchoServerApplication", LOG_LEVEL_INFO);

  NodeContainer nodes;   //1、创建两个节点

  nodes.Create (2);  

  PointToPointHelper pointToPoint;  //2、创建P2P类型的Helper

  pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("5Mbps"));   //使用Helper设置链路属性

  pointToPoint.SetChannelAttribute ("Delay", StringValue ("2ms"));

  NetDeviceContainer devices;

  devices = pointToPoint.Install (nodes);  //使用Helper将网卡安装到节点

  InternetStackHelper stack;  //3、安装IP协议栈

  stack.Install (nodes);

  Ipv4AddressHelper address;  //4、分配IP地址

  address.SetBase ("10.1.1.0", "255.255.255.0");

  Ipv4InterfaceContainer interfaces = address.Assign (devices);  //分配到网卡

  UdpEchoServerHelper echoServer (9);   //5.1、安装UdpServer应用服务,9表示服务端口

  ApplicationContainer serverApps = echoServer.Install (nodes.Get (1));

  serverApps.Start (Seconds (1.0));

  serverApps.Stop (Seconds (10.0));

  serverApps.Start (Seconds (1.0));   //6.1、Server启动时间

  serverApps.Stop (Seconds (10.0));

  UdpEchoClientHelper echoClient (interfaces.GetAddress (1), 9);   //5.2、安装UdpClient应用服务,需要指明服务器IP以及服务端口

  echoClient.SetAttribute ("MaxPackets", UintegerValue (1));

  echoClient.SetAttribute ("Interval", TimeValue (Seconds (1.)));

  echoClient.SetAttribute ("PacketSize", UintegerValue (1024));

  ApplicationContainer clientApps = echoClient.Install (nodes.Get (0));

  clientApps.Start (Seconds (2.0));   //6.2、Client启动时间

  clientApps.Stop (Seconds (10.0));

  Simulator::Run ();   //6.3、启动仿真

  Simulator::Destroy ();

  return 0;

}

三、编译与运行

当我们装好NS-3的运行环境之后,在NS-3的程序目录下会有一个scratch目录,其性质类似于VC/VC++环境下的Debug目录。

将上述脚本文件保存为example.cc,复制到scratch下面,然后在NS-3目录下使用命令waf完成编译,然后运行。例如:

      $~/NS-3.2.1 > ./waf

      $~/NS-3.2.1 > ./waf --run scratch/example

可以看到程序输出:

    Entering directory ‘~/NS-3.2.1/build’

    Compilation finished successfully

    Sent 1024 bytes to 10.1.1.2

    Received 1024 bytes from 10.1.1.1

    Received 1024 bytes from 10.1.1.2

╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬═╬

NS-3日志子系统的提供了各种查看仿真结果的渠道:

一、使用Logging Module

0、【预备知识】日志级别及其对应的宏

NS-3提供了若干个日志级别来满足不同的Debug需求,每一级的日志内容都涵盖了低一级的内容。这些级别对应的宏从低到高排列为:

  *NS_LOG_ERROR — Log error messages;

  *NS_LOG_WARN — Log warning messages;

  *NS_LOG_DEBUG — Log relatively rare, ad-hoc debugging messages;

  *NS_LOG_INFO — Log informational messages about program progress;

  *NS_LOG_FUNCTION — Log a message describing each function called;

  *NS_LOG_LOGIC — Log messages describing logical flow within a function;

  *NS_LOG_ALL — Log everything.

  *NS_LOG_UNCOND — 无条件输出

方式1、通过设置shell环境变量NS_LOG使用日志系统

 1.1)首先,定义好一个日志模块:

  可以在脚本中使用宏NS_LOG_COMPONENT_DEFINE(name)定义一个日志模块。(注意,为了使用宏NS_LOG(name, level)来输出这个模块所定义的内容,这个定义语句必须写在每个脚本文件的开始。宏NS_LOG将在方式2中进行介绍。)

  也有一些日志模块是内置的,比如上文的名为“UdpEchoClientApplication”“UdpEchoServerApplication”的模块就是UdpEcho应用程序内置的日志模块,只要使用了相应的类,就可以启用相应的日志模块。

 1.2)在shell中通过设置环境变量NS_LOG,来控制仿真输出级别:

  $~/ns-3.2.1 > export NS_LOG = '<日志模块名称> =level_all | prefix_func | prefix_time'

   *level_all表示启用所有级别(=error | warn | debug | info | function | logic)

   *prefix_func表示记录输出该消息的函数

   *prefix_time表示加上时间前缀

  $~/ns-3.2.1 > export NS_LOG = '<日志模块名称1>=level_all : <日志模块名称2>=info'

   *符号:隔开两个不同的日志模块

  $~/ns-3.2.1 > export NS_LOG = * = level_all

   *符号*作为通配符。上行命令表示启用所有可用模块的所有日志级别。

   *这一般会形成大量的数据,此时可以使用shell的输出重定向保存日志到文件里面:

    $~/ns-3.2.1 > ./waf --run scratch/example >& log.out

方式2、通过在脚本里使用宏NS_LOG调用日志模块

 2.0)宏NS_LOG(level, msg)用于定义对应level的输出内容;为了方便使用,系统预定义了各个级别的NS_LOG宏NS_LOG_ERROR等(参见【预备知识】):

  #define NS_LOG_ERROR(msg)   NS_LOG(ns3::LOG_ERROR, msg)

 2.1)如上文,在脚本里使用宏NS_LOG_COMPONENT_DEFINE(name)定义一个日志模块;

 2.2)使用宏LogComponentEnable(name, level)启用日志(对应地,有宏LogComponentDisable(name, level)用于禁用日志);                                                                                                                                                 

 2.3)使用【预备知识】里定义的各种级别的宏输出内容,注意程序只会输出低于等于已经启用的level的宏内容。

  NS_LOG_COMPONENT_DEFINE("Example");

  LogComponentEnable("Example", LOG_LEVEL_INFO);   //等价于shell中:export NS_LOG = 'Example=info'

  NS_LOG_WARN("Message:level_warn");

  NS_LOG_INFO("Message:level_info");

  NS_LOG_LOGIC("Message:level_logic");

   //由于我们启用的日志level是INFO,因此编译运行后,程序会输出低于和等于INFO级别的内容,而高于INFO级别的宏内容不会被输出

   //即,Message:level_warn和Message:level_info会被输出,而Message:level_logic不会被输出

===============================================================================================================

二、使用Command Line参数

仿真一般是为了收集各种不同条件下的数据,常常需要改变一些变量。NS-3提供了Command Line参数接口,可以在运行时对脚本中的变量进行设置,免去了每次更改变量后要重新编译的麻烦。(相当于在运行前进行变量的scanf/cin操作,但因为有默认值,CLI更灵活一些。)

1、在脚本中添加语句

int main (int argc, char *argv[])

{

  ...

  CommandLine cmd;

  cmd.Parse (argc, argv);  //将命令行输入的参数作为类CommandLine的参数进行分析

  ...

}

  这样可以在shell中使用某些附加参数如PrintHelp:

   $~/ns-3.2.1 > ./waf --run "scratch/example --PrintHelp"

  这条命令将会列出example当前可用的命令参数:

   Entering directory '/home/craigdo/repos/ns-3-dev/build'

   Compilation finished successfully

   --PrintHelp: Print this help message.

   --PrintGroups: Print the list of groups.

   --PrintTypeIds: Print all TypeIds.

   --PrintGroup=[group]: Print all TypeIds of group.

   --PrintAttributes=[typeid]: Print all attributes of typeid.

   --PrintGlobals: Print the list of globals.

  从输出中(倒数第二行)我们知道可以打印某些类的属性:

   $~/ns-3.2.1 > ./waf --run "scratch/example --PrintAttributes=ns3::PointToPointNetDevice"

  这条命令将会列出类型为PointToPointNetDevice的设备的属性:

   --ns3::PointToPointNetDevice::DataRate=[32768bps]:

   The default data rate for point to point links

  知道了属性名称,我们也可以使用命令更改这个属性:

   $~/ns-3.2.1 > ./waf --run "scratch/example --ns3::PointToPointNetDevice::DataRate=5Mbps"

2、使用CommandLine::AddValue添加自己的变量,使之成为CommandLine可以使用的参数

   CommandLine cmd;

   cmd.AddValue("nPackets", "Number of packets to echo", nPackets);   //(属性名称,属性说明,变量)

   cmd.Parse(argc, argv);

  这样在shell中我们可以在命令中更改这个属性:

   $~/ns-3.2.1 > ./waf --run "scratch/example --nPackets=2"

===============================================================================================================

三、使用Tracing System

1、启用ASCII Tracing

  NS-3提供了类似NS-2的日志输出(*.tr文件),记录系统中的动作。在Simulator::Run()之前添加语句:

   #include <fstream>

   ...

   std::ofstream ascii;

   ascii.open ("example.tr");

   PointToPointHelper::EnableAsciiAll (ascii);

  则运行后我们可以在example.tr文件中看到系统的日志(使用ASCII文本阅读器即可),其中每一行都是以+/-/d/r开头的:

   +: An enqueue operation occurred on the device queue;

   -: A dequeue operation occurred on the device queue;

   d: A packet was dropped, typically because the queue was full;

   r: A packet was received by the net device.

  例如我们可以看到文件中的第一行(为了说明方便,这里分段编号显示),显示了一个入队操作:

   00 +

   01 2

   02 /NodeList/0/DeviceList/0/$ns3::PointToPointNetDevice/TxQueue/Enqueue

   03 ns3::PppHeader (

   04  Point-to-Point Protocol: IP (0x0021))

   05  ns3::Ipv4Header (

   06   tos 0x0 ttl 64 id 0 offset 0 flags [none]

   07   length: 1052 10.1.1.1 > 10.1.1.2)

   08   ns3::UdpHeader (

   09    length: 1032 49153 > 9)

   10    Payload (size=1024)

  其中编号为02的部分显示了发生操作的路径:根/NodeList是NS-3维护的所有节点列表,因此/NodeList/0表示编号为0的节点;随后的/DeviceList/0表示在该节点上的编号为0的NetDivece(比如网卡);接下来的$ns3::PointToPointNetDevice指明了该NetDivece的类型;最后的TxQueue/Enqueue表示在传送队列上发生了入队操作,也就是行开头的+所表现的意义。

2、启用PCAP Tracing

  NS-3也可以生成*.pcap文件,从而可以使用诸如Wireshark、tcpdump(前文NS-3入门[1]概念引入介绍过)等工具进行分析。

 2.1)在脚本Simulator::Run()之前添加语句:

   PointToPointHelper::EnablePcapAll ("example");

  这个语句将会产生若干*.pcap文件,命名为example-<Node编号>-<NetDevice编号>.pcap,分别记录每个设备的日志。也可以使用语句***Helper::EnablePcap (filename, NodeId, DeviceId)来只产生特定设备的pcap文件:

   PointToPointHelper::EnablePcap ("example", p2pNodes.Get (0)->GetId (), 0);  //只产生example-0-0.pcap文件

 2.2)使用tcpdump在命令行阅读pcap文件:

   ~/ns-3.2.1 > tcpdump -r example-0-0.pcap -nn -tt

   reading from file second-0-0.pcap, link-type PPP (PPP)

   2.000000 IP 10.1.1.1.49153 > 10.1.2.4.9: UDP, length 1024

   2.007382 IP 10.1.2.4.9 > 10.1.1.1.49153: UDP, length 1024

 2.3)使用Wireshark等软件打开pcap文件。

=======End===================================================================

转载链接:http://www.cnblogs.com/dabbei/archive/2013/07/18/3198552.html