0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

在ns2中实现网络协议的方案详解

454398 来源:博客园 作者: 原来... 2020-10-27 14:24 次阅读

用IE不会有显示的问题Firefox有的代码显示不出来;

这篇文章适合初学者,关于初学者应该参考的文档:NS by Example、NS2 Beginners Page都有很多实例可以参考。

本文通过实现一个简单的传输协议来说明如何在 ns2 中实现网络协议,当然,这个协议非常简单,但是在ns2 中实现协议(不是修改)的流程大体就是这个样子的了。我们称这个简单的协议做: simple_trans 协议,我们一步一步来,把 simple_trans 这个协议慢慢做的复杂。首先我想要明确一个概念:什么是在 ns2 中实现网络协议,不把这个问题搞明白我们都不知道自己在做什么。网路协议顾名思义网络上运行的协议,网络是由关系(无论什么关系)组成的,在这个网络上运行的规则(无论是优化网络数据传输还是共享网络信息)就叫做协议,所以我觉得把协议理解为强逻辑的规则是没有问题的。我们实现一个网络协议的前提是这个协议被设计出来,所以我们先要想好我们所要实现的协议是要用来做什么事情的;回到 ns2 , ns2 帮我们实现好了一个框架,这个框架给我们提供了数据包初始化,链路连接,数据包传递路由等功能,也就是说我们只要搭建好我们的逻辑就可以完成协议的模拟了,在 ns2 中我们通过对数据包类型、发送数据包逻辑等等进行控制。这就好比于 ns2 给我们提供了一个铁路网,火车需要的电也有了,火车不够了还可以生产,我们在 ns2 中实现协议就是要对火车进行调度,何时到站,到站后如何运行等等就是协议的内容。

下面就从我们的 simple_trans开始 说起,在这个协议里,首先我们要实现的任务非常简单,简单到什么程度了呢,简单到这个协议就是 a 节点对 b 节点说一句话:“ hi!, I’m a ”。不要笑,这也是一个协议。要在 ns2 上完成这个任务,我们首先要给 simple_trans 这个协议起个名字使得 ns2 可以发出这个协议的数据包并且认得这个协议发出的数据包,现在开始就是第一步了。

1, 在 NS_HOME/common/packet.h 的 enum packet中 加入协议数据包名称 PT_SIMPLE_TRANS_PACKET(必须的,注意不要加错地方,最好加在倒数第二的地方),在 class p_info 中加入name_[PT_SIMPLE_TRANS_PACKET] = “simple_trans_packet” (非必须的)。 Packet.cc 就不要动了。

2, 为了我们协议的独立性、好看性,我们在 NS_HOME 根目录下创建一个文件夹,我就叫他 kgn ,好在 kgn目录(也就是 NS_HOME/kgn )目录下给协议的主角: simple_trans.h&simple_trans.cc 。两个空文件没什么用,下面我们添加协议内容。

3, simple_trans.h 内容:

view plain

#ifndef ns_simple_trans_h

#define ns_simple_trans_h

#include “agent.h”

#include “tclcl.h”

#include “packet.h”

#include “address.h”

#include “ip.h”

#define PROTOCOL_DEFAULT_PORT 1023

#define PROTOCOL_INIT_SYN 1

首先我们引用一些需要用到的头文件,然后我们定义了两个宏,第一个是我们 simple_trans 协议默认传输的端口(这方面如果有所疑问请参考这里 ),第二个是我们仅有的一条指令:同步指令(类似于 TCP 协议中三次握手的第一步,事实上我们的这个协议最终就是要实现一个简化版的三步握手)。继续看:

view plain

struct hdr_simple_trans {

int type;

static int offset_;

inline static int& offset() {

return offset_;

}

inline static hdr_simple_trans * access(const Packet * p) {

return (hdr_simple_trans*) p-》access(offset_);

}

};

这些可以当做领导讲话的开头部分内容。就是定义一个我们协议的头所包括的内容,只有 type 这个是我定义的,其他的内容是 ns2 系统需要的。再继续:

view plain

class simple_trans_agent : public Agent {

public :

simple_trans_agent();

virtual void recv(Packet *, Handler *);

void send_simple_msg(int type, int target);

int get_target(){ return simple_target; }

protected:

int simple_target;

int simple_port;

int command(int argc, const char*const*argv);

};

