ns-3 網絡模擬器教程。 第5章

ns-3 網絡模擬器教程。 第5章
第 1,2 章
第3章
第4章

5 設定
5.1 使用日誌模組
5.1.1 日誌記錄概述
5.1.2 啟用日誌記錄
5.1.3 在程式碼中新增日誌記錄
5.2 使用命令列參數
5.2.1 覆寫預設屬性值
5.2.2 捕獲自己的命令
5.3 使用追蹤系統
5.3.1 ASCII 跟踪
解析 ASCII 追蹤
5.3.2 PCAP 跟踪

第5章

調整

5.1 使用日誌模組

我們已經透過查看腳本簡要地了解了 ns-3 日誌記錄模組 第一抄送。在本章中,我們將仔細研究日誌子系統的可能用途。

5.1.1 日誌記錄概述

許多大型系統都支援某種訊息記錄工具,ns-3 也不例外。在某些情況下,只有錯誤訊息會寫入「操作員控制台」(在基於 Unix 的系統上通常是 stderr)。在其他系統上,可能會顯示警告訊息以及更詳細的資訊。在某些情況下,日誌記錄工具用於輸出可以快速模糊輸出的偵錯訊息。

ns-3 中使用的 subHRD 假設所有這些層級的資訊內容都是有用的,並且我們提供了一種選擇性的、分層的訊息記錄方法。日誌記錄可以完全停用、針對每個元件或全域啟用。為此目的,使用可調整的資訊內容等級。 ns-3 日誌記錄模組提供了一種相對簡單的方法來從模擬中獲取有用的信息。

您應該了解,我們提供了一種通用機制 - 追蹤 - 用於從模型中提取數據,這應該是模擬的首選輸出(有關我們的追蹤系統的更多信息,請參閱教程第 5.3 節)。日誌記錄應該是獲取偵錯資訊、警告、錯誤訊息或隨時從腳本或模型快速輸出訊息的首選方法。

目前,系統依資訊內容遞增的順序定義了七種日誌訊息等級(類型)。

  • LOG_ERROR - 記錄錯誤訊息(相關巨集:NS_LOG_ERROR);
  • LOG_WARN - 記錄警告訊息(相關巨集:NS_LOG_WARN);
  • LOG_DEBUG - 記錄相對罕見的特殊偵錯訊息(相關巨集:NS_LOG_DEBUG);
  • LOG_INFO - 註冊有關程式進度的資訊訊息(相關巨集:NS_LOG_INFO);
  • LOG_FUNCTION - 記錄描述每個呼叫的函數的訊息(兩個相關的巨集:NS_LOG_FUNCTION,用於成員函數,和 NS_LOG_FUNCTION_NOARGS,用於靜態函數);
  • LOG_LOGIC - 描述函數內邏輯流程的日誌訊息(相關巨集:NS_LOG_LOGIC);
  • LOG_ALL - 記錄上述所有內容(無關聯巨集)。
    對於每種類型 (LOG_TYPE),還有一個 LOG_LEVEL_TYPE,如果使用它,除了它自己的層級之外,還允許記錄其之上的所有層級。 (因此,LOG_ERROR和LOG_LEVEL_ERROR以及LOG_ALL和LOG_LEVEL_ALL在功能上是等價的。)例如,啟用LOG_INFO將只允許由NS_LOG_INFO巨集提供的訊息,而啟用LOG_LEVEL_INFOFO_LOGROR_N:NS、NS_LOGR、NS、NS_LOGROR_ROR_TROR_、NS、NS、NS、NS」提供的訊息。

我們也提供了一個始終顯示的無條件日誌記錄宏,無論日誌記錄等級或選擇元件為何。

  • NS_LOG_UNCOND - 關聯訊息的無條件日誌記錄(無關聯的日誌記錄等級)。

每個等級可以單獨查詢,也可以累積查詢。可以使用 sh 環境變數 NS_LOG 或透過記錄系統函數呼叫來配置日誌記錄。如前所述,日誌記錄系統有 Doxygen 文檔,如果您還沒有,現在是查看它的好時機。

現在您已經詳細閱讀了文檔,讓我們利用這些知識從範例腳本中獲取一些有趣的信息 暫存/myfirst.cc您已經編譯過。

5.1.2 啟用日誌記錄

讓我們使用 NS_LOG 環境變數來執行更多日誌,但首先,為了了解情況,請像之前一樣執行最後一個腳本,

$ ./waf --run scratch/myfirst

您應該會看到第一個 ns-3 範例程式中熟悉的輸出

$ Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build' 'build'
finished successfully (0.413s)
Sent 1024 bytes to 10.1.1.2
Received 1024 bytes from 10.1.1.1
Received 1024 bytes from 10.1.1.2

事實證明,您在上面看到的“已發送”和“已接收”訊息實際上是來自的記錄訊息 UdpEcho客戶端應用程式 и UdpEcho伺服器應用程式。例如,我們可以透過 NS_LOG 環境變數設定其日誌記錄等級來要求客戶端應用程式列印附加資訊。

