滑动窗口模拟.docx

上传人:b****3 文档编号:5260750 上传时间:2022-12-14 格式:DOCX 页数:27 大小:969.06KB
下载 相关 举报
滑动窗口模拟.docx_第1页
第1页 / 共27页
滑动窗口模拟.docx_第2页
第2页 / 共27页
滑动窗口模拟.docx_第3页
第3页 / 共27页
滑动窗口模拟.docx_第4页
第4页 / 共27页
滑动窗口模拟.docx_第5页
第5页 / 共27页
点击查看更多>>
下载资源
资源描述

滑动窗口模拟.docx

《滑动窗口模拟.docx》由会员分享,可在线阅读,更多相关《滑动窗口模拟.docx(27页珍藏版)》请在冰豆网上搜索。

滑动窗口模拟.docx

滑动窗口模拟

<滑动窗口协议的模拟>

项目设计报告

 

作者:

完成日期:

签收人:

签收日期:

修改情况记录:

版本号

修改批准人

修改人

安装日期

签收人

目录

1需求分析3

1.1问题重述……………………………………………………………………………………3

2概要设计4

2.1原理概述4

2.2主要问题……………………………………………………………………………………4

2.2数据结构5

2.3算法分析6

2.4行为模型9

2.5端口设置……………………………………………………………………………………10

3详细设计10

3.1参数说明……………………………………………………………………………………10

3.2主要函数说明………………………………………………………………………………11

4测试报告…………………………………………………………………………………16

4.1正常发送模式测试…………………………………………………………………………16

4.2错序发送模式测试…………………………………………………………………………17

4.3信道出错测试………………………………………………………………………………18

4.4信道丢帧测试………………………………………………………………………………19

4.5信道丢失确认帧测试………………………………………………………………………20

5简明用户手册……………………………………………………………………………21

6项目评价………………………………………………………………………………22

6.1项目总结……………………………………………………………………………………22

6.2心得体会……………………………………………………………………………………23

6.3有待改进的地方……………………………………………………………………………23

附录一:

参考书目及其他…………………………………………………………………24

1.需求分析

实验目的:

加深对滑动窗口协议的理解

实验任务:

实现对于滑动窗口协议的模拟

实验环境:

1~2台PC机

操作系统:

WindowsXP

开发环境:

MicrosoftVisualC++6.0,可以使用MFC类库

1.1问题重述

界面要求:

项目要求的所有功能应可视,要有简单的界面。

由一台PC(或线程)向另一台PC(或线程)发送数据包时,界面应显示出双方帧个数的变化,帧序号,发送和接受速度,暂停或重传提示等,界面中必须动态显示数据帧的发送和接受情况,包括在相应的窗口详细显示相应的ACK和其他收发数据帧后发出的消息,以表明模拟协议的正确运作过程。

在各种情况下,接受方和发送方窗口应实时显示帧的发送和接受情况,包括序号,时间戳,内容等。

以及窗口的填充和清空情况。

网络接口要求:

两台机器或是一台机器中两个独立的线程模拟发送方与接受方,接收数据的端口初始应为监听状态。

发送方向接受方发起连接,成功后开始发送数据。

接受方要求:

接受方应由固定大小的滑动窗口,并对收到信息缓存。

当发送方速度过快或帧丢失(超时),接受方应发送消息,要求暂停或是重传(停---等协议)。

接受方要求按序向网络层提交收到的帧。

发送方要求:

发送方发送速度可以调节,并可以暂停或是重发。

发送方重传时可仅重传需要的帧。

可指定滑动窗口数目和要发送的帧的总数,停等的超时时间间隔以及发送类型(正常发送,错序发送,以及缺帧,丢帧的现象),发送速率等参数。

 

2概要设计

2.1原理概述

发送方和接受方都维持了一个窗口,窗口内部包含了那些可以接受的序列号。

发送方的窗口大小从0开始,以后可以增大到某一个预设的最大值。

