【NS-3學習】ns3-模擬基礎:關鍵概念,日誌,命令行參數

前言

本篇博客先介紹在仿真過程當中會使用到的一些關鍵概念,而後介紹便於調試仿真腳本的經常使用技術:日誌、命令行參數。linux

關鍵概念

節點git


在因特網術語中,主機(終端)是指任何一臺鏈接到網絡的計算設備。ns-3並不是一個專門的因特網模擬器,而是一個網絡模擬器。爲此不採用術語「主機」,由於這個詞太容易讓人聯想到因特網以及相關協議。所以,選用其餘術語:節點。github

能夠將節點理解爲一個空的機箱,咱們能夠向其中添加各類功能,將其變成咱們想要的主機或者交換機。json

節點由C++中的Node類來描述。Node類提供了用於管理仿真器網絡組件表示的各類方法。例如NodeContainer類,用於管理一組節點指針。服務器

應用網絡


在ns-3中,須要被仿真的用戶程序被抽象爲應用。應用在C++中用Application類來描述。這個類提供了管理仿真時用戶層應用的各類方法。框架

信道函數


一般把網絡中數據流流過的媒介稱爲信道。在ns-3的模擬環境中,能夠把節點鏈接到表明數據交換信道的對象上。在這裏,基本的通訊子網這一抽象的概念被稱爲信道,在C++中用Channel類來描述。學習

Channel類提供了管理通訊子網對象和把節點鏈接至他們的各類方法。信道類一樣能夠由開發者以面向對象的方法自定義。一個信道實例能夠模擬一條簡單的線纜(wire),也能夠是一個複雜的巨型以太網交換機,甚至是無線網絡中充滿障礙物的三維空間。ui

我曾用到的ns-3仿真的兩種信道模型:CsmaChannelPointToPointChannel
CsmaChannel信道模擬了一個能夠用於實現載波偵聽多路訪問通訊子網中的媒介,這個信道具備和以太網類似的功能。
PointToPointChannel這個類表明一個簡單的點對點信道,此信道沒有多點通訊能力,能夠最多2個點到點鏈接的網絡設備。

網絡設備


在Unix(或者linux)系統中,外圍硬件被稱爲「設備」。設備經過驅動程序來控制,而網卡經過網卡驅動程序來控制。在Unix系統中,網卡被稱爲像eth0這樣的名字。在ns-3中,網絡設備這一抽象概念至關於硬件設備和軟件驅動的總和。

ns-3仿真環境中,網絡設備安裝在節點上,使得節點經過信道和其餘節點通訊。與真實的計算機同樣,一個節點能夠經過多個網絡設備同時鏈接到多條信道上。

網絡設備由C++中的NetDevice類來描述。NetDevice提供了管理鏈接其餘節點和信道對象的各類方法,而且容許開發者以面向對象的方法來自定義。

拓撲幫助


不少模塊都有幫助類來幫助咱們快速構建仿真程序,相似於幫助咱們快速配置應用程序的BulkSendHelper。要好好利用幫助類來幫咱們編寫仿真腳本。

附圖一張:ns-3仿真中兩個節點通訊所要經歷的模塊。

日誌

在不少大型系統中,都會提供一種基於控制檯的消息記錄模塊,用來向用戶及時反饋命令的執行狀況或者系統的運行狀況,ns-3採用這種機制實現了一種可選的、多等級的消息記錄模塊——日誌系統。在ns-3中,日誌系統主要用於提供程序調試的信息或者輸出程序中間結果用於驗證咱們的思路。

日誌的級別

日誌系統有7個等級,有低到高依次爲:

  1. LOG_ERROR :記錄錯誤信息,程序中使用NS_LOG_ERROR來輸出信息;
  2. LOG_WARN :記錄警告信息,程序中使用NS_LOG_WARN來輸出信息;
  3. LOG_DEBUG :記錄一些調試信息,程序中使用NS_LOG_DEBUG來輸出信息;
  4. LOG_INFO :記錄一些程序相關的信息,程序中使用NS_LOG_INFO來輸出信息;
  5. LOG_FUNCTION : 當有函數被調用時,記錄該調用信息,程序中使用NS_LOG_FUNCTION來輸出信息;
  6. LOG_LOGIC :記錄程序中執行流程的信息,程序中使用NS_LOG_LOGIC來輸出信息;
  7. LOG_ALL :包含上述全部信息,顯示任何級別的日誌。