從現在開始,我將假設您使用的是類似 sh 的 shell,它使用「VARIABLE=value」語法。如果您使用的是類似 csh 的 shell,那麼您必須將我的範例轉換為這些 shell 所需的「setenv 變數值」語法。

目前,UDP echo 用戶端應用程式回應以下程式碼行 暫存/myfirst.cc,

LogComponentEnable("UdpEchoClientApplication", LOG_LEVEL_INFO);

它啟用日誌記錄等級 LOG_LEVEL_INFO。當我們傳遞日誌記錄等級標誌時,我們實際上啟用了該等級以及所有較低等級。在本例中,我們啟用了 NS_LOG_INFO、NS_LOG_DEBUG、NS_LOG_WARN 和 NS_LOG_ERROR。我們可以透過設定 NS_LOG 環境變數來提高日誌記錄等級並獲取更多信息,而無需更改腳本和重新編譯,如下所示:

$ export NS_LOG=UdpEchoClientApplication=level_all

所以我們將 sh shell 變數 NS_LOG 設定為以下值,

UdpEchoClientApplication=level_all

賦值的左側是我們要設定的已記錄組件的名稱,右側則是我們要為其申請的標誌。在這種情況下,我們將在應用程式中啟用所有層級的偵錯。如果您以這種方式設定 NS_LOG 來執行腳本,則 ns-3 日誌系統將接受更改,並且您應該看到以下輸出:

Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finished successfully (0.404s)
UdpEchoClientApplication:UdpEchoClient()
UdpEchoClientApplication:SetDataSize(1024)
UdpEchoClientApplication:StartApplication()
UdpEchoClientApplication:ScheduleTransmit()
UdpEchoClientApplication:Send()
Sent 1024 bytes to 10.1.1.2
Received 1024 bytes from 10.1.1.1
UdpEchoClientApplication:HandleRead(0x6241e0, 0x624a20)
Received 1024 bytes from 10.1.1.2
UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()

應用程式提供的附加偵錯資訊現在位於 NS_LOG_FUNCTION 層級。它顯示腳本執行期間函數呼叫的每個實例。作為一般規則,在方法函數中最好使用(至少)NS_LOG_FUNCTION (this)... 用 NS_LOG_FUNCTION_NOARGS ()
僅在靜態函數中。但請注意,ns-3 系統不需要支援任何日誌記錄功能。關於記錄多少資訊的決定留給各個模型開發人員。在回顯應用程式中,可以使用大量的日誌輸出。

現在您可以查看應用程式進行的函數呼叫的日誌。如果你仔細觀察,你會注意到線條之間有一個冒號 UdpEcho客戶端應用程式 以及方法的名稱,您可能會在其中看到 C++ 作用域運算子 (: :)。這是故意的。

這實際上不是類別的名稱,而是日誌記錄組件的名稱。當原始檔案和類別之間存在匹配時,它通常是類別的名稱,但您應該意識到它實際上並不是類別的名稱,並且有一個冒號而不是雙冒號。這是一種幫助您以相對微妙的方式在概念上將日誌記錄 bean 名稱與類別名稱分開的方法。

但是,在某些情況下,可能很難確定實際產生日誌訊息的方法。如果您查看上面的文本,您可能想知道“Received 1024 bytes from 10.1.1.2」可以透過設定level來解決這個問題 前綴函數 到 NS_LOG 環境變數。請嘗試以下操作:

$ export 'NS_LOG=UdpEchoClientApplication=level_all|prefix_func'

請注意,引號是必要的,因為我們用來表示 OR 運算的垂直線也是 Unix 管道連接器。現在,如果運行該腳本,您將看到日誌記錄系統確保給定日誌中的每個訊息都以元件名稱為前綴。

Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finished successfully (0.417s)
UdpEchoClientApplication:UdpEchoClient()
UdpEchoClientApplication:SetDataSize(1024)
UdpEchoClientApplication:StartApplication()
UdpEchoClientApplication:ScheduleTransmit()
UdpEchoClientApplication:Send()
UdpEchoClientApplication:Send(): Sent 1024 bytes to 10.1.1.2
Received 1024 bytes from 10.1.1.1
UdpEchoClientApplication:HandleRead(0x6241e0, 0x624a20)
UdpEchoClientApplication:HandleRead(): Received 1024 bytes from 10.1.1.2
UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()

現在您可以看到來自 UDP 回顯客戶端應用程式的所有訊息都被如此標識。資訊 "Received 1024 bytes from 10.1.1.2" 現在已明確標識為來自 echo 用戶端應用程式。其餘訊息必須來自 UDP 回顯伺服器應用程式。我們可以透過在 NS_LOG 環境變數中輸入以冒號分隔的元件清單來啟用此元件。

$ export 'NS_LOG=UdpEchoClientApplication=level_all|prefix_func:
               UdpEchoServerApplication=level_all|prefix_func'