这里就是我们定义的负责“调度火车”的功能的类了。继承的是 agent 类,在 ns2 中,这个 agent 不可小觑,他是我们可以产生数据包、发送数据包、接收数据包的地方,包括的 target 变量就是数据包发送给的下一个目标。recv 函数会在仿真的过程中“自动”的收到网络上传输的数据包(更深层次的是经过了地址和端口过滤器);send_simple_msg 函数用来执行创建并发送数据的功能; get_target 就不用解释了(接口保护)。接下来是我们在协议制定过程中经常会用到的 timer 的定义, timer 顾名思义是一个定时器(闹钟)在到时时候会调用一个expire (超时)函数,这个被执行的超时函数的内容就是我们所感兴趣的,因为通过 timer 我们可以实现很多逻辑。

view plain

class SYNTimer : public TimerHandler {

public:

SYNTimer(simple_trans_agent* t) : TimerHandler(), t_(t) {

}

inline virtual void expire(Event *);

protected:

simple_trans_agent* t_;

};

我们只要实现 expire 函数即可, timer 的初始和使用见 simpe_trans.cc 文件:

view plain

SYNTimer *syn_timer = new SYNTimer(this);

syn_timer-》resched(1.00);

resched 用来给“闹钟上弦”。

view plain

void SYNTimer::expire(Event *){

t_-》send_simple_msg(PROTOCOL_INIT_SYN, t_-》get_target());

this-》resched(1.00);

}

expire 可以实现我们的“理想”了,譬如,我们到时了就发送我们的 SYN 信息给我们的目标节点(目标节点通过tcl 文件定义,下文中我们会见到)。

4, simple_trans.cc 内容:

view plain

int hdr_simple_trans::offset_;

static class simple_transHeaderClass : public PacketHeaderClass {

public:

simple_transHeaderClass() : PacketHeaderClass(“PacketHeader/simple_trans”,sizeof(hdr_simple_trans)) {

bind_offset(&hdr_simple_trans::offset_);

}

} class_simple_transhdr;

static class simple_transClass : public TclClass {

public:

simple_transClass() : TclClass(“Agent/simple_trans”) {}

TclObject* create(int, const char*const*) {

return (new simple_trans_agent());

}

} class_simple_trans;

simple_trans_agent::simple_trans_agent() : Agent(PT_SIMPLE_TRANS_PACKET),

simple_target(-1), simple_port(PROTOCOL_DEFAULT_PORT) {

bind(“simple_target_”, &simple_target);

bind(“simple_port_”, &simple_port);

}

这个又是八股文,前面几个类照葫芦画瓢即可,如果想要理解是什么意思可以参考我的文章 ,最后一个我们bind 了几个变量,这几个变量通过绑定就可意思让我们通过 tcl 脚本方便的改变他们的值了(不需要重新编译c++ 文件)。

5, 在这个文件中我们主要注意这么几点:

a) 在 send_simple_msg 中数据包的生成 Packet* pkt = allocpkt() ;

b) 数据包的访问: hdr_ip *iph = hdr_ip::access(pkt) ;

c) 数据包 ip 地址和端口号的设定(从这里我们可以看出实现的是一个应用层协议);

d) 发送数据包 send( pkt, 0 ) ,我们可以不用去管 0 是什么意思;

e) Command 命令中不要忘记 return (TCL_OK) 这句话,否则会出错的。

f) 在 recv 函数中实现我们的简单逻辑:显示出我们收到了来自对方的一个 simple_trans 的数据包。

6, 看我们这两个宏命令:

view plain

#define NOW Scheduler::instance().clock()

#define MYNODE Address::instance().get_nodeaddr(addr())

这两个命令给我们编程提供帮助,分别显示系统时间和得到当前节点的地址,也许以后我们会用得着。

7, 在 tcl 脚本中我们需要使用我们的 simple_trans 协议:

view plain

set sT1 [new Agent/simple_trans]

$sT1 set-target [AddrParams addr2id [$n1 node-addr]]

$n0 attach $sT1 1023

set sT2 [new Agent/simple_trans]

$n1 attach $sT2 1023

。.. 。..

$ns at 1.0 “$sT1 begin”

在 tcl 中 new 一个对象,比如 sT1 之后我们要将其 attach 到所属的节点上,注意最后一个 1023 ,这是我们attach 到节点上的给我们 simple_trans 协议分配的端口(深层次的意思是端口分类器会把目的端口是 1023 的数据包分给 sT1 )。 begin 方法是在 command 中实现的,回过头到 simple_trans.cc 中可以看到他的意思,我们可以好好理解一下 command 中函数和 tcl 中的使用关系。