還有一種特殊的級別須要注意,NS_LOG_UNCOND,這種輸出方式不會綁定到上述任何一個級別當中,只要所屬組件的日誌開啓,不論當前顯示哪一個級別,都將輸出日誌信息。

日誌的定義和使用

想要輸出日誌,首先要定義一個日誌組件:

NS_LOG_COMPONENT_DEFINE("MyTestLoggingComponent");

注意,日誌組件的名字不要重複,通常加上前綴。

而後是啓用日誌,啓用日誌有不少方式。這裏介紹一種簡單的,即在代碼中啓用。語法以下:

LogComponentEnable("MyTestLoggingComponent", LOG_INFO);

這樣就啓用了MyTestLoggingComponent組件當中的INFO級別的日誌。

下面是使用日誌的一個示例:

#include "ns3/core-module.h"
using namespace ns3;
NS_LOG_COMPONENT_DEFINE ("MyTestLoggingComponent");

int 
main (int argc, char *argv[])
{
    LogComponentEnable("MyTestLoggingComponent", LOG_INFO);
    
    NS_LOG_ERROR("test error");
    NS_LOG_WARN("test warn");
    NS_LOG_DEBUG("test debug");
    NS_LOG_INFO("test info");
    NS_LOG_FUNCTION("test function");
    NS_LOG_LOGIC("test logic");
    NS_LOG_UNCOND("test uncond");
}
sakura@sakura-pc:ns3-load-balance-origin$ ./waf --run "testlog"
Waf: Entering directory `/home/sakura/Application/ns3-load-balance-origin/build'
[ 985/2302] Compiling scratch/testlog.cc
[ 986/2302] Compiling scratch/scratch-simulator.cc
[ 987/2302] Compiling scratch/MyTest.cc
[ 988/2302] Compiling scratch/hidden-terminal.cc
[2279/2302] Linking build/scratch/testlog
[2287/2302] Linking build/scratch/scratch-simulator
[2290/2302] Linking build/scratch/hidden-terminal
[2291/2302] Linking build/scratch/MyTest
Waf: Leaving directory `/home/sakura/Application/ns3-load-balance-origin/build'
Build commands will be stored in build/compile_commands.json
'build' finished successfully (23.377s)
test info
test uncond

從輸出能夠看出,咱們啓用了LOG_INFO級別的日誌,輸出INFO級別的日誌內容和只有開啓日誌就能夠輸出的UNCOND信息。

若是想禁用全部日誌輸出,能夠不啓用日誌或者使用LOG_NONE。

LogComponentEnable("MyTestLoggingComponent", LOG_NONE);

輸出:

sakura@sakura-pc:ns3-load-balance-origin$ ./waf --run "testlog"
Waf: Entering directory `/home/sakura/Application/ns3-load-balance-origin/build'
[ 986/2302] Compiling scratch/testlog.cc
[2287/2302] Linking build/scratch/testlog
Waf: Leaving directory `/home/sakura/Application/ns3-load-balance-origin/build'
Build commands will be stored in build/compile_commands.json
'build' finished successfully (2.119s)
test uncond

可是須要注意,NS_LOG_UNCOND是依舊會輸出信息的,除非註釋掉日誌啓用那一行代碼。

累積日誌級別

以上的LOG_TYPE都是輸出單一級別的日誌信息,咱們還能夠輸出LOG_LEVEL_INFO或者LOG_LEVEL_DEBUG這樣的累積日誌級別。
其累計原則是,高等級的級別會使比其等級低的日誌都輸出。如LOG_LEVEL_INFO除了輸出INFO信息,還會輸出DEBUG、WARN、ERROR信息。

將啓動日誌的行改成:

LogComponentEnable("MyTestLoggingComponent", LOG_LEVEL_INFO);

輸出:

sakura@sakura-pc:ns3-load-balance-origin$ ./waf --run "testlog"
Waf: Entering directory `/home/sakura/Application/ns3-load-balance-origin/build'
[ 985/2302] Compiling scratch/testlog.cc
[2291/2302] Linking build/scratch/testlog
Waf: Leaving directory `/home/sakura/Application/ns3-load-balance-origin/build'
Build commands will be stored in build/compile_commands.json
'build' finished successfully (2.207s)
test error
test warn
test debug
test info
test uncond

日誌前綴

ns-3還提供了日誌前綴,來控制顯示日誌的來源,從而更加方便咱們進行調試。ns-3中可用的前綴有以下幾種:

  • LOG_PREFIX_FUNC:輸出日誌所在的函數
  • LOG_PREFIX_TIME:輸出日誌產生的時間(注意此時間是仿真時間,若仿真沒有開始則不會有任何輸出)
  • LOG_PREFIX_NODE:輸出日誌所在的節點編號
  • LOG_PREFIX_LEVEL:輸出日誌的級別
  • LOG_PREFIX_ALL:輸出以上全部信息

可使用LogComponentEnable和級別疊加使用,例如:

LogComponentEnable("MyTestLoggingComponent", LOG_LEVEL_INFO);
LogComponentEnable("MyTestLoggingComponent", LOG_PREFIX_LEVEL);

完整程序以下:

#include "ns3/core-module.h"
using namespace ns3;
NS_LOG_COMPONENT_DEFINE ("MyTestLoggingComponent");

int 
main (int argc, char *argv[])
{
    LogComponentEnable("MyTestLoggingComponent", LOG_LEVEL_INFO);
    LogComponentEnable("MyTestLoggingComponent", LOG_PREFIX_LEVEL);
    
    NS_LOG_ERROR("test error");
    NS_LOG_WARN("test warn");
    NS_LOG_DEBUG("test debug");
    NS_LOG_INFO("test info");
    NS_LOG_FUNCTION("test function");
    NS_LOG_LOGIC("test logic");
    NS_LOG_UNCOND("test uncond");
}
sakura@sakura-pc:ns3-load-balance-origin$ ./waf --run "testlog"
Waf: Entering directory `/home/sakura/Application/ns3-load-balance-origin/build'
[ 985/2302] Compiling scratch/testlog.cc
[2281/2302] Linking build/scratch/testlog
Waf: Leaving directory `/home/sakura/Application/ns3-load-balance-origin/build'
Build commands will be stored in build/compile_commands.json
'build' finished successfully (2.142s)
[ERROR] test error
[WARN ] test warn
[DEBUG] test debug
[INFO ] test info
test uncond

命令行參數

ns-3提供了一種不須要重修編輯和構建腳本就能夠改變腳本運行行爲的方法。這種方法就是經過命令行傳遞參數來改變腳本中的變量。

在使用命令行系統時,首先要聲明一個命令行類的對象,而後調用函數Parse。以下:

int main(int argc, char* argv[])
{
    ...
    CommandLine cmd;
    cmd.Parse(argc, argv);
    ...
}

這兩行代碼表明,如今用戶能夠命令行來訪問代碼中的變量和ns-3中的屬性系統

咱們在使用./waf 運行腳本使,能夠加上參數--PrintHelp來顯示可使用的參數。
例如,咱們運行代碼中examples/tutorial/first.cc。(這裏將first.cc複製到了scratch目錄下)

root@iZuf6c029wyeq0gfy84vcuZ:~/Applications/ns-allinone-3.29/ns-3.29# ./waf --run "scratch/first --PrintHelp"
Waf: Entering directory `/root/Applications/ns-allinone-3.29/ns-3.29/build'
Waf: Leaving directory `/root/Applications/ns-allinone-3.29/ns-3.29/build'
Build commands will be stored in build/compile_commands.json
'build' finished successfully (16.088s)
first [General Arguments]


General Arguments:
    --PrintGlobals:              Print the list of globals.
    --PrintGroups:               Print the list of groups.
    --PrintGroup=[group]:        Print all TypeIds of group.
    --PrintTypeIds:              Print all TypeIds.
    --PrintAttributes=[typeid]:  Print all attributes of typeid.
    --PrintHelp:                 Print this help message.