警告:在上面的範例文字中,您需要刪除冒號 (:) 後面的換行符,它用於格式化文件。現在,如果您運行該腳本,您將看到來自客戶端和伺服器回顯應用程式的所有日誌訊息。您可以看到這在調試時非常有用。

Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finished successfully (0.406s)
UdpEchoServerApplication:UdpEchoServer()
UdpEchoClientApplication:UdpEchoClient()
UdpEchoClientApplication:SetDataSize(1024)
UdpEchoServerApplication:StartApplication()
UdpEchoClientApplication:StartApplication()
UdpEchoClientApplication:ScheduleTransmit()
UdpEchoClientApplication:Send()
UdpEchoClientApplication:Send(): Sent 1024 bytes to 10.1.1.2
UdpEchoServerApplication:HandleRead(): Received 1024 bytes from 10.1.1.1
UdpEchoServerApplication:HandleRead(): Echoing packet
UdpEchoClientApplication:HandleRead(0x624920, 0x625160)
UdpEchoClientApplication:HandleRead(): Received 1024 bytes from 10.1.1.2
UdpEchoServerApplication:StopApplication()
UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoServerApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()
UdpEchoServerApplication:~UdpEchoServer()

有時能夠查看產生日誌訊息的模擬時間也很有用。您可以透過新增 OR 位元來完成此操作 前綴時間:

$ export 'NS_LOG=UdpEchoClientApplication=level_all|prefix_func|prefix_time: UdpEchoServerApplication=level_all|prefix_func|prefix_time'

同樣,您必須刪除上面的換行符。如果您現在運行該腳本,您應該會看到以下輸出:

Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finished successfully (0.418s)
0s UdpEchoServerApplication:UdpEchoServer()
0s UdpEchoClientApplication:UdpEchoClient()
0s UdpEchoClientApplication:SetDataSize(1024)
1s UdpEchoServerApplication:StartApplication()
2s UdpEchoClientApplication:StartApplication()
2s UdpEchoClientApplication:ScheduleTransmit()
2s UdpEchoClientApplication:Send()
2s UdpEchoClientApplication:Send(): Sent 1024 bytes to 10.1.1.2
2.00369s UdpEchoServerApplication:HandleRead(): Received 1024 bytes from 10.1.1.1
2.00369s UdpEchoServerApplication:HandleRead(): Echoing packet
2.00737s UdpEchoClientApplication:HandleRead(0x624290, 0x624ad0)
2.00737s UdpEchoClientApplication:HandleRead(): Received 1024 bytes from 10.1.1.2
10s UdpEchoServerApplication:StopApplication()
10s UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoServerApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()
UdpEchoServerApplication:~UdpEchoServer()

請注意,構造函數 UDP回顯伺服器 在模擬 0 秒期間被呼叫。這實際上發生在模擬開始之前,但時間顯示為零秒。構造函數訊息也是如此 UdpEcho客戶端.

Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finished successfully (0.418s)
0s UdpEchoServerApplication:UdpEchoServer()
0s UdpEchoClientApplication:UdpEchoClient()
0s UdpEchoClientApplication:SetDataSize(1024)
1s UdpEchoServerApplication:StartApplication()
2s UdpEchoClientApplication:StartApplication()
2s UdpEchoClientApplication:ScheduleTransmit()
2s UdpEchoClientApplication:Send()
2s UdpEchoClientApplication:Send(): Sent 1024 bytes to 10.1.1.2
2.00369s UdpEchoServerApplication:HandleRead(): Received 1024 bytes from 10.1.1.1
2.00369s UdpEchoServerApplication:HandleRead(): Echoing packet
2.00737s UdpEchoClientApplication:HandleRead(0x624290, 0x624ad0)
2.00737s UdpEchoClientApplication:HandleRead(): Received 1024 bytes from 10.1.1.2
10s UdpEchoServerApplication:StopApplication()
10s UdpEchoClientApplication:StopApplication()
UdpEchoClientApplication:DoDispose()
UdpEchoServerApplication:DoDispose()
UdpEchoClientApplication:~UdpEchoClient()
UdpEchoServerApplication:~UdpEchoServer()

回想一下腳本 暫存/first.cc 在模擬開始前一秒啟動回顯伺服器應用程式。現在你可以看到該方法 開始申請 伺服器實際上是在第一秒被呼叫的。您可能還注意到,正如我們在腳本中所要求的那樣,回顯客戶端在模擬的第二秒啟動。

現在您可以隨叫隨到地追蹤模擬進度 調度傳輸 在呼叫 HandleRead 回呼的客戶端中傳送 echo 伺服器應用程式。請注意,透過點對點連結傳送封包所需的時間為 3,69 毫秒。您可以看到 echo 伺服器記錄了一條訊息,表示它已回應封包,然後,經過通道延遲後,您會看到 echo 用戶端在其 HandleRead 方法中收到了 echo 封包。

在這個模擬中,很多事情都會在您沒有註意到的情況下發生。但是,您可以透過啟用系統中的所有日誌記錄元件來非常輕鬆地追蹤整個過程。嘗試將 NS_LOG 變數設定為以下值,

$ export 'NS_LOG=*=level_all|prefix_func|prefix_time'

