1. 下表显示了 NS2 和 TCP/IP、OSI七层网络结构的大致对应关系(这个表很有好处哦)

TCP       NS2     OSI 
    应用层
     应用层    应用层

表示层

会话层
 
 传输层(TCP/UDP)

网络层
  代理(Agent)    传输层

网络层
 
    物理层  节点和连接

(NODE & Link)
  数据链路层

物理层

2. 下面我们将演示 在NS2中实现自己编写的Agent的全过程,对于传输层和网络层模拟的研究有着基本的指导意义。 
    实现的参考: Marc Greis‘ Tutorial http://www.isi.edu/nsnam/ns/tutorial/ 
               VII. A new protocol for NS
    我用的是NS-2.34版本的, 其中已经添加了 Agent/Ping,此处仅验证 Marc Greis' Tutotial的可行性;
    
    下面是整个实现的过程:(过程是一致的,但是目录、实现的细节有些区别,注意哦,哈哈!)
    (相信在看过前面的实例,并具体运行测试过后,阅读并实现以下的Task,应该是不会有问题的!)
 
    一般在NS2下实现一个协议,主要是编写.h 和 .cc 两个文件,但是当问题比较复杂后,可能需要编写很多个文件,但一般都是每个 .h 文件就对应着相应的 .cc 文件。 .h 文件,一般用于定义 数据包格式和类, 而 .cc文件,需要完成以下工作:
    (1). .h中定义的类方法的实现, 
    (2). TclClass的编写,为TCL脚本提供接口, 此函数的代码基本不变,每次只做简单的替换即可;
    (3). TCL脚本的变量和 C++ 中类的变量的绑定函数,也只直接对应着填写即可!
    (4). command 函数,是Agent类与TCL的接口,TCL脚本的命令直接作用于该函数!
    (5). recv函数,是Agent类功能实现的关键; 网络中对于数据包的分类、转发和处理的操作都是通过这个函数来实现的! 参看具体的应用再编写吧!
 
    以下演示Marc Greis‘ Tutorial 中Agent/Ping的实现过程,最后给出测试结果!
    
   1.  ping.h 文件:

#ifndef ns_ping_h
#define ns_ping_h

#include "agent.h"
#include "tclcl.h"
#include "packet.h"
#include "address.h"
#include "ip.h"

struct hdr_ping {
  char ret;         //从源端出来时值为 0, 从目的端回来时值为 1; 
  double send_time; //源端发送的时间锉,用于往返时延的计算;
};
class PingAgent : public Agent {
 public:
  PingAgent();
  int command(int argc, const char*const* argv);
  void recv(Packet*, Handler*);
 protected:
  int off_ping_;  //it will be used to access a packet's ping header
};

#endif

2.  ping.cc 文件:

#include "ping.h"

//以下的两个函数主要完成C++和OTCL的连接,每次可套用,做相应的修改即可!

static class PingHeaderClass : public PacketHeaderClass {
public:
  PingHeaderClass() : PacketHeaderClass("PacketHeader/Ping", 
     sizeof(hdr_ping)) {}
} class_pinghdr;

static class PingClass : public TclClass {
public:
  PingClass() : TclClass("Agent/Ping") {}
  TclObject* create(int, const char*const*) {
    return (new PingAgent());
  }
} class_ping;

PingAgent::PingAgent() : Agent(PT_PING)   //PingAgent的构造函数
{
  bind("packetSize_", &size_);
  bind("off_ping_", &off_ping_);
}

/×  The function 'command()' is called when a Tcl command for the   class 'PingAgent' is executed.    In our case that would be '$pa send' (assuming 'pa' is an instance of the Agent/Ping class), because we want to send ping packets from the Agent to another ping agent. You basically have to parse the command in the 'command()' function, and if no match is found, you have to pass the command with its arguments to the 'command()' function of the base class (in this case 'Agent::command()').  ×/

// $pa send 命令作为command 函数的输入

int PingAgent::command(int argc, const char*const* argv)
{
  if (argc == 2) {
    if (strcmp(argv[1], "send") == 0) {
      // Create a new packet
      Packet* pkt = allocpkt();
      // Access the Ping header for the new packet:
      hdr_ping* hdr = (hdr_ping*)pkt->access(off_ping_);
      // Set the 'ret' field to 0, so the receiving node knows
      // that it has to generate an echo packet
      hdr->ret = 0;
      // Store the current time in the 'send_time' field
      hdr->send_time = Scheduler::instance().clock();
      // Send the packet
      send(pkt, 0);
      // return TCL_OK, so the calling function knows that the
      // command has been processed
      return (TCL_OK);
    }
  }
  // If the command hasn't been processed by PingAgent()::command,
  // call the command() function for the base class
  return (Agent::command(argc, argv));
}