參數--PrintAttributes的功能就是掛載用戶想要修改的屬性系統的屬性名
在first.cc腳本中,以下幾行代碼就使用到了屬性系統,即配置屬性系統中的一些屬性的值。

PointToPointHelper pointToPoint;
pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("5Mbps"));
pointToPoint.SetChannelAttribute ("Delay", StringValue ("2ms"));

腳本中使用到的類是PointToPoint類,經過命令行顯示該類在使用中所定義的默認值,在操做過程當中是使用已經和網絡設備綁定的類PointToPointNetDevice中所涉及的屬性的默認值:

root@iZuf6c029wyeq0gfy84vcuZ:~/Applications/ns-allinone-3.29/ns-3.29# ./waf --run "scratch/first --PrintAttributes=ns3::PointToPointNetDevice"
Waf: Entering directory `/root/Applications/ns-allinone-3.29/ns-3.29/build'
Waf: Leaving directory `/root/Applications/ns-allinone-3.29/ns-3.29/build'
Build commands will be stored in build/compile_commands.json
'build' finished successfully (16.326s)
Attributes for TypeId ns3::PointToPointNetDevice
    --ns3::PointToPointNetDevice::Address=[ff:ff:ff:ff:ff:ff]
        The MAC address of this device.
    --ns3::PointToPointNetDevice::DataRate=[32768bps]
        The default data rate for point to point links
    --ns3::PointToPointNetDevice::InterframeGap=[+0.0ns]
        The time to wait between packet (frame) transmissions
    --ns3::PointToPointNetDevice::Mtu=[1500]
        The MAC-level Maximum Transmission Unit
    --ns3::PointToPointNetDevice::ReceiveErrorModel=[0]
        The receiver error model used to simulate packet loss
    --ns3::PointToPointNetDevice::TxQueue=[0]
        A queue to use as the transmit queue in the device.

這裏顯示默認值爲32768bps,咱們在腳本文件中定義的5Mbit/s會在實際運行時覆蓋該默認值。咱們比較一下使用自定義值(5Mbit/s)和默認值的運行結果。
使用自定義值:

root@iZuf6c029wyeq0gfy84vcuZ:~/Applications/ns-allinone-3.29/ns-3.29# ./waf --run "scratch/first"
Waf: Entering directory `/root/Applications/ns-allinone-3.29/ns-3.29/build'
Waf: Leaving directory `/root/Applications/ns-allinone-3.29/ns-3.29/build'
Build commands will be stored in build/compile_commands.json
'build' finished successfully (16.072s)
At time 2s client sent 1024 bytes to 10.1.1.2 port 9
At time 2.00369s server received 1024 bytes from 10.1.1.1 port 49153
At time 2.00369s server sent 1024 bytes to 10.1.1.1 port 49153
At time 2.00737s client received 1024 bytes from 10.1.1.2 port 9

使用默認值:

root@iZuf6c029wyeq0gfy84vcuZ:~/Applications/ns-allinone-3.29/ns-3.29# ./waf --run "scratch/first"
Waf: Entering directory `/root/Applications/ns-allinone-3.29/ns-3.29/build'
[2558/2609] Compiling scratch/first.cc
[2569/2609] Linking build/scratch/first
Waf: Leaving directory `/root/Applications/ns-allinone-3.29/ns-3.29/build'
Build commands will be stored in build/compile_commands.json
'build' finished successfully (19.405s)
At time 2s client sent 1024 bytes to 10.1.1.2 port 9
At time 2.25932s server received 1024 bytes from 10.1.1.1 port 49153
At time 2.25932s server sent 1024 bytes to 10.1.1.1 port 49153
At time 2.51865s client received 1024 bytes from 10.1.1.2 port 9

因爲客戶端應用程序的開始運行時間沒有改變,所以咱們能夠發現客戶端發送數據的時間仍是在2s時刻。而後,因爲把數據的發送速率從5Mbit/s降到了32768bit/s,因此服務器接收到數據的時間相應的推遲了一段時間。

咱們除了能夠修改應用程序的數據發送速率,還能夠修改其餘的屬性變量,好比延遲、最大分組發送數量等。
接下來咱們就看看如何經過命令行來修改這些屬性值。

掛鉤自定義變量

