【致谢】: 感谢关注NS3 Tutorial中文版的广大网友们,感谢NS3翻译小组,感谢初稿的翻译者们,感谢审稿和编辑们!!!   (详细的名单请查看博文翻译Tutorial
 
  声明:引用和转载相关内容,请尊重作者、翻译者和审稿者的辛勤付出,我们坚信开源的思想,我们希望自己的工作可以为更多的人提供帮助,我们不希望成为随意粘贴和任意复制的对象。
5 Tweaking NS3 NS3调整?)
翻译: 杨飞
校稿: Xiaochuan Shen
编辑: ProbibidoAmor
5.2 使用命令行参数 
5.2.1 重载默认属性 
另一个不用编译就可改变ns-3脚本行为的方法就是通过使用命令行参数。我们提供了一种分析命令行属性和基于这些属性自动设置的本地和全局变量的机制。
使用命令行属性系统的第一步就是声明命令行分析器。可以用下面的代码来非常简单的完成。
    int
  main (int argc, char *argv[])
  {
    ... 
 
    CommandLine cmd;
    cmd.Parse (argc, argv);
 
    ...
  }
这两行简单的代码实际上是很有用的。它提供了向ns-3全局变量和属性传递参数的途径。在scratch/myfirst.ccmain函数开始加上这两行代码。编译并运行脚本,同时可以根据下面这两行向脚本寻求帮助。
  ./waf --run "scratch/myfirst --PrintHelp"
这个命令是让Waf运行scratch/myfirst脚本并且通过命令行参数--PrintHelp。引号需要用来确定那个程序得到那个参数。命令行分析器将查看--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.
下面来关注--PrintAttributes选项。在我们查看first.cc脚本时,已经提到过ns-3属性系统。
    PointToPointHelper pointToPoint;
    pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("5Mbps"));
    pointToPoint.SetChannelAttribute ("Delay", StringValue ("2ms"));
我们提到DataRate事实上是PointToPointNetDevice的一个属性。现在使用命令行参数分析器来看一下PointToPointNetDevice的属性。帮助列表需要我们提供一个TypeId。本例中,这个TypeIdns3::PointToPointNetDevice。然后我们可以键入,
  ./waf --run "scratch/myfirst --PrintAttributes=ns3::PointToPointNetDevice"
系统将显示出网络设备这种所有属性。你将看到,
  --ns3::PointToPointNetDevice::DataRate=[32768bps]:
    The default data rate for point to point links
当系统中的PointToPointNetDevice被创立时,DataRate将是一个默认值。我们的脚本用PointToPointHelper重载了这个默认值。现在让我们删除Scratch目录中myfirst.ccSetDeviceAttributethe SetChannelAttribute调用来使用端到端设备使用默认值。
你的脚本现在只需要声明PointToPointHelper而并不做任何设置,
  NodeContainer nodes;
  nodes.Create (2);
 
  PointToPointHelper pointToPoint;
 
  NetDeviceContainer devices;
  devices = pointToPoint.Install (nodes); 
使用Waf(./waf)运行你的脚本,启用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()
回忆上次响应服务器收到数据包的仿真时间,是2.00369秒,
  2.00369s UdpEchoServerApplication:HandleRead(): Received 1024 bytes from 10.1.1.1
现在在2.25732秒收到数据包。这是因为我们将PointToPointNetDevice的数据传输率从每秒5Mbits降到了每秒32768bits
如果我们采用命令行来改用一个新的DataRate,能够加速我们的仿真速度。我们可以根据帮助文档中指明的规则,按照下面的方式来做:
  ./waf --run "scratch/myfirst --ns3::PointToPointNetDevice::DataRate=5Mbps"
这个将DataRate属性的默认值设置回每秒5Mbits。你对结果是不是感到很惊讶?为了设置回脚本最初的行为,我们必须将信道设成光速的延迟。我们可以让命令行系统按照我们刚为网络设备做的那样显示出信道属性:
  ./waf --run "scratch/myfirst --PrintAttributes=ns3::PointToPointChannel"
