4.1兩個以上的節點
現在,我們將邁出一大步:創建幾個tic
模塊並將它們連接到網絡中。現在,我們將使它們的工作變得簡單:一個節點生成一條消息,其他節點繼續沿隨機方向扔消息,直到它到達預定的目標節點為止。NED文件將需要進行一些更改。首先,該Txc
模塊將需要具有多個輸入和輸出門:
simple Txc10
{
parameters:
@display("i=block/routing");
gates:
input in[]; //聲明input,ouput兩種類型的gate數組
output out[];
}
數組[ ]
多個門變成gate向量。向量的大小(門數)將在我們使用Txc構建網絡的地方確定。
network Tictoc10
{
submodules:
tic[6]: Txc10;
connections:
tic[0].out++ --> { delay = 100ms; } --> tic[1].in++;
tic[0].in++ <-- { delay = 100ms; } <-- tic[1].out++;
tic[1].out++ --> { delay = 100ms; } --> tic[2].in++;
tic[1].in++ <-- { delay = 100ms; } <-- tic[2].out++;
tic[1].out++ --> { delay = 100ms; } --> tic[4].in++;
tic[1].in++ <-- { delay = 100ms; } <-- tic[4].out++;
tic[3].out++ --> { delay = 100ms; } --> tic[4].in++;
tic[3].in++ <-- { delay = 100ms; } <-- tic[4].out++;
tic[4].out++ --> { delay = 100ms; } --> tic[5].in++;
tic[4].in++ <-- { delay = 100ms; } <-- tic[5].out++;
}
在這里,我們創建了6個模塊作為模塊向量,並將它們連接起來。
生成的拓撲如下所示:
在此版本中,tic[0]
將生成要發送的消息。這是在函數initialize()
的幫助下完成的,該getIndex()
函數返回向量中模塊的索引。
代碼的forwardMessage()
實質是handleMessage()
每當消息到達節點時我們從中調用的函數。它繪制一個隨機的gate number,並在該gate上發送message。
void Txc10::forwardMessage(cMessage *msg)
{
// 我們選擇隨機的一個gate發送信息
// 我們在0~out[]數組長度之間選擇一個隨機數
int n = gateSize("out");
int k = intuniform(0, n-1);
EV << "Forwarding message " << msg << " on port out[" << k << "]\n";
send(msg, "out", k);
}
當消息到達時tic[3]
,handleMessage()
它將刪除該消息。
請參閱txc10.cc中的完整代碼
練習
您會注意到,這種簡單的“路由”效率不是很高:通常,數據包會在兩個節點之間不斷跳動一段時間,然后再發送到另一個方向。如果節點不將數據包發送回發送方,則可以在某種程度上進行改進。實現這一點。提示:cMessage::getArrivalGate()
, cGate::getIndex()
。請注意,如果消息不是通過門到達的,而是self-messages,則getArrivalGate()
返回NULL
。
來源:tictoc10.ned,txc10.cc,omnetpp.ini
4.2信道和內部類型定義
我們新的網絡定義變得非常復雜冗余,尤其是連接部分。讓我們嘗試簡化它。我們注意到的第一件事是,連接始終使用相同的delay
參數。與簡單模塊類似,可以為連接創建類型(它們稱為信道)。我們應該創建一個指定延遲參數的信道類型,並將該類型用於網絡中的所有連接。
network Tictoc11
{
types:
channel Channel extends ned.DelayChannel {
delay = 100ms;
}
submodules:
如您所見,我們通過添加一個types
字段在網絡定義中定義了新的信道類型。此類型定義僅在網絡內部可見。它稱為局部或內部類型。如果願意,您也可以將簡單模塊用作內部類型。
筆記
我們通過專門內置來創建通道DelayChannel
。(可以在ned
包內找到內置信道。這就是為什么我們在extends
關鍵字后使用完整類型名稱的ned.DelayChannel的原因
)
現在,讓我們檢查一下該connections
部分是如何更改的。
connections:
tic[0].out++ --> Channel --> tic[1].in++;
tic[0].in++ <-- Channel <-- tic[1].out++;
tic[1].out++ --> Channel --> tic[2].in++;
tic[1].in++ <-- Channel <-- tic[2].out++;
tic[1].out++ --> Channel --> tic[4].in++;
tic[1].in++ <-- Channel <-- tic[4].out++;
tic[3].out++ --> Channel --> tic[4].in++;
tic[3].in++ <-- Channel <-- tic[4].out++;
tic[4].out++ --> Channel --> tic[5].in++;
tic[4].in++ <-- Channel <-- tic[5].out++;
}
如您所見,我們僅在連接定義內指定通道名稱。這樣可以輕松更改整個網絡的延遲參數。
來源:tictoc11.ned,txc11.cc,omnetpp.ini
4.3使用雙向連接
如果再檢查一下該connections
部分,我們將意識到每個節點對都通過兩個連接連接。每個方向一個。OMNeT ++ 4支持兩種方式的連接,因此讓我們使用它們。
首先,我們必須定義雙向(或所謂的inout
)門,而不是之前使用的分離input
和output
門。
simple Txc12
{
parameters:
@display("i=block/routing");
gates:
inout gate[]; // declare two way connections
}
新connections
部分如下所示:
connections:
tic[0].gate++ <--> Channel <--> tic[1].gate++;//tic[0].gate[0]<-->tic[1].gate[0]
tic[1].gate++ <--> Channel <--> tic[2].gate++;//tic[1].gate[1]<-->tic[2].gate[1]
tic[1].gate++ <--> Channel <--> tic[4].gate++;
tic[3].gate++ <--> Channel <--> tic[4].gate++;
tic[4].gate++ <--> Channel <--> tic[5].gate++;
}
我們已經修改了門名稱,因此我們必須對C ++代碼進行一些修改。
void Txc12::forwardMessage(cMessage *msg)
{
// In this example, we just pick a random gate to send it on.
// We draw a random number between 0 and the size of gate `gate[]'.
int n = gateSize("gate");
int k = intuniform(0, n-1);
EV << "Forwarding message " << msg << " on gate[" << k << "]\n";
// $o and $i 后綴被用來識別門的雙向輸入輸出
send(msg, "gate$o", k);
}
筆記
gate名稱后的特殊$i和$o后綴允許我們分別使用連接的兩個方向。
來源:tictoc12.ned,txc12.cc,omnetpp.ini
4.4定義我們的message class
在此步驟中,不再對目標地址進行硬編碼tic[3]
-我們繪制一個隨機目標,然后將目標地址添加到消息中。
最好的方法是重寫cMessage的子類並將目標添加為數據成員。手動編碼消息類通常很乏味,因為它包含許多樣板代碼,因此我們讓OMNeT ++為我們生成該類。消息類規范位於tictoc13.msg
:
message TicTocMsg13
{
int source;
int destination;
int hopCount = 0;
}
筆記
有關消息的更多詳細信息,請參見OMNeT ++手冊的第6節。
設置makefile以便調用消息編譯器opp_msgc並生成消息聲明tictoc13_m.h
並tictoc13_m.cc
從消息聲明生成(文件名是根據tictoc13.msg
文件名而不是消息類型名生成的)。它們將包含一個TicTocMsg13
從[ cMessage
]子類生成的類;該類將為每個字段提供getter和setter方法。
我們將tictoc13_m.h
在我們的C ++代碼中包含該代碼,並且可以將其TicTocMsg13
用作任何其他類。
#include "tictoc13_m.h"
例如,我們使用以下幾行generateMessage()
來創建消息並填寫其字段。
TicTocMsg13 *msg = new TicTocMsg13(msgname);
msg->setSource(src);
msg->setDestination(dest);
return msg;
然后,handleMessage()
像這樣開始:
void Txc13::handleMessage(cMessage *msg)
{
TicTocMsg13 *ttmsg = check_and_cast<TicTocMsg13 *>(msg);
if (ttmsg->getDestination() == getIndex()) {
在handleMessage()的參數中,我們將消息作為cMessage*
指針。但是,只有TicTocMsg13
將msg轉換為時,我們才能訪問在中定義的字段TicTocMsg13*
。純C樣式的強制轉換((TicTocMsg13 *)msg
)是不安全的,因為如果消息_不是_a TicTocMsg13
,則程序最終將崩潰,從而導致錯誤,而這是很難發現的。
C ++提供了一種稱為的解決方案dynamic_cast
。在這里,我們使用check_and_cast<>()
OMNeT ++提供的功能:它嘗試通過強制轉換指針dynamic_cast
,如果失敗,則會通過錯誤消息停止模擬,類似於以下內容:
在下一行中,我們檢查目標地址是否與節點的地址相同。該getIndex()
成員函數返回子模塊向量模塊的索引(記住,在我們聲明是NED文件tic[6]: Txc13
,所以節點地址0..5)。
為了使模型的執行時間更長,在消息到達其目標之后,目標節點將生成另一條具有隨機目標地址的消息,依此類推。閱讀完整的代碼:txc13.cc
運行模型時,它將如下所示:
您可以單擊消息以在檢查器窗口中查看其內容。雙擊將在新窗口中打開檢查器。(您必須為此暫時停止模擬,或者要非常快地處理鼠標)。檢查器窗口顯示許多有用的信息;消息字段可/以在“_目錄”_頁面上看到。
來源:tictoc13.ned,tictoc13.msg,txc13.cc,omnetpp.ini
練習
在此模型中,在任何給定時刻只有一條消息正在運行:節點僅在另一條消息到達它們時才生成一條消息。我們這樣做是為了使跟蹤仿真變得更加容易。更改模塊類,以便改為定期生成消息。消息之間的間隔應該是一個模塊參數,返回指數分布的隨機數。