8, 最后一步,就是编译我们整个协议将其键入到 ns 中了,编译前我们要修改 makefile 文件,由于我们是在NS_HOME/kgn 目录中所以, makefile 需要修改的有两个地方:在 INCLUDES = 中加入 -I./kgn ,加入这个的好处就是我们在其他目录使用 simple_trans.h 的时候不用将 kgn 次级目录包含进去;在 OBJ_CC = 中加入 kgn/simple_trans.o / 。好了大功告成,下面回到 NS_HOME 目录下 make 一下,如果成功,我们执行一下我们的 tcl 脚本,看看是不是真的可以运行了呢。

小结:到了这里我们已经添加了一个简单的协议了,好了,有的人会说了,这么简单的协议有什么用呢?那好,我们想一想我们有什么可以改进的吗?以上的协议我们叫做 simpe_trans 协议 0.1 版,那么我们看看 0.2 版给我带来了什么新的变化。

ACK timer

首先要做的就是协议的复杂化,我们将协议改为三次握手过程如图所示:

这个过程对应以下代码(修改simple_trans.h):

view plain

#define PROTOCOL_INIT_SYN 1

#define PROTOCOL_INIT_SYN_ACK 2

#define PROTOCOL_INIT_ACK 3

#define INTERVAL 0.3

class simple_trans_agent;

enum simple_state{

CLOSED,

SYN_SENT,

SYN_RCVD,

ESTABLISHED

};

其中 C-》CLOSED , SS-》SYN_SENT , SR-》SYN_RCVD , E-》ESTABLISHED 为节点可能处于的状态在发送或接受 SYN 和发送 SYN-ACK 接受 ACK 后的变化,而两个 timer 的作用就是使得没有正确到底目的地的数据包可以被重新发送,当然这些 timer 需要在适当的时机取消比如: ack_timer-》cancel() ,取消 timer 使用 cancel 函数即可。具体代码实现参考 0.2 版本的代码。那么现在我们重新 make 编译我们的程序,我们会发现两个节点可以通过三次握手建立起来一个简单的链接了,可以说我们在有这个简单的可以建立连接的程序之后我们马上想到是不是还可以发送数据呢,在 ns2 中,数据的发送,我们常见的如 CBR 或者 FTP ,都可以发送数据但是他们之间有很大的不同 CBR 使用的是 trafficgenerater ,而 FTP 可以看成是一个带发送数据包的 agent ,现在为了让我们的 simple_trans 协议可以在建立起连接以后发送数据,我们就有了两种选择,是继承 trafficgenerater 成为数据发送源呢,还是类似 FTP 使用 agent 发送数据,考虑到我们协议的简洁易懂性,我们直接使用一个 timer ,在每次 timer 到时的时候都利用 simple_trans 的 send 函数发送一个具有 PROTOCOL_DATA 类型(标识是一个数据)的包给通信对端( CN )。在 sendmsg 函数中的实现如下:

view plain

hdr_rtp* rh = hdr_rtp::access(p);

hdr_simple_trans *shdr = hdr_simple_trans::access(p);

hdr_ip* ih = hdr_ip::access(p);

double local_time = NOW;

hdr_cmn::access(p)-》size() = size;

hdr_cmn::access(p)-》timestamp() =

(u_int32_t) (SAMPLERATE * local_time);

rh-》seqno() = seqno++;

ih-》daddr() = simple_target;

ih-》dport() = simple_port;

ih-》saddr() = MYNODE;

shdr-》type = PROTOCOL_DATA;

target_-》recv(p);

这里面我们还可以通过 RTP 协议给每一个包设置序列号,当然也可以在 hdr_simple_trans 中添加一个 seq 的属性。当然我们的协议升级到 0.3 版本后的变化并不只是有这些而已。我们还将 simple_trans 协议的数据包的大小以及发送频率设置成可变的等,具体可以参考 0.3 的代码。

小结:通过以上的设计,我们初步有了一个可以建立连接并发送数据的协议,什么?像是 SIP 协议,没错我们也可以将我们的程序叫做一个简单的会话发起协议,当然你可以实现的更加复杂。至此,我们在 ns2 中添加一个基本网络协议的事情已经完成了,我们注意到:不同的协议使用节点上的不同的端口,这样的协议是不能够影响到诸如路由、无线链路等协议的结果的,所以并不是所有的 ns2 中的协议都可以这么添加,我们还可以修改节点数据结构等方法添加我们自己的一些修改进 ns2 达到仿真的目的,所以这篇文章的目的还是介绍如何在 ns2 中实现协议的基础,我们要根据我们自己的仿真需要来设计我们的程序。通过以上的介绍我们应该掌握的是在 ns2 中发送数据的方法、 ns2 中 timer 的使用方法等等技巧。下面我介绍一个比较有意思的利用我们的 simple_trans 做的协议修改实验:添加无线节点丢包模型,在这里主要参考的是柯志亨老师的实现方法,但是在丢包方面我这里做的对原有协议破坏性更多(更不合理吧),我们将演示当两个无线节点距离增大的时候会丢失数据包并且我们的ACKTimer 以及 SYNTimer 的作用。好,下面就是如何修改的过程了:

在 NS_HOME/mac 目录下的 wireless-phy.cc 的 380 行左右,我们添加如下代码:

view plain

//error model.

hdr_cmn *hdr_err = HDR_CMN(p);

hdr_simple_trans *sh = hdr_simple_trans::access(p);

double ratio = Pr/RXThresh_;

double std = error_modle_lf(ratio);

//printf(“wireless-phy model receive packet ratio=%lf std=%lf/n”,ratio,std);

if (hdr_err-》ptype() == PT_SIMPLE_TRANS_PACKET){

if (!sh-》error){

double tmp=((double)rand())/RAND_MAX;

if (tmp》std){

sh-》error = false;

}else{

sh-》error = true;

//printf(“wireless-phy error model set the packet error/n”);

}

}

}

//end of error model.

我们修改的是 WirelessPhy::sendUp(Packet *p) 函数,在发送数据包之前我们检查数据包中 simple_trans 协议的数据包,并将该数据包中在 hdr_simple_trans 中定义好的 error 属性置为 true (说明这个数据包出错),实现数据包出错分布的函数 error_modle_lf ,这是一个拉格朗日差值函数的实现:

view plain

double error_modle_lf(double ratio){

if(ratio 》1.5)return 0;

double x[6] = {1,1.1,1.2,1.3,1.4,1.5};

double y[6] = {1,0.5,0.3,0.1,0.02,0};

double res = 0;

for(int i = 0; i 《 6; i++){

double temp = 1;

double temp1 = 1;

for(int j = 0; j 《 6; j++){

if(i == j)continue;

temp *= (x[i] - x[j]);

temp1 *= (ratio - x[j]);

}

res += (temp1 / temp) * y[i];

}

return res;

}

显然,我们设计的是无线节点离基站越远对包个数越多。

1, 在 simple_trans.cc 中添加 if( shdr-》error )return ,这样错误的包我们就“装作”收不到了

2, 这里补充说明,柯志亨老师的错误模型实现是基于无线层的,出错了就真的不发或者重发,而我的实现可以说是假的,还会造成无线网络的吞吐,但是还是可以演示无线丢包情况的,具体结果可以编译我称作 0.4 版本的程序运行。可以将包的序列号画出来,这样会更形象的展现丢包情况。

3, 我们将包序列号、包收到时间等等信息都通过 printf 函数打印出来,这样我们就可以不用去考虑如何通过trace 文件来分析得到数据,这种方法有的时候更加有效,我们不必去了解 trace 机制,这也算是一个捷径了。

总结:

Ns2 作为一个在科研领域应用广泛的仿真器有着其内在的很多优势的:开源协议修改自如、分裂设计可设计不同的仿真场景而不需要修改协议代码,但是,我们在做网络协议的研究的时候往往会发现 ns2 现有的协议不足以完成我们的仿真,这是就需要自己设计协议或者修改现有的协议,所以通过对这个简单的 simple_trans 协议的实现我们可以更加的有的放矢,知道如何在那里修改 ns2 的协议,虽然 simple_trans 只是一个超级笨的协议,但是它已经展现了基本的协议设计技巧:集成 agent 、 timer 的使用、协议包头设计等等。如果我们能够再将 ns2有线无线节点结构、路由模块、无线 mac 等等这些代码仔细研读,那么到时你就会发现在 ns2 上面实现一个协议倒不是难事,反而是在协议自身的设计上,这就和我们高级程序语言一样,语言的学习不是难事,而真正熟练的利用语言解决问题才是我们的学习目标。
编辑:hfy

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 仿真器
    +关注

    关注

    14

    文章

    1017

    浏览量

    83726
  • 网络协议
    +关注

    关注

    3

    文章

    267

    浏览量

    21536
  • NS2
    NS2
    +关注

    关注

    4

    文章

    10

    浏览量

    12179
