什麼是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