在ns-3中咱們能夠添加本身的變量,而後經過掛鉤將其他命令行相關聯。
以first.cc中下面這句代碼爲例講解:

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

這句話限制了MaxPackets屬性是固定的,若是要將這句話改爲在命令行編譯腳本時自定義,那麼作如下改動:
在first.cc開頭

uint32_t nPackets = 1; //這裏添加一個變量
CommandLine cmd;
cmd.AddValue("nPackets", "Number of packets to echo", nPackets);
//這上面代碼使得變量nPackets在命令行中能夠修改
cmd.Parse (argc, argv);

在修改代碼:

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

運行first.cc腳本:

root@iZuf6c029wyeq0gfy84vcuZ:~/Applications/ns-allinone-3.29/ns-3.29# ./waf --run "scratch/first --PrintHelp"
Waf: Entering directory `/root/Applications/ns-allinone-3.29/ns-3.29/build'
[2558/2609] Compiling scratch/first.cc
[2569/2609] Linking build/scratch/first
Waf: Leaving directory `/root/Applications/ns-allinone-3.29/ns-3.29/build'
Build commands will be stored in build/compile_commands.json
'build' finished successfully (19.032s)
first [Program Options] [General Arguments]


Program Options:
    --nPackets:  Number of packets to echo [1]


General Arguments:
    --PrintGlobals:              Print the list of globals.
    --PrintGroups:               Print the list of groups.
    --PrintGroup=[group]:        Print all TypeIds of group.
    --PrintTypeIds:              Print all TypeIds.
    --PrintAttributes=[typeid]:  Print all attributes of typeid.
    --PrintHelp:                 Print this help message.

嘗試修改這個參數的值:

root@iZuf6c029wyeq0gfy84vcuZ:~/Applications/ns-allinone-3.29/ns-3.29# ./waf --run "scratch/first --nPackets=2"
Waf: Entering directory `/root/Applications/ns-allinone-3.29/ns-3.29/build'
Waf: Leaving directory `/root/Applications/ns-allinone-3.29/ns-3.29/build'
Build commands will be stored in build/compile_commands.json
'build' finished successfully (16.033s)
At time 2s client sent 1024 bytes to 10.1.1.2 port 9
At time 2.25932s server received 1024 bytes from 10.1.1.1 port 49153
At time 2.25932s server sent 1024 bytes to 10.1.1.1 port 49153
At time 2.51865s client received 1024 bytes from 10.1.1.2 port 9
At time 3s client sent 1024 bytes to 10.1.1.2 port 9
At time 3.25932s server received 1024 bytes from 10.1.1.1 port 49153
At time 3.25932s server sent 1024 bytes to 10.1.1.1 port 49153
At time 3.51865s client received 1024 bytes from 10.1.1.2 port 9

與以前的結果比較能夠發現,客戶端發送了兩次。參數修改爲功!

小結

本篇文章又零零散散地繼續介紹了NS-3的相關知識。重點介紹了日誌系統,日誌的輸出對於咱們調試仿真腳本是十分重要的,要好好掌握。日誌中還有一些部分沒有提例如函很多天志的詳細使用和啓用日誌組件的其餘方式,這些均可以參考[2]。NS-3中還有一個對咱們分析腳本很是重要的功能,即追蹤框架(可參考[3])。這個跟蹤某些事件的發生,幫助咱們調試腳本。NS3的知識暫且介紹這些。一是後面沒有那麼多的時間讓我來寫這些學習筆記,二是附着的參考博客連接能夠是說很是好的學習資源(我老師寫的固然好( ̄^ ̄)ゞ,這令文筆拙劣的我怎麼還敢寫下去)。關於NS-3有任何的疑問,各位看官能夠私聊我,咱們共同進步!

參考: [1]馬春光.姚建盛.ns-3網絡模擬器基礎及應用[M].北京:人民郵電出版社,2014 [2]NS-3學習筆記(二):NS-3的日誌系統.http://rainsia.github.io/2018/03/30/ns3-002/ [3]NS-3學習筆記(九):NS-3的對象框架 之 追蹤框架.http://rainsia.github.io/2018/10/22/ns3-009/

相關文章
相關標籤/搜索