收藏 人收藏

    评论

    相关推荐

    LoRaWAN网络智慧水务的创新解决方案

    随着城市化的不断发展,对水资源的高效管理变得愈发重要。LoRaWAN(低功耗广域网)网络作为一种适用于长距离、低功耗的通信解决方案,正日益智慧水务领域展现其独特的创新应用。本文将探讨LoRaWAN
    的头像 发表于 12-20 16:17 98次阅读
    LoRaWAN<b class='flag-5'>网络</b><b class='flag-5'>在</b>智慧水务<b class='flag-5'>中</b>的创新解决<b class='flag-5'>方案</b>

    mtu不同网络协议的应用

    现代网络通信中,数据包的传输效率和可靠性是衡量网络性能的关键指标。MTU作为网络协议的一个重
    的头像 发表于 12-16 14:22 292次阅读

    dap协议跨链技术的应用

    和通信协议实现不同区块链网络之间的无缝连接。 一、DAP协议概述 DAP协议是一种去中心化应用协议
    的头像 发表于 11-22 15:45 218次阅读

    dap协议的基本概念 dap协议区块链的应用

    DAP协议,即分布式应用协议(Distributed Application Protocol),是一种旨在促进去中心化应用(DApps)区块链网络上的构建和运行的框架。DAP
    的头像 发表于 11-22 15:39 239次阅读

    ipc协议物联网的应用

    联网概述 物联网是一个由互联网、传统电信网、传感器网络等多种网络组成的网络,它允许物体与物体、物体与人、人与人之间的智能互联。物联网的核心在于数据的收集、传输、处理和应用,而通信协议
    的头像 发表于 11-15 14:19 306次阅读

    受电端Type-C设计,PD协议的纯硬件实现详解

    受电端Type-C设计,PD协议的纯硬件实现详解 USB Type-C口电源主要有以下三个角色定义,分别为: ·SOURCE 纯供电方
    的头像 发表于 11-13 11:01 320次阅读

    打破网络边界:P2Link助力实现高效远程访问与内网穿透

    ,解决内网穿透难题,让用户轻松实现远程访问和管理。以下是一些典型的应用场景: 远程办公与文件访问: 远程办公场景,员工常常需要从外部网络访问公司内网
    发表于 10-31 11:54

    华纳云:探讨可用于降低服务器网络延迟的先进的网络协议

    网络延迟是影响在线服务性能的重要因素之一,尤其是实时应用和高交互性网站。通过采用更高级别的网络协议,可以有效降低
    的头像 发表于 09-30 15:14 215次阅读

    NDP协议是怎样帮助IPv6实现网络安全运行的?

    复杂多变的网络环境,确保各节点之间能够高效、准确地发现与通信,是构建稳定、可扩展网络架构的基石。那么IPv6是依靠什么实现
    的头像 发表于 09-10 10:19 384次阅读
    NDP<b class='flag-5'>协议</b>是怎样帮助IPv6<b class='flag-5'>实现</b><b class='flag-5'>网络</b>安全运行的?

    Linux网络协议栈的实现

    网络协议栈是操作系统核心的一个重要组成部分,负责管理网络通信中的数据包处理。 Linux 操作系统
    的头像 发表于 09-10 09:51 302次阅读
    Linux<b class='flag-5'>网络</b><b class='flag-5'>协议</b>栈的<b class='flag-5'>实现</b>

    LwIP协议栈源码详解—TCP/IP协议实现

    电子发烧友网站提供《LwIP协议栈源码详解—TCP/IP协议实现.pdf》资料免费下载
    发表于 07-03 11:22 3次下载

    RA MCU CANFDFSP的配置详解

    RA MCU CANFDFSP的配置详解
    的头像 发表于 06-19 08:06 523次阅读
    RA MCU CANFD<b class='flag-5'>在</b>FSP<b class='flag-5'>中</b>的配置<b class='flag-5'>详解</b>

    工业网络通讯协议有哪些

    随着工业自动化的不断发展,设备之间的互联互通和数据交换变得越来越重要。工业网络通讯协议作为实现这一功能的关键技术,其工业自动化系统扮演着
    的头像 发表于 06-06 18:02 1649次阅读

    网络传输协议有几种?

    协议)、TCP(传输控制协议)、UDP(用户数据报协议)、ICMP(互联网控制报文协议)等。这些协议负责数据传输、路由选择、数据包封装和拆封
    的头像 发表于 04-02 16:04 1431次阅读

    消防预警系统Modbus协议和EthernetIP协议都发挥着重要的作用

    技术,可以用于传输IP数据包。消防预警系统,Ethernet/IP协议也发挥着重要的作用。它可以实现设备之间的实时通信和数据交换,将各个传感器、控制器、报警器等设备连接到
    发表于 01-02 19:34