我们发现信道的Delay属性是通过下面这种方法设置的,
  --ns3::PointToPointChannel::Delay=[0ns]:
    Transmission delay through the channel
我们可以通过命令行来设置这两个默认值。
  ./waf --run "scratch/myfirst
    --ns3::PointToPointNetDevice::DataRate=5Mbps
    --ns3::PointToPointChannel::Delay=2ms"
这样在脚本中精确地设置好了DataRateDelay,我们会重新得到以前的时间:
  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秒服务器端收到数据包。我们可以用这样的方法来设置脚本中的任何属性。特别的我们可以将UdpEchoClient接收到的MaxPackets属性设置为1以外的其他值。
下面请读者尝试一下。记住必须要注释掉我们重载默认属性的地方,并且设置的MaxPackets属性。还必须重新编译脚本。你必须使用命令行的帮助功能来找到设置新的默认属性值得语法格式。一旦设置好,就可以从命令行控制回显数据包数目。作为例子,我们这里给出了命令行的设置。
  ./waf --run "scratch/myfirst
    --ns3::PointToPointNetDevice::DataRate=5Mbps
    --ns3::PointToPointChannel::Delay=2ms
    --ns3::UdpEchoClient::MaxPackets=2"
 
5.2.2 钩挂自己的参数值
你可以在命令行系统中增加自己的钩子。可以通过使用命令分析器中的AddValue方法非常简单的实现。
我们用与以前一个完全不同的方法即钩挂的方法来指定回显数据包数。首先我们增加一个局部变量nPacket。我们将其默认值初始化为1,以便和以前的情形保持一致。为了允许命令行分析器来改变这个值,我们需要将这个值钩挂到分析器上。我们需要增加一个AddValue调用。现在修改scratch/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属性的地方并把它来从1改到nPacket,如下所示,
  echoClient.SetAttribute ("MaxPackets", UintegerValue (nPackets));
现在可以运行脚本,并且采用了--PrintHelp参数,你需要在帮助列表中查看新User参数。
尝试,
  ./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 使用Tracing系统
整个仿真的目的是为了进一步研究生成结果,ns-3 tracing系统就是为了这个目的而制的。ns-3是一个c++程序,c++的标准输入输出设施在ns3种都可以使用:
  #include <iostream>
  ...
  int main ()
  {
    ...
    std::cout << "The value of x is " << x << std::endl;
    ...
  }
你甚至可以使用日志模块来为你的解决方案增加小的结构。但这种方法会产生很多已知的问题,因此我们提供了一种生成事件tracing的子系统来解决我们认为重要的问题。
The basic goals of the ns-3 tracing system are:
Ns-3 tracing系统的基本目标是:
·             对于基本的任务,tracing系统允许用户为常用的tracing发送端生成标准的tracing,可以定制哪些个对象生成tracing
·             中间用户必须能够能够通过扩展tracing系统来修改生成的输出格式,或是在不修改仿真器核心的情况下插入新的tracing发送端;
·             高级用户可以修改仿真器的核心来增加新的tracing发送端和接收端。
Ns-3 tracing系统是建立在独立的tracing发送端和tracing接收端的概念上的,并且有统一的机制来连接发送端和接收端。Trace发送端是可以在仿真过程中产生信号事件并且提供有关数据访问通道。例如,一个trace发送端可以提供一个数据包被一个网络设备接收的时间并且根据接收端的要求提供此数据包的内容。
Trace发送端自身是没有用的,必须要跟接收端提供有用信息的代码段相连Trace接收端是trace发送端提供数据和事件的使用者。例如,可以创建一个输出接收数据包有用部分的trace接收端(当连接到之前例子的trace发送端)
这种发送端和接收端工作的基本原理是允许用户给已有的tracing发送端关联上新类型的接收端,而不需要编辑和重新编译仿真器的核心。因此,以上面为例,一个用户可以在脚本中定义新的tracing接收端,并且可以将其关联到仿真核心中定义的tracing接收端。
本教程中,我们将展示之前定义好的发送端和接收端,并且示范用户怎样轻易的定制它们。如需扩展tracing命名空间,和创建新tracing发送端的高级的tracing配置,请查看ns3手册。
5.3.1 ASCII Tracing 
ns-3提供了封装底层tracing系统的帮助功能,用来提供配置简单数据包trace得更多细节。如果你开启了这个功能,将在ASCII文件中输出结果,这就是为什么这种tracingASCII tracing。对于熟悉ns-2的用户,这种trace类型是跟ns2生成的out.tr相类似的。
我们开始并为我们的脚本增加一些ASCII tracing结果。
首先需要做的是在脚本的GNU GPL说明之后加上下面的包含语句:
  #include <fstream> 