由于发送方可能在将来的某个时刻重传未被确认的帧,所以它必须把已经送出去的帧保留一段时间,直到他知道接受方已经接受了这些帧。

当第n帧的确认到来时,第n-1,第n-2等也都被自动地确认了。

接受方的窗口总是固定大小的。

接受方为其窗口内的每一个序列号保留了一个缓冲区。

与每个缓冲区相连关联的还有一位,用来指明该缓冲区是满的还是空的。

任何时候当一帧到达时,接受方通过between函数检查它的序列号,看是否落在窗口内。

如果确实落在窗口内,并且以前还没有收到这一帧,则接受该帧,并且保存起来。

而不管这一帧是否包含网络层所期望的下一个分组,这个过程是肯定要执行的。

该帧被保存在数据链路层中,直到所有序列号比他小的那些帧都已经按照正确的顺序提交给网络层之后,他才会被传递给网络层。

2.2主要问题

问题一:

如何模拟网络层的数据流量?

因为是模拟滑动窗口协议,为了使项目能够进行下去,先做两个假设

假设一:

发送方的网络层总有数据需要发送

假设二:

接受方没有反向流量,因此不能捎带确认,每次等待辅助定时器超时之后发送一个不带数据的ACK.

为了解决这个问题,我在发送方设置了一个发送定时器,定时间隔与发送速率一致。

发送定时器每触发一次,一个数据包(新的数据包或者重发数据包)被送出。

这样就可以通过调节该定时器调节发送方的发送速度。

问题二:

如何模拟接受方的接收速率?

考虑到发送方发送数据包是在定时器的控制下主动地发送比较好控制,而用套接字编程时,数据包一到自然接收,如果不采取特殊的措施是无法控制接受速率的。

因此在接受方设置一个接受定时器,时间间隔与接受速率一致。

类似网络层中为了控制突发流量所采用的令牌桶算法,接受定时器每触发一次,接受方就获得一块令牌,在有令牌的情况下,发送方的数据包到来才可以接受,否则就不作处理,当作丢失了。

而且在已经有令牌的情况下,接受定时器再被触发,也不增加令牌的数量,始终只有一块。

这样就大致控制了接收速率。

问题三:

如何模拟信道出现的状况?

考虑到在实际的传输过程中信道可能出现丢包,或是干扰信号导致出错,但是在传输层上做实验的时候,这些错误已经被下层处理过了,我能看到的是一个可靠的数据位流。

因此只好人工模拟出错和丢包的情况。

具体是这样实现的,在数据包内增加一个校验和域,当然校验和本来不应该放在数据包内(在写程序的时候,没有明确区分网络层的数据包和数据链路层的帧的概念,无伤大雅但是显的概念不清,以后注意改进)。

这里的校验和其实只是指明该帧是否在信道中被丢弃或是出错。

接受方将通过检查这一位对不同的数据包进行不同的处理(或者不处理)。

问题四:

如何理解错序发送?

由于教材上协议六是放在数据链路层一章来讲解的,使得我以为滑动窗口协议只是链路层的协议。

而数据链路层试点对点的通信,数据帧不会被路由,转发,更不会在子网里待上一阵子再冒出来,因此也不存在先发送的数据包后到达的情况。

但是与助教讨论过之后,知道了滑动窗口协议也可以用在tcp层。

这样的话,就需要我人为地打乱顺序。

在具体实现中,按照错序发送模式下输入的顺序逐个发送数据包,并逐个启动重发定时器,没有被发送的数据包就当作是在传输过程中遗失了。

问题五:

关于序列号的范围,发送窗口大小,接收窗口大小,缓冲区大小以及重发定时器数量的关系。

这个问题本来应该在计算机网络课程理论学习的过程中就解决的,但是当时学得不够仔细,所以在编程初期这个问题给我带来了不小的麻烦,所以在这里重新提一下。