/×  The function 'recv()' defines the actions to be taken when a packet is received. If the 'ret' field is 0, a packet with the same value for the 'send_time' field, but with the 'ret' field set to 1 has to be returned. If 'ret' is 1, a Tcl function (which has to be defined by the user in Tcl) is called and processed the event. */

void PingAgent::recv(Packet* pkt, Handler*)
{
  // Access the IP header for the received packet:
  hdr_ip* hdrip = (hdr_ip*)pkt->access(off_ip_);
  // Access the Ping header for the received packet:
  hdr_ping* hdr = (hdr_ping*)pkt->access(off_ping_);
  // Is the 'ret' field = 0 (i.e. the receiving node is being pinged)?
  if (hdr->ret == 0) {
    // Send an 'echo'. First save the old packet's send_time
    double stime = hdr->send_time;
    // Discard the packet
    Packet::free(pkt);
    // Create a new packet
    Packet* pktret = allocpkt();
    // Access the Ping header for the new packet:
    hdr_ping* hdrret = (hdr_ping*)pktret->access(off_ping_);
    // Set the 'ret' field to 1, so the receiver won't send another echo
    hdrret->ret = 1;
    // Set the send_time field to the correct value
    hdrret->send_time = stime;
    // Send the packet
    send(pktret, 0);
  } else {
    // A packet was received. Use tcl.eval to call the Tcl
    // interpreter with the ping results.
    // Note: In the Tcl code, a procedure 'Agent/Ping recv {from rtt}'
    // has to be defined which allows the user to react to the ping
    // result.
    char out[100];
    // Prepare the output to the Tcl interpreter. Calculate the round
    // trip time
    sprintf(out, "%s recv %d %3.1f", name(), 
            hdrip->src_ >> Address::instance().NodeShift_[1], 
     (Scheduler::instance().clock()-hdr->send_time) * 1000);
    Tcl& tcl = Tcl::instance();
    tcl.eval_r(out);
    // Discard the packet
    Packet::free(pkt);
  }
}

简单的Protocol, 这两个文件可以直接放在 ~/ns-2.xx/ 目录下。 复杂的情况,将在后面的实例中做更详尽的描述!
 
 3. NS2中应该做的必要修改
   
    (1). "packet.h" 文件
         enum packet_t {
              PT_TCP,
              PT_UDP,
              ......
           // insert new packet types here
              PT_TFRC,
              PT_TFRC_ACK,
              PT_PING,    //  packet protocol ID for our ping-agent
              PT_NTYPE    // This MUST be the LAST one
          };
 
         packet.h : home/username/ns-allinone-2.34/ns-2.34/common/
 
         我的机子上的 packet.h 对 PT_PING 的定义没有采用枚举,而是直接定义成为了static const packet_t 类型的:
         .......
         static const packet_t PT_TFRC = 42;
         static const packet_t PT_TFRC_ACK = 43;
         static const packet_t PT_PING = 44;
         .......
 
         修改  class p_info {} 结构:
         
         class p_info {
            public:
                  p_info() {
      name_[PT_TCP]= "tcp";
                  name_[PT_UDP]= "udp";
                  ...........
                  name_[PT_TFRC]= "tcpFriend";
                  name_[PT_TFRC_ACK]= "tcpFriendCtl";
                  name_[PT_PING]="Ping";  //添加的

name_[PT_NTYPE]= "undefined";
             }
                 .....
            }