上面的星號是日誌記錄組件的通配符。這將包括模擬中使用的所有組件中的所有條目。我不會在此處重現輸出(在撰寫本文時,它會為單個 echo 資料包產生 1265 行輸出),但您可以將此資訊重定向到檔案並在您喜歡的編輯器中查看它。

$ ./waf --run scratch/myfirst > log.out 2>&1

當我遇到問題並且不知道哪裡出了問題時,我個人會使用這種極其詳細的日誌記錄版本。我可以非常輕鬆地追蹤程式碼執行,而無需設定斷點並單步執行偵錯器中的程式碼。我可以在我最喜歡的編輯器中編輯輸出,並找到我期望的內容,並看到發生我沒有預料到的事情。一旦我大致了解出了什麼問題,我就會跳入除錯器來深入研究問題。當您的腳本執行完全意想不到的操作時,這種類型的輸出尤其有用。如果您只使用調試器,您可能會完全錯過一個轉折點。註冊使這種轉變變得引人注目。

5.1.3 在程式碼中新增日誌記錄

您可以透過從多個巨集呼叫日誌元件來為模擬新增條目。讓我們在腳本中完成它 我的第一站,我們在「clean」目錄中。回想一下,我們在這個場景中定義了一個日誌元件:

NS_LOG_COMPONENT_DEFINE ("FirstScriptExample");

您知道,您可以透過在不同層級設定 NS_LOG 環境變數來啟用來自該元件的所有訊息的日誌記錄。讓我們繼續向腳本添加一些條目。用於向日誌新增資訊層級訊息的巨集是 NS_LOG_INFO。讓我們新增一則訊息(就在我們開始建立節點之前),告訴您腳本處於「建立拓撲」階段。這是在以下程式碼片段中完成的,
打開 暫存/myfirst.cc 在您最喜歡的編輯器中添加以下行,
NS_LOG_INFO ("Creating Topology");
就在線條之前,

NodeContainer nodes;
nodes.Create (2);

現在使用編譯腳本 WAF,並清除 NS_LOG 變數以停用我們先前啟用的日誌流:

$ ./waf
$ export NS_LOG=
Теперь, если вы запустите скрипт,
$ ./waf --run scratch/myfirst

您不會看到新訊息,因為關聯的日誌記錄元件 (FirstScriptExample) 尚未啟用。要查看您的訊息,您需要啟用日誌記錄元件 第一個腳本範例 等級不低於 NS_LOG_INFO。如果您只想查看這個特定的日誌記錄級別,您可以這樣啟用它,

$ export NS_LOG=FirstScriptExample=info

如果您現在運行腳本,您將看到一條新訊息“Creating Topology”,

Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finished successfully (0.404s)
Creating Topology
Sent 1024 bytes to 10.1.1.2
Received 1024 bytes from 10.1.1.1
Received 1024 bytes from 10.1.1.2

5.2 使用命令列參數

5.2.1 覆寫預設屬性值

無需編輯或建置即可更改 ns-3 腳本行為的另一種方法是使用命令列參數。我們提供了一種解析命令列參數並根據結果自動設定局部和全域變數的機制。

使用命令列參數系統的第一步是聲明一個命令列解析器。這很容易做到(在你的主程式中),如下面的程式碼所示,

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

這個簡單的兩行程式碼片段本身實際上非常有用。它打開了 ns-3 全域變數和屬性系統的大門。讓我們在主腳本函數的開頭添加兩行程式碼 暫存/myfirst.cc。繼續,我們編譯腳本並運行它,啟動時我們發出如下幫助請求,

$ ./waf --run "scratch/myfirst --PrintHelp"

該指令會詢問 瓦夫 運行腳本 從頭開始/我的第一 並傳遞一個命令列參數 —列印幫助。需要使用引號來指示該參數用於哪個程式。命令列解析器將檢測參數 —列印幫助 並會顯示答案,

Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finished successfully (0.413s)
TcpL4Protocol:TcpStateMachine()
CommandLine:HandleArgument(): Handle arg name=PrintHelp value=
--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.

現在讓我們看看這個選項 —列印屬性。我們在研究first.cc腳本時已經提到ns-3屬性系統。我們看到了以下幾行程式碼,

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

他們說 數據速率 實際上是一個屬性 點對點網路設備。讓我們使用命令列參數解析器來查看屬性 點對點網路設備。幫助清單說明了我們必須提供的內容 類型ID。這是感興趣的屬性所屬的類別的名稱。在我們的例子中它將是 ns3::點對點網路設備。讓我們繼續前進,進入,

$ ./waf --run "scratch/myfirst --PrintAttributes=ns3::PointToPointNetDevice"

系統將列印該網路設備類型的所有屬性。您將看到清單中的屬性包括:

--ns3::PointToPointNetDevice::DataRate=[32768bps]:
The default data rate for point to point links

這是系統建立物件時將使用的預設值 點對點網路設備。我們將使用參數覆蓋這個預設值 屬性 в 點對點助手 更高。讓我們使用點對點設備和通道的預設值。為此,我們將刪除通話 設定設備屬性 и 設定頻道屬性 我的第一站,我們在一個乾淨的目錄中。