接受方窗口的尺寸应该不超过序列号范围的一半,以确保接收方向向前移动窗口之后,新的窗口与老的窗口之间没有重叠。

接受方所需的缓冲区和定时器数量等于窗口的尺寸而不是序列号的范围。

问题六:

关于nak的理解

当接受方有理由怀疑出现了错误时,他就给发送方送回一个否定的确认(nak)帧。

这样的帧实际上是一个重传请求,在nak中指定了要重传的帧。

有两种情况接受方应该怀疑:

接受到一个受损的帧,或者到达的帧并非是自己所期望的。

为了避免多次请求重传同一个丢失的帧,接受方应该记录下对于某一帧是否已经发送过nak。

如果对于frame_expected还没有发送过nak,则变量no_nak为true.如果nak被损坏了,或者丢失了,则不会有实质性的伤害,因为发送方最终会超时,无论如何会重传丢失的帧。

如果一个nak被发送出去之后丢了,而接受方又收到一个错误的帧,则no_nak将为true,并且辅助定时器将被启动。

当辅助定时器超时后,一个ack帧将被发送出去,以便将发送方重新同步到接受方的当前状态。

2.3数据结构

数据包的结构:

Cstringframekind

数据包的类型,可能是数据(),确认(),否定性确认(),其实对于接受方来说这个域是没有意义的,因为他收到的总是数据,但是对接受方来说,就需要区分确认信息与否定确认。

本来数据包和消息可以用不同的数据结构,但是考虑到程序的可扩展性(例如,一个线程内同时收发数据,或是有捎带确认等等),所以采用一样的结构。

Cstringseq

对于数据类型的包来说,这个域指明了数据包的序列号。

对于确认信息来说,这个域说明了到这个序列号之前的所有数据包都已经收到。

对于否定性确认来说,这个域放的是需要重传的数据包的序列号。

Cstringinfo

数据包的内容,没有什么意义。

Cstringtm

时间戳,指明了该数据包被生成(或发送)的时间,通过这个域的信息,可以区分重传的帧。

Cstringcksum

校验和,详见前面的说明。

当这个域为0时,说明这个数据包没有受损,也没有被丢失。

这个域为1表示数据包受损,大于2表示数据包被丢弃,程序接收到数据包之后首先查看这个域的值,然后交付相应的分支进行处理。

发送窗口的数据结构:

intack_expected

发送窗口的下界,表示最小的尚未被确认的帧,收到接受方的ack之后,向前滑动这个值。

intframe_to_send

发送窗口的上界,指出了当前待发送的帧序号,每成功发送一帧,向前滑动一位,直到窗口满为止。

intnbuffer

指出了当前发送窗口的大小,即尚未被确认的帧的个数.通过对这个值的检验,可以强行关闭或打开网络层,以避免网络层流量太大导致缓冲区溢出.

接收窗口的数据结构:

intframe_expected

接收窗口的下界,即当前期望接收到的帧.当链路层将数据包顺序提交网络层之后,接收窗口整个向前滑动.

inttoo_far

接收窗口的上界,序列号落在这两个值之间的数据包,接受方都为之准备了缓存空间.而序列号掉在窗口外的那些数据包则认为是已经过期的数据包,直接扔掉.

booleanarrived[]

因为是选择性重传的协议,在收到一个错误的数据包之后不会将之后的数据包统统丢弃,而是缓存起来,所以需要指明接受缓冲区内那些数据包已经收到,那些还没有收到.

2.4算法分析

以下用伪代码的形式分别给出接收方和发送方的基本算法

sender:

voidprotocol(void)