我机子上该类的实现为:
         class p_info {
               public:
                     p_info() {
                           initName();  //因此我应该到initName()中做修改!!
                     }
               ............
               static void initName()
              {
                    if(nPkt_ >= PT_NTYPE+1)
                            return;
                    char **nameNew = new char*[PT_NTYPE+1];
                    ...............
 
                    name_[PT_TFRC]= "tcpFriend";
                    name_[PT_TFRC_ACK]= "tcpFriendCtl";
                    name_[PT_PING]="ping";  //应该添加!
                    ................
                    ................
 
               
    (2).  tcl/lib/ns-default.tcl 文件  (这个好找到的哦!)
          ##Agent set seqno_ 0 now is gone
          ##Agent set class_ 0 now is gone
 
           Agent/Ping set packetSize_ 64
 
    (3).  tcl/lib/ns-packet.tcl 文件
                  { SRMEXT off_srm_ext_}
                  { Ping off_ping_ }} {
                  set cl PacketHeader/[lindex $pair 0]
         
          我机子上:
          #   { UMP off_ump_  }
          #   { TFRC off_tfrm_ }
          #   { Ping off_ping_ }  //注意,已经注释掉了
          #   { rtProtoLS off_LS_ }
          #   { MPLS off_mpls_ }
          #   { GAF off_gaf_ } 
          #   { LDP off_ldp_ }
          #   } {
          #   create-packet-header [lindex $pair 0] [lindex $pair 1]
          #  }
          而是利用 foreach prot {} {} 结构实现的
          foreach prot {
          # Common:
                  Common 
                  Flags
                  IP  # IP
         # Routing Protocols:
           ........
         # Application-Layer Protocols:
                  Message # a protocol to carry text messages
                  Ping  # Ping
                  PBC     # PBC
           ........
         # Other:
                Encap   # common/encap.cc
                IPinIP  # IP encapsulation 
                HDLC    # High Level Data Link Control
               } {
                        add-packet-header $prot
               }
   
       具体实现机制还不甚了解,但是这不影响的, 只要一葫芦画瓢即可!  哈哈!
           
    (4).  Makefile 文件 (这个文件肯定知道在哪吧? 哈哈,要不知道,就很可惜,你不大可能能测试通过,Good Luck!)
          sessionhelper.o delaymodel.o srm-ssm.o \
          srm-topo.o \
          ping.o \
          $(LIB_DIR)int.Vec.o $(LIB_DIR)int.RVec.o \
          $(LIB_DIR)dmalloc_support.o \
 
       在我机子上,如下:
 diffusion/hash_table.o diffusion/routing_table.o diffusion/iflist.o \
 tcp/tfrc.o tcp/tfrc-sink.o mobile/energy-model.o apps/ping.o tcp/tcp-rfc793edu.o \
 queue/rio.o queue/semantic-rio.o tcp/tcp-sack-rh.o tcp/scoreboard-rh.o \
 
      从中,可以看到,我机子上ping.h 和 ping.cc 文件都存储在ns-2.34/apps/ 文件夹中, (make命令之后理应在该文件夹下生成ping.o的,但是没找到,而ping.tcl代码却执行如常,哈哈,以后再来思考这点吧!)
          
    (5). 执行make 命令
      或 
         make clean
         make depend
         make
    (6). 运行ping.tcl 进行测试:
         ping.tcl 文件
   #Create a simulator object
set ns [new Simulator]

#Open a trace file
set tracefd [open out.tr w]
$ns trace-all $tracefd
set nf [open out.nam w]
$ns namtrace-all $nf

#Define a 'finish' procedure
proc finish {} {
        global ns nf
        $ns flush-trace
        close $nf
        exec nam out.nam &
        exit 0
}

#Create three nodes
set n0 [$ns node]
set n1 [$ns node]
set n2 [$ns node]

#Connect the nodes with two links
$ns duplex-link $n0 $n1 1Mb 10ms DropTail
$ns duplex-link $n1 $n2 1Mb 10ms DropTail

#Define a 'recv' function for the class 'Agent/Ping'
Agent/Ping instproc recv {from rtt} {
 $self instvar node_
 puts "node [$node_ id] received ping answer from \
              $from with round-trip-time $rtt ms."
}

#Create two ping agents and attach them to the nodes n0 and n2
set p0 [new Agent/Ping]
$ns attach-agent $n0 $p0

set p1 [new Agent/Ping]
$ns attach-agent $n2 $p1

#Connect the two agents
$ns connect $p0 $p1

#Schedule events
$ns at 0.2 "$p0 send"
$ns at 0.4 "$p1 send"
$ns at 0.6 "$p0 send"
$ns at 0.6 "$p1 send"
$ns at 1.0 "finish"

#Run the simulation
$ns run

【NS2】新协议的添加示例(转载)的更多相关文章

  1. 向SQL Server 现有表中添加新列并添加描述.

    注: sql server 2005 及以上支持. 版本估计是不支持(工作环境2005,2008). 工作需要, 需要向SQL Server 现有表中添加新列并添加描述. 从而有个如下存储过程. (先 ...

  2. Atitit.aticmd v4  新特性q39 添加定时器释放功能

    Atitit.aticmd v4  新特性q39 添加定时器释放功能 V1  实现兰cmd V2 标准输入,标准输出,标准错误与重新定向 V3  stdout stderr统一重新定向 V4  添加定 ...

  3. Nginx 简单的负载均衡配置示例(转载)

    原文地址:Nginx 简单的负载均衡配置示例(转载) 作者:水中游于 www.s135.com 和 blog.s135.com 域名均指向 Nginx 所在的服务器IP. 用户访问http://www ...

  4. OAF在打开的新页面中添加按钮,功能是关闭当前页面

    OAF在打开的新页面中添加按钮,功能是关闭当前页面 javascript:close()

  5. cesium编程中级(一)添加示例到Sandcastle

    cesium编程中级(一)添加示例到Sandcastle 添加示例到Sandcastle在cesium编程入门(七)3D Tiles,模型旋转中提到过,这里是一份完整的说明 创建例子 开启node服务 ...

  6. 第44章 添加新协议 - Identity Server 4 中文文档(v1.0.0)

    除了对OpenID Connect和OAuth 2.0的内置支持之外,IdentityServer4还允许添加对其他协议的支持. 您可以将这些附加协议端点添加为中间件或使用例如MVC控制器.在这两种情 ...

  7. 【NS2】NS2机制浅显分析一下(转载)

    [我在之前看的是以ping协议为实例来理解TclCL机制和分裂对象模型] 本文以channel实例的创建过程为例,试图说明ns2的分裂机制,请在阅读本文前阅读<The NS Manual> ...

  8. 简单的C#TCP协议收发数据示例

    参考:http://www.cnblogs.com/jzxx/p/5630516.html 一.原作者的这段话很好,先引用一下: Socket的Send方法,并非大家想象中的从一个端口发送消息到另一个 ...

  9. PPTP协议握手流程分析--转载

    一  PPTP概述   PPTP(Point to Point Tunneling Protocol),即点对点隧道协议.该协议是在PPP协议的基础上开发的一种新的增强型安全协议,支持多协议虚拟专用网 ...

随机推荐

  1. pytorch 加载训练好的模型做inference

    前提: 模型参数和结构是分别保存的 1. 构建模型(# load model graph) model = MODEL() 2.加载模型参数(# load model state_dict) mode ...

  2. Django项目:CRM(客户关系管理系统)--36--28PerfectCRM实现King_admin编辑限制

    #admin.py # ————————01PerfectCRM基本配置ADMIN———————— from django.contrib import admin # Register your m ...

  3. PHP的安全性问题,你能说得上几个?

    一.SQL注入 所谓SQL注入,就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令.具体来说,它是利用现有应用程序,将(恶意)的SQL命令 ...

  4. 【springmvc学习】常用注解总结

    @Controller 在springmvc中,我们用它来告诉前端控制器,他这个类是controller,也就是springmvc的一个对象了,我们在spring.xml配置文件中用<conte ...

  5. 【JZOJ5338】【NOIP2017提高A组模拟8.25】影子 点分治?/ 排序

    题面 65 看到路径问题,就想到了套路:点分治. 对于一个分治中心,先把在其子树的结点的sum和mn求出来,分别表示该节点到分治中心的边权和和点权最小值. 然后把mn离散化,并插入权值线段树中,以su ...

  6. CesiumLab V1.4 分类3dtiles生成(倾斜单体化、楼层房间交互)我记得我是写过一篇关于倾斜单体化的简书文章的,但是现在找不到了。不过找不到也好,就让他随风逝去吧,因为当时我写那篇文章的时候,就发现了cesium实际是有另一种更高效的单体化。就下面这个示例https://cesiumjs.org/Cesium/Build/Apps/Sandcastle/index.html?src=

    我记得我是写过一篇关于倾斜单体化的简书文章的,但是现在找不到了.不过找不到也好,就让他随风逝去吧,因为当时我写那篇文章的时候,就发现了cesium实际是有另一种更高效的单体化.就下面这个示例 http ...

  7. springmvc框架通过web.xml配置404 500错误导向页

    总不能用户输错了url就弹 这玩意吧? <error-page> <error-code>404</error-code> <location>/WEB ...

  8. oracle-约束-序列

    概要图 一 约束 --问题:往表中插入数据的时候,可能出现一些问题,比如:重复插入数据,内容不对(性别) --如何保证数据库表中数据的完整性和一致性呢? 约束是强加在表上的规则或条件,确保数据库满足业 ...

  9. Leetcode96.Unique Binary Search Trees不同的二叉搜索树

    给定一个整数 n,求以 1 ... n 为节点组成的二叉搜索树有多少种? 示例: 输入: 3 输出: 5 解释: 给定 n = 3, 一共有 5 种不同结构的二叉搜索树: 假设n个节点存在二叉排序树的 ...

  10. Git pull 强制覆盖本地文件 - CSDN博客

    Git pull 强制覆盖本地文件 原创 2015年11月16日 22:07:56 标签: git git fetch --all git reset --hard origin/master git ...