您的腳本現在應該簡單地聲明 點對點助手 且不執行任何安裝操作,如下例所示,

...
NodeContainer nodes;
nodes.Create (2);
PointToPointHelper pointToPoint;
NetDeviceContainer devices;
devices = pointToPoint.Install (nodes);
...

繼續建立一個新腳本 瓦夫 (./waff),讓我們返回並包含來自 UDP 回顯伺服器應用程式的一些條目並包含時間前綴。

$ export 'NS_LOG=UdpEchoServerApplication=level_all|prefix_time'

如果運行該腳本,您應該看到以下輸出:

Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finished successfully (0.405s)
0s UdpEchoServerApplication:UdpEchoServer()
1s UdpEchoServerApplication:StartApplication()
Sent 1024 bytes to 10.1.1.2
2.25732s Received 1024 bytes from 10.1.1.1
2.25732s Echoing packet
Received 1024 bytes from 10.1.1.2
10s UdpEchoServerApplication:StopApplication()
UdpEchoServerApplication:DoDispose()
UdpEchoServerApplication:~UdpEchoServer()

回想一下,上次我們查看模擬時間,也就是 echo 伺服器收到封包的那一刻,它是 2,00369 秒。

2.00369s UdpEchoServerApplication:HandleRead(): Received 1024 bytes from 10.1.1.1

現在他在 2.25732 秒內收到了資料包。這是因為我們只是將 PointToPointNetDevice 資料速率從每秒 32768 兆位元重設為預設值,即每秒 XNUMX 位元。如果我們使用命令列取代新的 DataRate,我們可以再次加快模擬速度。我們將根據 help 元素隱含的公式執行以下操作:

$ ./waf --run "scratch/myfirst --ns3::PointToPointNetDevice::DataRate=5Mbps"

這會將 DataRate 屬性傳回預設值 5 兆位元/秒。您對結果感到驚訝嗎?事實證明,為了返回腳本的原始行為,我們還需要設定通道延遲以匹配光速。我們可以要求命令列系統列印通道屬性,就像我們對網路設備所做的那樣:

$ ./waf --run "scratch/myfirst --PrintAttributes=ns3::PointToPointChannel"

我們會發現通道延遲屬性設定如下:

--ns3::PointToPointChannel::Delay=[0ns]:
Transmission delay through the channel

然後,我們可以透過命令列系統設定這兩個預設值。

$ ./waf --run "scratch/myfirst
--ns3::PointToPointNetDevice::DataRate=5Mbps
--ns3::PointToPointChannel::Delay=2ms"

在這種情況下,我們恢復在腳本中明確設定 DataRate 和 Delay 時的時間:

Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finished successfully (0.417s)
0s UdpEchoServerApplication:UdpEchoServer()
1s UdpEchoServerApplication:StartApplication()
Sent 1024 bytes to 10.1.1.2
2.00369s Received 1024 bytes from 10.1.1.1
2.00369s Echoing packet
Received 1024 bytes from 10.1.1.2
10s UdpEchoServerApplication:StopApplication()
UdpEchoServerApplication:DoDispose()
UdpEchoServerApplication:~UdpEchoServer()

請注意,伺服器在 2,00369 秒後再次收到該封包。我們實際上可以透過這種方式設定腳本中使用的任何屬性。特別是,我們可以將 MaxPackets 屬性設為非 XNUMX 值 UdpEcho客戶端.

你會如何使用它?試一試。請記住,您必須註解掉我們覆蓋預設屬性值並明確設定的地方 最大資料包數 在腳本中。然後您必須重建腳本。您也可以使用命令列取得語法幫助以設定新的預設屬性值。一旦了解這一點,您就可以控制命令列上顯示的包的數量。由於我們是勤奮好學的人,我們的命令列應該如下所示:

$ ./waf --run "scratch/myfirst
--ns3::PointToPointNetDevice::DataRate=5Mbps
--ns3::PointToPointChannel::Delay=2ms
--ns3::UdpEchoClient::MaxPackets=2"

此時出現的自然問題是如何知道所有這些屬性的存在。同樣,命令列系統有針對此事的幫助功能。如果我們向命令列尋求幫助,我們應該看到:

$ ./waf --run "scratch/myfirst --PrintHelp"
myfirst [Program Arguments] [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.

如果您選擇「PrintGroups」參數,您應該會看到所有已註冊群組的清單 類型ID。群組名稱與來源目錄中的模組名稱一致(儘管是大寫)。一次列印所有資訊會過於龐大,因此可以使用附加過濾器來按組列印資訊。因此,再次關注點對點模組:

./waf --run "scratch/myfirst --PrintGroup=PointToPoint"
TypeIds in group PointToPoint:
ns3::PointToPointChannel
ns3::PointToPointNetDevice
ns3::PointToPointRemoteChannel
ns3::PppHeader

在這裡您可以找到用於屬性查找的可用 TypeId 名稱,例如
--PrintAttributes = ns3 :: PointToPointChannel如上圖所示。

了解屬性的另一種方法是透過 Doxygen ns-3。有一個頁面列出了模擬器中註冊的所有屬性。

5.2.2 捕獲自己的命令

您也可以透過命令列系統新增自己的掛鉤。使用命令列解析器方法可以非常簡單地完成此操作 附加值.
讓我們使用此功能以完全不同的方式指定要顯示的套件的數量。讓我們加入一個名為的局部變數 n包數 變成一個函數 。我們將其設為 1 以匹配我們先前的預設行為。為了允許命令列解析器更改這個值,我們需要在解析器中捕獲這個值。我們透過添加一個呼叫來做到這一點 附加值。去修改一下腳本 暫存/myfirst.cc 所以從下面的程式碼開始,

int
main (int argc, char *argv[])
{
uint32_t nPackets = 1;
CommandLine cmd;
cmd.AddValue("nPackets", "Number of packets to echo", nPackets);
cmd.Parse (argc, argv);
...

向下捲動到腳本中設定 MaxPackets 屬性的位置並更改它,以便將其設定為 nPackets 變數而不是常數 1,如下所示。

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

現在,如果您執行腳本並提供 -PrintHelp 參數,您應該會看到新的使用者參數。幫助顯示幕上列出。進入,

$ ./waf --run "scratch/myfirst --PrintHelp"
Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finished successfully (0.403s)
--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.
User Arguments:
--nPackets: Number of packets to echo

如果要更改傳輸的資料包數量,可以透過設定命令列參數 - -nPackets 來實現。

$ ./waf --run "scratch/myfirst --nPackets=2"

現在你應該看到

Waf: Entering directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone/ns-3-dev/build'
'build' finished successfully (0.404s)
0s UdpEchoServerApplication:UdpEchoServer()
1s UdpEchoServerApplication:StartApplication()
Sent 1024 bytes to 10.1.1.2
2.25732s Received 1024 bytes from 10.1.1.1
2.25732s Echoing packet
Received 1024 bytes from 10.1.1.2
Sent 1024 bytes to 10.1.1.2
3.25732s Received 1024 bytes from 10.1.1.1
3.25732s Echoing packet
Received 1024 bytes from 10.1.1.2
10s UdpEchoServerApplication:StopApplication()
UdpEchoServerApplication:DoDispose()
UdpEchoServerApplication:~UdpEchoServer()

您現在已經發送了兩個包裹。很簡單,不是嗎?
可以看到,作為 ns-3 用戶,可以使用命令列參數系統來操作全域值和屬性。如果您是模型作者,您可以為物件新增屬性,並且使用者可以透過命令列系統自動配置這些屬性。如果您是腳本作者,您可以為腳本新增變數並將它們無縫插入到命令列系統中。

5.3 使用追蹤系統

建模的全部目的是產生輸出以進行進一步研究,而 ns-3 追蹤系統是其主要機制。由於 ns-3 是一個 C++ 程序,因此可以使用從 C++ 程式產生輸出的標準方法:

#include <iostream>
...
int main ()
{
...
std::cout << "The value of x is " << x << std::endl;
...
}

您甚至可以使用日誌記錄模組為您的解決方案添加一些結構。這種方法引起了許多已知的問題,因此我們提供了一個通用的事件追蹤子系統來解決這些問題。

ns-3 追蹤系統的主要目標是:

  • 對於基本任務,追蹤系統應該允許使用者為流行來源產生標準追蹤並選擇產生追蹤的物件;

  • 中級用戶應該能夠擴展追蹤系統以更改生成的輸出格式或插入新的追蹤來源,而無需修改模擬器核心;

  • 進階用戶可以修改模擬器核心以新增新的追蹤來源和接收器。 ns-3追蹤系統建立在獨立追蹤來源和接收者的原則上,以及連結來源和消費者的統一機制之上。

ns-3追蹤系統建立在獨立追蹤來源和接收器的原則以及連接來源和接收器的統一機制的基礎上。追蹤來源是可以發出模擬中發生的事件訊號並提供對感興趣的基礎資料的存取的物件。例如,追蹤來源可以指示網路設備何時接收到資料包,並使資料包的內容可供有興趣的追蹤接收者使用。

追蹤來源本身是無用的,除非它們與程式碼的其他部分“耦合”,這些程式碼實際上對接收器提供的資訊做了一些有用的事情。追蹤器是追蹤來源提供的事件和資料的使用者。例如,您可以建立一個追蹤接收器,它將(連接到上一個範例的追蹤來源時)列印出接收到的資料包中感興趣的部分。

這種明確分離的基本原理是允許使用者將新的接收器類型連接到現有的追蹤來源,而無需編輯和重新編譯模擬器核心。因此,在上面的範例中,使用者可以在其腳本中定義新的追蹤器,並僅透過編輯使用者腳本將其連接到模擬核心中定義的現有追蹤來源。

在本教程中,我們將介紹一些預先定義的來源和接收器,並展示如何以使用者最少的努力來配置它們。有關高級追蹤配置的信息,請參閱 ns-3 手冊或操作方法部分,包括擴展追蹤命名空間和建立新的追蹤來源。

5.3.1 ASCII 跟踪

ns-3 提供了幫助程式功能,該功能提供了低階追蹤系統,可協助您在設定簡單的封包追蹤時了解詳細資訊。如果啟用此功能,您將看到 ASCII 檔案中的輸出。對於熟悉 ns-2 輸出的人來說,這種類型的追蹤類似於 輸出.tr,它是由許多腳本產生的。

讓我們言歸正傳,將一些 ASCII 追蹤結果添加到我們的 scrap/myfirst.cc 腳本中。就在通話前 Simulator :: Run (),新增以下程式碼行:
AsciiTraceHelper ascii;

pointToPoint.EnableAsciiAll (ascii.CreateFileStream ("myfirst.tr"));

與許多其他 ns-3 習慣用法一樣,此程式碼使用輔助物件來建立 ASCII 追蹤。第二行包含兩個巢狀方法呼叫。 “內部”方法 建立檔案流() 使用匿名物件習慣用法在堆疊上建立檔案流物件(沒有物件名稱)並將其傳遞給被呼叫的方法。我們將來會更深入地討論這個問題,但此時您需要知道的是您正在建立一個表示名為的檔案的對象 myfirst.tr 並將其傳輸到 ns-3。我們委託 ns-3 在其整個生命週期中照顧所創建的對象,在此期間它解決了由與 C++ 流對象複製構造函數相關的鮮為人知(有意)限制引起的問題。

外部呼叫 啟用AsciiAll() 告訴助手您希望在所有點對點設備連接的模擬中包含 ASCII 跟踪,並且您希望(指定的)跟踪接收器以 ASCII 格式記錄數據包移動信息。

對於熟悉 ns-2 的人來說,追蹤事件相當於記錄事件「+」、「-」、「d」和「r」的已知追蹤點。
現在您可以建立腳本並從命令列運行它:

$ ./waf --run scratch/myfirst

與之前很多次一樣,您將看到來自 Waf 的幾條訊息,然後「『建置』成功完成」以及來自正在運行的程式的一些訊息。

程式運行時會建立一個名為 myfirst.tr。由於工作性質 瓦夫,預設情況下,檔案不是在本機目錄中建立的,而是在儲存庫的頂級目錄中建立的。如果你想改變保存軌跡的路徑,那麼你可以使用Waf參數來指定它 --cwd。我們還沒有這樣做,因此要在您最喜歡的編輯器中查看 ASCII 追蹤檔案 myfirst.tr,我們需要導航到儲存庫的頂級目錄。

解析 ASCII 追蹤

其中包含相當密集的大量信息,但您首先需要注意的是該文件由單獨的行組成。如果您將觀察窗口擴大得更寬,這將變得清晰可見。

文件中的每一行對應一個追蹤事件。在這種情況下,我們追蹤模擬中每個點對點網路設備中存在的傳輸佇列中的事件。傳輸佇列是點對點連結中每個資料包必須經過的佇列。請注意,追蹤檔案中的每一行都以單一字元開頭(後面有一個空格)。該符號將具有以下含義:

+:設備佇列上發生了排隊操作;
-:設備佇列中發生元素檢索操作;
d:封包被丟棄,通常是因為佇列已滿;
r:資料包被網路設備接收。

讓我們仔細看看追蹤文件中的第一行。我將把它分成幾個部分(為了清晰起見,用縮排表示),行號在左邊:

0 +
1 2
2 /NodeList/0/DeviceList/0/$ns3::PointToPointNetDevice/TxQueue/Enqueue
3 ns3::PppHeader (
4   Point-to-Point Protocol: IP (0x0021))
6   ns3::Ipv4Header (
7     tos 0x0 ttl 64 id 0 protocol 17 offset 0 flags [none]
8     length: 1052 10.1.1.1 > 10.1.1.2)
9     ns3::UdpHeader (
10      length: 1032 49153 > 9)
11      Payload (size=1024)

此擴充追蹤事件的第一部分(第 0 行)是操作。我們這裡有一個+符號,對應的是排隊傳輸的操作。第二部分(第 1 行)是模擬時間,以秒為單位。你可能還記得我們問過什麼 UdpEcho客戶端應用程式 兩秒後開始發送資料包。在這裡我們看到這確實發生了。

追蹤範例的下一部分(從第 2 行開始)顯示哪個追蹤來源產生了此事件(指示名稱空間追蹤)。您可以將追蹤命名空間視為檔案系統命名空間。命名空間的根是 節點列表。這對應於主 ns-3 程式碼中管理的容器。它包含在腳本中創建的所有節點。就像檔案系統的根目錄可以有目錄一樣, 節點列表 我們可以有很多節點。所以 /NodeList/0 行指的是 NodeList 中的空節點,我們通常會將其視為「節點 0」。每個節點都有一個已安裝的設備清單。此清單位於命名空間的下一個位置。可以看到這個trace事件來自 設備清單/0,這是節點中安裝的空設備。

下一個子字串, $ ns3 :: PointToPointNetDevice,告訴哪個設備位於零位置:節點零的設備清單。回想一下,第 0 行中的 + 操作意味著將一個元素新增至裝置的傳輸佇列。這反映在「軌道路徑」的最後一段: 發送隊列/入隊.

跟踪中的其餘部分應該相當直觀。第 3-4 行表示封包被封裝在點對點協定中。第 5-7 行顯示封包具有 IP4 版本標頭並且源自 IP 位址 10.1.1.1 並且旨在用於 10.1.1.2。第 8-9 行顯示該封包具有 UDP 標頭,最後第 10 行顯示有效負載為預期的 1024 位元組。

追蹤檔案中的下一行顯示從同一節點上的傳輸佇列中提取了相同的資料包。

追蹤檔案中的第三行顯示封包是由回顯伺服器主機上的網路設備接收的。我在下面重現了事件。

0 r
1 2.25732
2 /NodeList/1/DeviceList/0/$ns3::PointToPointNetDevice/MacRx
3   ns3::Ipv4Header (
4     tos 0x0 ttl 64 id 0 protocol 17 offset 0 flags [none]
5     length: 1052 10.1.1.1 > 10.1.1.2)
6     ns3::UdpHeader (
7       length: 1032 49153 > 9)
8       Payload (size=1024)

請注意,追蹤操作現在為 r,並且模擬時間已增加到 2,25732 秒。如果您仔細遵循本教學課程,這表示您將網路裝置的 DataRate 和 Link Delay 保留為其預設值。正如您在上一節中看到的那樣,這種時態應該很熟悉。

追蹤來源命名空間條目(第 2 行)已修改,以反映此事件源自節點 1 (/節點列表/1) 且封包被追蹤來源接收(/麥克接收器)。透過查看文件中的剩餘跟踪,您應該相當容易地跟踪數據包在拓撲中的移動。

5.3.2 PCAP 跟踪

ns-3 設備助理也可用於建立 .pcap 格式的追蹤檔案。縮寫詞 PCAP (通常以小寫形式書寫)代表資料包捕獲,實際上是一個 API,其中包括定義 .pcap 檔案格式。可以讀取和顯示這種格式的最受歡迎的程式是 Wireshark的 (以前稱為 空靈)。然而,有許多流量追蹤分析器使用這種資料包格式。我們鼓勵使用者使用許多可用的工具來分析 pcap 痕跡。在本教程中,我們將重點關注使用查看 pcap 跟踪 轉儲.

啟用 pcap 追蹤只需一行程式碼即可完成。

pointToPoint.EnablePcapAll ("myfirst");

將此行程式碼貼到我們剛剛新增的 ASCII 追蹤程式碼之後 暫存/myfirst.cc。請注意,我們只傳遞了字串“myfirst”,而不是“myfirst.pcap”或類似的任何內容。這是因為該參數是前綴,而不是完整的檔案名稱。在模擬過程中,助手實際上會為每個點對點設備建立一個追蹤檔案。檔案名稱將使用前綴、節點號、設備號和後綴「.PCAP“。

對於我們的範例腳本,我們最終會看到名為“myfirst-0-0.pcap“和”myfirst-1-0.pcap”,分別是節點 0-設備 0 和節點 1-設備 0 的 pcap 追蹤。新增程式碼行以啟用 pcap 追蹤後,您可以按通常的方式運行腳本:

$ ./waf --run scratch/myfirst

如果查看發行版的頂級目錄,您應該會看到三個文件: ASCII 追蹤文件 myfirst.tr,我們之前研究過的文件 myfirst-0-0.pcap и myfirst-1-0.pcap - 我們剛剛產生的新 pcap 檔案。

使用 tcpdump 讀取輸出

目前,查看 pcap 檔案最簡單的方法是使用 tcpdump。

$ tcpdump -nn -tt -r myfirst-0-0.pcap
reading from file myfirst-0-0.pcap, link-type PPP (PPP)
2.000000 IP 10.1.1.1.49153 > 10.1.1.2.9: UDP, length 1024
2.514648 IP 10.1.1.2.9 > 10.1.1.1.49153: UDP, length 1024
tcpdump -nn -tt -r myfirst-1-0.pcap
reading from file myfirst-1-0.pcap, link-type PPP (PPP)
2.257324 IP 10.1.1.1.49153 > 10.1.1.2.9: UDP, length 1024
2.257324 IP 10.1.1.2.9 > 10.1.1.1.49153: UDP, length 1024

在垃圾場裡 myfirst-0-0.pcap (客戶端設備)可以看到模擬2秒後發送了echo資料包。如果你看第二個轉儲(myfirst-1-0.pcap),您將看到資料包在 2,257324 秒收到。您將在第二次轉儲中看到資料包在 2.257324 秒返回,最後在第一次轉儲中用戶端在 2.514648 秒收到資料包。

使用 Wireshark 讀取輸出

如果您不熟悉 Wireshark的,有一個網站可以下載程式和文件: http://www.wireshark.org/. Wireshark的 是一個可用於顯示這些追蹤檔案的 GUI。如果您有 Wireshark,則可以開啟任何追蹤檔案並顯示內容,就像使用資料包嗅探器擷取封包一樣。

來源: www.habr.com

添加評論