{

intack_expected;

intnext_frame_to_send;

intnbuffer;

packetout_buf[NR_BUFS];

inti;

framer;

enable_network_layer();

ack_expected=0;

next_frame_to_send=0;

nbuffer=0;

while(TRUE)

{

wait_for_event(&event)

switch(event)

{

casenetwork_layer_ready:

nbuffer=nbuffer+1;

getapacketfromnetworklayer;

sendthatpacketandstarttimer;

inc(next_frame_to_send);

break;

caseframe_arrival:

getaframetfromphysicallayer(&r);

if(r.framekind==nak&&r.seqinsendwindow)

preparetosendthatpacketagain;

if(r.framekind==ack)

{

while(between(ack_expected,r.ack,next_frame_to-send))

{

nbuffer=nbuffer-1;

stop_timer(ack-expected);

inc(ack_expected);

}

}

break;

casetimeout:

preparetosendtheframethatcausethetimerout;

break;

}

if(nbuffer

enable_network_layer();

else

closethenetworklayer;

}

}

 

receiver:

voidprotocol6(void)

{

intframe_expected;

inttoo_far;

packetin_buf[NR_BUFS];

booleanarrived[NR_BUFS];

frame_expected=0;

too_far=NR_BUFS;

for(i=0;i

arrived[I]=FALSE;

while(TRUE)

{

wait_for_event(&event)

switch(event)

{

caseframe_arrived:

from_physical_layer(&r);

if(r.seq!

=frame_expected&&no_nak)

sendnakforframe_expected;

else

start_ack_timer();

if(between(frame_expected,r,seq,too_far)&&

(arrived[r.seq%NR_BUFS]==FALSE))

{

arrived[r.seq%NR_BUFS]=TRUE;

in_buf[r.seq%NR_BUFS]=r.info;

while(arrived[frame_expected%NR_BUFS])

{

to_network_layer(

&in_buf[frame_expected%NR_BUFS]);

no_nak=TRUE;

arrived[frame_expected%NR_BUFS])=FALSE;

inc(frame_expected);

inc(too_far);

start_ack_timer(0;

}

}

break;

casechecksum_error:

if(no_nak)

sendnakforframe_expected;

break;

caseack_timertimeout:

sendackforframe_expected-1;

break;

}

}

}

出错按钮被按下

2.4行为模型

准备发送出错的帧

isError=1

准备发送正确的帧

isError=0

发送了一个出错的帧

丢帧按钮被按下

准备丢弃帧

isDiscard=2

不准备丢弃帧

isDiscard=0

发送了一个丢弃的帧

发送一个nak

已经对该帧发送过nak

no_nak=FALSE

对于该帧未发送过nak

no_nak=TRUE

Frame_expected提交网络层

2.5端口设置

为了模拟全双工的信道,使用了四个端口.收发双方各两个.

发送端使用1001号端口创建一个套接字,用该套接字向接收端的1002号端口发起连接,建立起一条数据通路,用于发送数据包.

收到发送端发起的连接请求后,接收端用1003号端口建立一个套接字,并试图连接发送端的1000号端口,这条数据通路将被被用来传送确认信息.

初始的时候,发送端的1000号端口和接收端的1002号端口都处于侦听的状态.

示意图如下:

3详细设计

3.1参数说明

发送方:

intisError

intisDiscard

这两个参数的使用参见行为模型,在此不再赘述.

BOOLisNetEnable

这个控制变量就是网络层的开关,,当尚未被确认的帧个数等于发送窗口时,强行关闭网络层.而当收到确认信息,释放掉一些缓冲区之后,再次打开网络层.

intisToResend

这个变量记录当前要重发的帧,初始化为-1,表示没有待重发的帧.当收到否定确认或者重发定时器被触发之后,用这个变量记录下要重发的帧,并且等待下一次发送定时器触发的时候发送这一帧.如果同时有需要重发的帧和网络层产生的新帧时,重发的帧优先级较高,如果同时有许多帧需要重发,定时器最后被触发或是nak最后到来的那个帧优先级最高。

 

BOOLStopSend

设置这个变量是考虑到发送方可能需要暂停发送。

当处在暂停状态时,网络层不再发送新的数据包,但是发送方依然可以接收消息并且重传已经在缓存区内的数据包。

原先设想是否可以不设置这个变量而是使用isNetEnable,后来发现isNetEnable的值每次都可能因为检验nbuffer的值而改变,不能用在这里,所以重新设置了一个变量。

intwaitsend[]

发送模式为错序时需要用这个数组记录下输入的序列号的顺序,并且按照这个顺序依次发送数据包,以达到人为打乱次序的效果。

接收方:

BOOLno_nak

intisLost

isLost的用法与发送方isDiscard的用法是一样的,这两个参数的使用可以参考行为模型。

BOOLisReady

用于控制接收速率,详细的解释请参考概要设计部分的问题二。

3.2主要函数说明

发送方:

voidCPro7Dlg:

:

OnTimer(UINTnIDEvent)

{

//TODO:

Addyourmessagehandlercodehereand/orcalldefault

switch(nIDEvent)

{

caseID_SEND_TIMER:

//发送定时器被触发

if(m_sType==0)//当前的发送模式是正常发送模式

{

if((isNetEnable)&&(isToResend==-1)&&(!

StopSend))

NetReady();//当网络层可用,没有要重发的帧,而且不在暂停状态,产生一个新的帧,并且发送

else{

if(isToResend!

=-1)//有需要重发的帧

Resend();

}

if(nbuffer

isNetEnable=1;

else

isNetEnable=0;

}

if(m_sType==1)//当前的发送模式是错序发送模式

{

if(waitsend[point]!

=-1)//按错序发送

{

SendFrame(waitsend[point]);

ShowFrame(waitsend[point]);

point++;

}

else

{

if(timerpoint

start_timer(timerpoint++);

else//重发定时器全部打开后,这一批错序发送已经完毕,重新转入到正常的发送模式

m_sType=0;

}

}

break;

caseID_FRAME_TIMER1:

//重发定时器触发,记录下要重发的帧序号

isToResend=0;

break;

caseID_FRAME_TIMER2:

isToResend=1;

break;

caseID_FRAME_TIMER3:

isToResend=2;

break;

caseID_FRAME_TIMER4:

isToResend=3;

break;

caseID_FRAME_TIMER5:

isToResend=4;

break;

caseID_FRAME_TIMER6:

isToResend=5;

break;

caseID_FRAME_TIMER7:

isToResend=6;

break;

caseID_FRAME_TIMER8:

isToResend=7;

break;

}

ShowWndStatus();//更新界面显示的滑动窗口

CDialog:

:

OnTimer(nIDEvent);

}

voidCPro7Dlg:

:

ReceiveFrame()

{

CStringfk;//接收消息

CStringsq;

CStringin;

CStringck;

CStringt;

CStringstr;

TCHARtemp[10]="\0";

charbuf[5][255];

m_recv.Receive(buf,1275);

fk.Format("%s",buf[0]);

sq.Format("%s",buf[1]);

in.Format("%s",buf[2]);

ck.Format("%s",buf[3]);

t.Format("%s",buf[4]);

if(ck=="0")//ack或nak正常的到达

{

AddItem(nIndex,0,fk);//显示消息

AddItem(nIndex,1,sq);

AddItem(nIndex,2,in);

if(fk=="ack")

AddItem(nIndex,3,"ackarrived");

else

AddItem(nIndex,3,"nakarrived");

AddItem(nIndex++,4,t);

if(fk=="nak"&&between(ack_expected,atoi(sq),frame_to_send))//如果收到nak且序列号在窗口中,用变量记录下来,等待下一个时隙重发。

{

isToResend=atoi(sq);

}

else

{

if(fk=="ack")//收到确认,滑动窗口

{

while(between(ack_expected,atoi(sq),frame_to_send))

{

nbuffer=nbuffer-1;

stop_timer(ack_expected%m_SendWnd);

inc(ack_expected);

}

}

}

}

else

if(ck=="2")//确认消息在信道中丢失了

{

AddItem(nIndex,0,fk);

AddItem(nIndex,1,sq);

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 自然科学 > 物理

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1