QualNet 开发者指南笔记
文章目录
一、组件和协议栈
1.1QualNet组件
QualNet模拟器
QualNet Architect
QualNet Analyzer
1.2QualNet协议栈
1.2.1 应用程序层:(4.2)
4.2节有实现的具体细节。
负责流量生成和应用层路由。依赖传输层传递数据。
QualNet已经实现的:
1.固定比特率(CBR)、FTP、Telnet。
2.(应用层路由协议:)RIP、Bellman-Ford、BGP。
1.2.2 传输层:(4.3)
向应用层提供端到端数据传输服务。依赖网络层转发。
QualNet已经实现的:UDP、TCP、RSVP-TE。
1.2.3 网络层:(4.4)
数据转发和排队/调度。IP协议负责包转发,依赖MAC(链路)层。
QualNet已经实现的:
网络层路由协议:AODV、 DSR、OSPF 和 DVMRP.
队列调度协议:FIFO、 RED、 RIO、 WFQ 和 WRR.
1.2.4 链路(MAC)层:(4.5)
QualNet已经实现的:point-to-point、 IEEE 802.3、 IEEE 802.11 和 CSMA.
1.2.5 物理层:(4.6)
有线网络,物理层代码和链路层合并。
QualNet已经实现的: wired point-to-point links、 IEEE 802.3 和 IEEE 802.11.
1.2.6 通信介质 Communication Medium(4.7)
在节点之间传输信号,为物理层服务。
通信介质模型的三个组成部分:
-
信号衰减模型path loss model:free space(自由空间)、two ray(双射线)、Irregular Terrain Model (ITM不规则地形)。
-
衰落模型fading model: Ricean、Rayleig
-
阴影模型shadowing model: constant(常数模型) and lognormal(对数正常模型).
1.2.7 结点移动性Node Mobility(4.8)
和节点布置模型(node placement)、地形模型(terrain)一起工作来模拟结点行为。
包括: 随机路径点模型(random waypoint)、群体移动模型( group mobility)、行人移动模型(pedestrian mobility), 和文件基模型( file-base mobility)。
二、目录和文件组织
文件组织、编译、安装插件、调试。
2.1 目录结构
TABLE 2-1. Default QualNet Subdirectories
Subdirectory | Description |
---|---|
addon | 作为自定义附加组件开发的组件 |
bin | 可执行文件和其他运行时组件,例如dll |
contributed | 与第三方提供的模型相关的文件 |
data | 无线模型库的数据文件,包括天线配置、调制方案和样本地形文件 |
documentation | 文件(用户指南、发行说明等) |
gui | 图形组件,包括图标和GUI配置文件 |
include | QualNet内核头文件 |
installers | 用于补充第三方软件的安装程序 |
interfaces | 将QualNet与第三方工具或者外部网络(如:HLA 、 DIS)连接的代码 |
kernel | 构建过程中使用的QualNet内核对象 |
lib | 构建过程中使用的第三方软件库 |
libraries | 模型库中的模型源代码,如开发人员、无线、多媒体和企业。 Developer, Wireless, and Multimedia & Enterprise. |
license_dir | 构建过程中需要的许可证文件和许可证库 |
main | 内核源文件和Makefiles |
scenarios | 示例场景 |
2.2 Windows上的编译
命令行编译:
1.Verify that the environment variables are properly set by typing the following command: cl.
The following output verifies that the configuration is correct:
Microsoft ® 32-bit C/C++ Optimizing Compiler Version 14.00.50727.42 for 80x86
Copyright © Microsoft Corporation. All rights reserved. usage: cl [ option… ] filename… [ /link linkoption… ]
Start > All Programs > Microsoft Visual Studio
2010 > Visual Studio Tools > Visual Studio 2010 x64 Win64 Command Prompt
三、仿真引擎
1.离散事件概述
QualNet是一个离散事件模拟器。在离散事件仿真系统建模为它的发展随着时间的推移,表示系统状态变化的瞬间,当一个事件发生时,事件被定义为一个瞬时发生导致系统改变其状态或执行特定的动作。事件的例子有:报文的到达,周期性的告警通知路由协议向邻居发送路由更新,等等。事件发生时要采取的行动的例子有:向相邻层发送数据包、更新状态变量、启动或重启计时器等。
在离散事件模拟中,模拟器维护一个事件队列。与每个事件相关的是它的事件时间,即事件设定发生的时间。事件队列中的事件按事件时间排序。模拟器还维护一个用于模拟时间的模拟时钟。模拟时钟以离散的步骤推进,如下所述。
模拟器循环以下步骤,直到模拟结束:
- 模拟器从事件队列中删除第一个事件。
- 模拟器将模拟时钟设置为事件的事件时间。这可能导致模拟时钟提前。
- 模拟器处理事件,即它执行与该事件关联的操作。这可能会导致更改系统状态、调度其他事件,或两者兼而有之。如果其他事件已被安排,则它们可能被安排在当前时间或未来发生。
2.协议的建模
QualNet中的每个节点运行一个协议栈,如图1-1所示。每一层通过使用下面各层的服务,向上面的层提供服务。每个协议都在堆栈的一个层上操作。QualNet中的协议本质上是一个有限状态机。每个协议都可以创建事件,使其改变自己的状态(或执行一些事件处理),或者创建由另一个协议处理的事件。
协议模型的核心是一个事件Dispatcher,它由一个Wait For Event状态和一个或多个Event Handler状态组成(见图3-1)。在Wait For Event状态下,协议等待事件的发生。当协议的事件发生时,协议转换到与该事件对应的事件处理程序状态(例如,当事件1发生时,协议转换到事件1处理程序状态)。在这个Event Handler状态下,协议执行事件对应的动作,然后返回到Wait For Event状态。在事件处理程序状态下执行的操作可能包括更新协议状态,或调度其他事件,或两者兼有。
除了事件调度程序之外,协议有限状态机还有另外两种状态 : 初始化状态和终结状态。在初始化状态下,协议读取外部输入来配置其初始状态。然后协议转换到Wait For Event状态。在模拟结束时自动转换到结束状态。在finalize状态下,打印仿真过程中采集到的协议统计信息。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6VSvjh98-1648295048996)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\1648288277543.png)]
3.事件和消息
- 用来表示事件的类称为消息。
- 消息保存有关事件的信息,如事件类型和相关数据。
- 事件和消息这两个术语可以互换使用。
包事件
模拟层之间或者节点之间的数据包交换。还用于建模同一层不同实体之间的通信。
包事件用于模拟包在网络上的传输。包被定义为协议栈任何一层的虚拟或真实数据单元。当一个节点需要向QualNet协议栈中的相邻层发送一个包时,它会在相邻层调度一个包事件。相邻层发生报文事件,模拟报文的到达。
通过协议栈发送报文:
当位于一个节点的特定层的协议将数据包发送到另一个节点的同一层的相应协议时,数据包将通过发送节点的协议栈、通过网络向下传递,然后通过接收节点的协议栈向上传递。在发送节点的协议栈的每一层,当数据包被发送到下面的层时,报头信息被添加到数据包中。每一层负责将数据包发送到相邻的一层。在接收节点,每一层剥离其报头并将包发送给上面的层,直到原始包最终可用于接收协议。以原始协议位于应用层为例,如图3-5所示。这个过程中的步骤如下所示。
• 原始协议使用API MESSAGE_Alloc创建一个新消息。协议使用API MESSAGE_PacketAlloc创建此消息的包字段。
• 协议将要发送给接收节点的数据放在消息的包字段中,适当地设置消息的其他字段,并使用API MESSAGE_Send将消息发送到下一层(在本例中为传输层)。函数MESSAGE_Send在一个参数指定的延迟之后为下一层安排一个包事件。
• 当传输层协议接收到数据包时,传输层协议使用API MESSAGE_AddHeader将其报头附加到数据包上,并适当地设置报头字段。然后,传输层协议使用API MESSAGE_Send。
• 在协议栈的每一层重复上一步:每一层将其报头添加到包中,并将结果包发送到下一层。
• 当报文到达源节点的物理层时,它为目的节点的物理层调度一个报文接收事件。
• 当目标节点的一层接收到一个数据包时,它使用API MESSAGE_RemoveHeader删除相应的报头,并使用API MESSAGE_Send将结果数据包发送到协议栈的下一层。
• 在协议栈的每一层重复上一步:每一层删除其报头,并将结果包发送到下一层更高的层。
• 当信息包到达目的地节点的应用层时,接收协议处理信息包并使用API MESSAGE_Free释放消息。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BzyM1gdv-1648295048999)(file:///C:/Users/lenovo/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg)]
通过分层接口发送报文:
为了简化协议开发,QualNet提供了特定于层的API函数来发送数据包。协议开发者可以简单地使用特定层的API函数来发送来自特定层的数据包,而不是使用如图3-5所示的原始消息API。特定于层的API函数负责在包通过协议栈时调度相邻层的事件。每一层提供的api封装了消息api,并隐藏了相邻层事件调度的细节,从而为从协议栈的特定层发送数据包提供了易于使用的功能。
特定于层的API函数因层而异。每一层可用的API调用将在第4章的相应部分中讨论。要理解每一层的可用API,请参考在该层操作的协议的QualNet实现的源代码中用于发送数据包的API函数。作为一个例子,我们在本节中概述如何使用应用层中可用的特定于层的数据包交换api。
例子:
应用层的数据包交换分为两类:
• 在传输层用UDP协议交换数据包,应用层通过传输层UDP协议发送报文的API调用如表3-1所示
• 在传输层与TCP协议交换数据包,应用层通过传输层TCP协议发送报文的API调用如表3-2所示
这些函数在QUALNET_HOME/main/app_util.cpp中定义。
这些函数的底层代码使用第3.3.1.2节中讨论的消息api创建和发送消息。
API函数 | 描述 |
---|---|
APP_UdpSendNewData | 在用户指定的延迟后,通过UDP将用户数据发送到目的地。 |
APP_UdpSendNewDataWithPriority | 在用户指定的延迟和用户指定的优先级值之后,通过UDP将用户数据发送到目的地。 |
APP_UdpSendNewHeaderData | 将应用程序头追加到用户数据,并在用户指定的延迟后通过UDP发送到目的地。 |
APP_UdpSendNewHeaderDataWithPriority | 将应用程序头追加到用户数据,并在用户指定的延迟和用户指定的优先级后通过UDP发送到目的地 |
APP_UdpSendNewHeaderVirtualDataWithPriority | 在用户指定的优先级值和用户指定的延迟之后,通过UDP发送一个由应用程序头组成的包,其中包含有用的信息和用户数据,其内容不重要,只会增加资源消耗(队列容量,传输延迟等)。 该函数是重载的,也可以用来发送到特定的端口号。 |
API****函数 | 描述 |
---|---|
APP_TcpSendData | 通过TCP将用户数据发送到目的地。 |
APP_TcpSendNewHeaderVirtualData | 发送由应用程序头组成的包,其中包含有用的信息和用户数据,其内容不重要,只会增加资源消耗(队列容量,传输延迟等),通过TCP到目的地。 |
下图所示为RIP实现功能RipSendResponse的一个代码段
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vt7a8Rh4-1648295049013)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\1648292980404.png)]
API函数APP_UdpSendNewDataWithPriority
用于从应用层发送数据包。RipSendResponse
在QUALNET_HOME/libraries/developer/src/routing_rip.cpp
中实现。注意,最初定义了一个变量response。这个变量是用户数据。然后在这个变量中填充相关信息(RIP命令和RIP版本信息)。接下来,该函数分配目标地址并调用API函数APP_UdpSendNewDataWithPriority
来发送数据包。这个API函数的参数解释如下。
• node:指向节点的指针
• appType:应用程序类型
• sourceAddr:源地址
• sourcePort:源端口号
• destAddr:目的地地址
• outgoingInterface:出接口索引
• 有效负载:指向用户数据的指针
• payloadSize:用户数据大小
• TosType:报文的优先级
• delay:延迟发送数据的时间
• traceProtocol:跟踪协议
通过消息接口发送报文
特定于层的api为通过协议栈发送数据包提供了一种方便的方式。然而,有时可能需要****绕过特定于层的api。这可能是由于协议设计的某些细节。介绍如何通过消息api发送报文。
为了理解消息API的使用,我们来看看特定于层的API APP_UdpSendNewDataWithPriority
的实现,它在RipSendResponse
函数中被使用(参见章节3.3.2.1.1),用来将一个包从应用层发送到传输层的UDP协议。
APP_UdpSendNewdataWithPriority
在QUALNET_HOME/main/app_util
中实现。如图3-7所示。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8hrj5qlo-1648295049019)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\1648294094161.png)]
函数APP_UdpSendNewDataWithPriority
使用API MESSAGE_Alloc
分配一个消息变量msg
。然后调用MESSAGE_PacketAlloc
来分配消息的包字段。MESSAGE_PacketAlloc
的第三个参数payloadSize
,用于设置数据包字段的大小。一旦调用了MESSAGE_PacketAlloc
,就可以使用消息结构中的packet
字段来访问这个空间。API函数MESSAGE_ReturnPacket
用于访问消息的packet
字段。
然后使用memcpy
函数将用户数据复制到packet
字段中。额外的信息可以存储在消息的infoArray[0].info
字段中(参见章节3.3.1.1.1),它是通过API MESSAGE_InfoAlloc
分配的。(MESSAGE_InfoAlloc
等价于使用MESSAGE_AddInfo
使用INFO_TYPE_DEFAULT
为info字段类型,分配infoArray的0th元素。
MESSAGE_ReturnInfo
API用于访问消息的infoArray[0].info
字段。在消息的infoArray[0].info
字段中保存信息后,通过MESSAGE_Send
功能将报文发送到下一层。(当APP_UdpSendNewDataWithPriority
的第一步使用MESSAGE_Alloc
分配消息时,layerType
、protocolType
和eventType
字段设置为分别为TRANSPORT_LAYER
、TransportProtocol_Udp
和MSG_TRANSPORT_FromAppSend
。
在APP_UdpSendNewDataWithPriority
的最后一步调用MESSAGE_Send
的结果是在传输层的UDP协议中调度一个MSG_TRANSPORT_FromAppSend
事件,在延迟之后由MESSAGE_Send
的第三个参数指定。)
当来自应用层的数据包到达传输层的UDP协议时,UDP会在数据包上附加一个报头并将其发送给下一层(网络层)。这是在UDP函数TransportUdpSendToNetwork中完成的,它是在QUALNET_HOME//libraries/developer/ src/transport_udp中实现的。如图3-8所示。
定时器事件:
模拟超时,是协议内部的事件。
4.消息类:
class Message
{
private:
static const UInt8 SENT = 0x01; // 消息正在被发送。
...
public:
//默认构造函数不应该被使用,除非在特定的情况下。消息在这里没有初始化。
Message();
...
short layerType; // 接收消息的层。
short protocolType; //接收层中消息的协议。
short instanceId; // 将消息传递给哪个实例(对于一个协议或应用程序的多个副本)
short eventType; // 消息的事件类型。
...
int packetSize; // 数据包字段的大小。
char *packet; // 模拟数据包,包括包头。
...
int virtualPayLoadSize; // 虚拟数据的大小。
clocktype packetCreationTime; // 如果这是一个包,它的创建时间。
...
std::vector<MessageInfoHeader> infoArray;
...
}
下面将解释Message类的一些成员。
• layerType:这是与事件关联的层。
• protocolType:该事件关联的协议类型。
• instanceId:如果一个协议有多个实例,该字段表示与该事件相关的协议实例。
• eventType:事件的类型。事件类型在QUALNET_HOME/include/api.h中列出。
• packet:如果类实例被用来模拟网络中的实际数据包,这个字段存储数据包。不同层添加的头信息包含在该字段中。
• packetSize:数据包字段的大小。
• virtualPayLoadSize:这部分用户数据的大小,它的内容不重要,因此不分配任何内存,但它的大小影响传输时间和缓冲区大小的计算。
• packetCreationTime:如果类实例被用来模拟网络中的实际数据包,这个字段存储数据包的创建时间。
• infoArray:这是一个存储附加信息的阵列,用于处理事件,以及需要在层或节点之间传输的信息。该字段的详细信息请参见3.3.1.1.1节。
5.消息API
QualNet中有几个API函数可用于消息操作。消息api可以从任何层调用。这些函数的原型可以在文件QUALNET_HOME/include/ message.h中找到。这些函数的实现代码可以在文件QUALNET_HOME/main/ message.cpp中找到。下面列出了一些消息api。参考API参考指南或文件message.h可以看到消息api及其参数的完整列表。
MESSAGE_Send://这个函数将指定的事件(消息)安排在指定的延迟之后发生。
//在通过调用****MESSAGE_Send****来调度事件之后,不要更改消息类实例的任何字段。**
MESSAGE_Alloc://该函数分配一个新的消息结构,并将结构的layerType、protocolType和eventType字段设置为作为参数传递给函数的值。
MESSAGE_Free://这个函数释放指定的消息。消息的packet和infoArray字段被释放,然后消息本身被释放。
MESSAGE_AddInfo://该函数分配指定消息的infoArray字段的一个元素。相关信息字段的类型和大小作为参数传递。
MESSAGE_ReturnInfo://这个函数接受infoType作为参数,并返回一个指向指定消息的相关信息字段的指针。
MESSAGE_ReturnInfoSize://该函数以infoType为参数,返回指定消息的相关infoSize字段。
MESSAGE_PacketAlloc://该函数分配指定消息的包字段。包字段的大小和创建包的协议名称作为参数传递。
MESSAGE_ReturnPacket://这个函数返回一个指向指定消息的包字段的指针。
MESSAGE_ReturnActualPacketSize://这个函数返回指定消息的packetSize字段。
MESSAGE_CancelSelfMsg://这个函数取消了之前调度的消息。
//不要显式释放消息或在取消消息后重用消息。函数****MESSAGE_CancelSelfMsg****也释放了与消息相关的内存。**
MESSAGE_AddHeader://这个函数向包含在指定消息中的包添加一个报头。消息的packetSize字段增加报头的大小,并且数据包字段指向新分配的报头。报头大小和添加报头的协议名称作为参数传递。
MESSAGE_RemoveHeader://这个函数从指定消息中包含的包中删除一个头。消息的packetSize字段根据消息头的大小递减,并且数据包字段指向被删除的消息头后面的空格。报头大小和删除报头的协议名称作为参数传递。
MESSAGE_GetLayer://这个函数返回指定消息的layerType字段。
MESSAGE_GetProtocol://这个函数返回指定消息的protocolType字段。
MESSAGE_GetEvent://这个函数返回指定消息的eventType字段。