然后,在Simulator::Run()前增加下面的代码:
  std::ofstream ascii;
  ascii.open ("myfirst.tr");
  PointToPointHelper::EnableAsciiAll (ascii);
前两行只是很平常的C++代码,用来打开一个将被写入名为myfirst.tr文件中的数据流。如果对这段代码不熟悉,可以查看你所钟爱的C++教程。代码段中的最后一行代码告诉ns-3在仿真中为所有点到点设备启用ASCII tracing功能;并且你想要用ASCII格式来写出数据流中数据包的移动信息。类似于ns-2trace事件等同于流行的trace“+”“-”“d”“r”事件。
可以编译脚本并用下面的命令行运行:
  ./waf --run scratch/myfirst
跟你之前看到很多次的一样,你将看到来自Waf的信息和编译成功信息。
脚本运行时,程序将创建一个名为myfirst.tr的文件。由于Waf的工作方式,所以这个文件将不在本地目录下创建,而是在ns3根目录中创建。如果你想改变trace文件的保存目录,你可以使用Waf--cwd选项来指定。而我们还没有这么做,因此我们需要转到ns3的根目录,并且用你喜欢的编辑器来查看ASCII trace文件myfirst.tr
5.3.1.1 分析Ascii Traces 
在一个极其密集的文档中有很多信息,但是需要注意的第一件事是在这个文件中有很多分立的行。除非你大大加宽你的窗口,否则是很难看清楚的。
每一行对应了一个trace事件。本例中我们在查看每个点到点设备的传输队列的trace事件。传输队列是任一个目的地为点到点信道的数据包的必经队列。注意trace文件的每行以一个单独的字符开始(后面带有空格)。这个字符具有如下含义:
·             +: 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.
 
·             +:设备队列中的入队操作;
·             -:设备队列中的出队操作;
·             d:数据包被丢弃,通常因为队列已满;
·             r:网络设备接收到数据包。
我们来更详细的看一下trace文件的第一行。为了看得更清晰,我把这一行分成了不同的部分,并在左边标出序号:
  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 protocol 17 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)
 展开后的trace事件的第一行(序号00)代表操作。+字符表示是一个传输队列的入队操作。第二行(序号01)是单位为秒的仿真时间。你或许回忆起我们让UdpEchoClientApplication在两秒开始发送数据包。我们确定这个正在发生。
Trace的下一行(序号02)告诉我们trace发送端发起这个事件(tracing命名空间表示)。你可以认为tracing命名空间有点像一个文件系统命名空间。命名空间的根为NodeList。这个NodeListNS-3核心代码管理的一个容器,此容器包含有一个脚本中创建的所有的节点。。正如一个文件系统在根下有目录,在NodeList下有节点数。字符串/NodeList/0是指NodeList中第0个节点,我们通常认为是"node 0".每个节点中有一个已经安装好的设备列表。这个列表是在命名空间的下一个出现的。可以看到trace事件来自节点中安装的第0个设备DeviceList/0
下一个字符串,$ns3::PointToPointNetDevice告诉我们第0个节点的设备列表的第0个位置的设备类型。回忆序号00处的+操作表示设备的传输队列发生了入队操作,这个在"trace path"TxQueue/Enqueue的最后部分反映出来了。
Trace中剩下的几行是很直观的。序号03-04处表明数据包封装成点到点协议。序号05-07处显示数据包IP版本,发送端IP地址10.1.1.1,接收端IP地址为10.1.1.2。序号08-09出显示数据包的UDP头,最后序号10处表明数据包数据量为1024bytes
trace文件中的下一行显示了这个数据包在这个节点中从传输队列中被移除。
Trace文件的第三行显示了数据包正在被回显服务器所在的节点的网络设备接收。trace如下。
  00 r
  01 2.25732
  02 /NodeList/1/DeviceList/0/$ns3::PointToPointNetDevice/MacRx
  03   ns3::Ipv4Header (
  04     tos 0x0 ttl 64 id 0 protocol 17 offset 0 flags [none]
  05     length: 1052 10.1.1.1 > 10.1.1.2)
  06     ns3::UdpHeader (
  07       length: 1032 49153 > 9)
  08       Payload (size=1024)
 
注意,trace操作现在是r并且仿真时间已经增加到2.25732秒。如果你一直按照本教程来操作,你已经把网络设备的DataRate,和信道Delay设置成默认值。那么对于这个值,你应该觉得眼熟,因为上一章中已经见过这个值。
在第2行中,Trace发送端命名空间条目已经改变,来显示这个事件是来自节点1(/NodeList/1),即数据包的接收trace(/MacRx)。通过查看文件中其他的traces,你可以很容易的跟踪数据包。
5.3.2 PCAP 格式Tracing 
ns-3设备控制器也可以被用来创建.pcap格式的trace文件。缩写pcap(通常为小写)表示packet capture,事实上是包含有定义一个.pcap文件格式的API。可以读取并且显示这种格式的最流行的程序是Wireshark(以前被称为Ethereal)。然而,有很多其他的分析器也使用这个包格式。我们鼓励读者了解其他的分析pcap追踪文件的工具。在本教程中,我们用tcpdump来查看pcap trace
用来启用pcap tracing的代码只有一行,
  PointToPointHelper::EnablePcapAll ("myfirst");
scratch/myfirst.cc中我们刚增加的ASCII追踪代码后面插入这行代码。注意我们刚使用的是字符串''myfirst"而不是"myfirst.pcap"。这是因为这里传递的参数是个前缀,而不是完整的文件名。在仿真过程中,helper将为任何一个点到点设备创建一个追踪文件。文件名将包含预设前缀,节点名,设备名,和".pcap"后缀。
在我们的例子脚本中,我们最终将看到名为"myfirst-0-0.pcap""myfirst-1-0.pcap"。这分别是为节点0设备0,和节点1设备0创建的pcap trace文件。
一旦你已经增加了启用pcap tracing的代码行,你可以以下面的方式来运行脚本:
  ./waf --run scratch/myfirst
 
如果你查看ns3根目录,你将看到三个日志文件:myfirst.tr是我们之前看到过的ASCII trace文件。Myfirst-0-0.pcapmyfirst-1-0.pcap是我们刚生成的新的pcap文件。
5.3.2.1 tcpdump读取结果 
此处最简单的做法就是使用tcpdump来查看pcap文件,
  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(客户端设备)文件中可以看到回显数据包在第二秒被发送。如果查看第二个文件(myfirst-1-0.pcap)可以看到此包在2.257324秒被收到。在第二个文件中可以看到在2.257324秒这个包被回应,最终,在客户端2.514648秒回应的包被收到。
5.3.2.2 Wireshark读取结果
如果你对Wireshark不熟悉,从http://www.wireshark.org/可以下载到程序和文档。
Wireshark是一个可以用来显示trace文件的用户图形接口。如果你安装了Wireshark,可以打开和查看每一个trace文件,就像这些trace是使用包嗅探工具在真正的网络上抓下来一样。

    未完,待续!