计算机网络实验报告(三)——编程实现可靠数据传输原理 go-back-n(一)实验目的:运用各种编程语言实现基于go-back-n 的可靠数据传输软件。
通过本实验,使学生能够对可靠数据传输原理有进一步的理解和掌握。
(二)实验内容:(1).选择合适的编程语言编程实现基于go-back-n 的可靠数据传输软件。
(2).在实际网络环境或模拟不可靠网络环境中测试和验证自己的可靠数据传输软件。
(三)实验原理:1.gbn协议含义:go-back-n arq中文翻译为后退n式arq、回退n式arq。
该协议对传统的自动重传请求(arq,automatic repeat reques)进行了改进,从而实现了在接收到ack之前能够连续发送多个数据包。
在go-back-n arq中,发送端不需要在接收到上一个数据包的ack后才发送下一个数据包,而是可以连续发送数据包。
在发送端发送数据包的过程中,如果接收到对应已发送的某个数据包的nack,则发送端将nack对应的某个数据包进行重发,然后再将该数据包之后的数据包依次进行重发。
后退n帧arq的图例:后退n帧arq就是从出错处重发已发出过的n个帧。
2.go-back-n 的有限状态机模型表示如图所示:(a)(b)图3.1 go-back-n 的有限状态机模型(a)发送端 (b)接受端(四)实验步骤:在eclipse平台编写并调试gbn模拟java程序,观察三组以上实验结果,验证程序可以正确模拟gbn的发送规则。
(五)实验结果:以下为随机数模拟的某次发送情况:接收方开始接收分组数据!发送方开始发送分组数据!发送方现在开始第一次发送序号为0的数据分组当前窗口内的分组情况为:第0号窗口里面存放的是序号为1的马上待发送的数据分组!第1号窗口里面存放的是序号为2的马上待发送的数据分组!第2号窗口里面存放的是序号为3的马上待发送的数据分组!接收方收到了序号为0的分组!该数据分组正是接收方所期待的,接收方接受了它并准备回送对应的ack!发送方收到了ack,序号为0并且开始加以确认!发送方现在开始第一次发送序号为1的数据分组当前窗口内的分组情况为:第0号窗口里面存放的是序号为2的马上待发送的数据分组!第1号窗口里面存放的是序号为3的马上待发送的数据分组!第2号窗口里面存放的是序号为4的马上待发送的数据分组!发送数据分组时发生延迟:1200毫秒!接收方收到了序号为1的分组!该数据分组正是接收方所期待的,接收方接受了它并准备回送对应的ack!发送方收到了ack,序号为1并且开始加以确认!发送方现在开始第一次发送序号为2的数据分组当前窗口内的分组情况为:第0号窗口里面存放的是序号为3的马上待发送的数据分组!第1号窗口里面存放的是序号为4的马上待发送的数据分组!第2号窗口里面存放的是序号为5的马上待发送的数据分组!发送数据分组时发生延迟:750毫秒!序号为2的分组在传给接收方途中发生了丢包!发送方开始重新发送序号为 2的数据分组发送数据分组时发生延迟:750毫秒!接收方收到了序号为2的分组!该数据分组正是接收方所期待的,接收方接受了它并准备回送对应的ack!发送方收到了ack,序号为2并且开始加以确认!发送方现在开始第一次发送序号为3的数据分组当前窗口内的分组情况为:第0号窗口里面存放的是序号为4的马上待发送的数据分组!第1号窗口里面存放的是序号为5的马上待发送的数据分组!第2号窗口里面存放的是序号为6的马上待发送的数据分组!发送数据分组时发生延迟:300毫秒!接收方收到了序号为3的分组!该数据分组正是接收方所期待的,接收方接受了它并准备回送对应的ack!发送方收到了ack,序号为3并且开始加以确认!发送方现在开始第一次发送序号为4的数据分组当前窗口内的分组情况为:第0号窗口里面存放的是序号为5的马上待发送的数据分组!第1号窗口里面存放的是序号为6的马上待发送的数据分组!第2号窗口已经空了,并且后续窗口、发送方没有要发送的数据分组了!发送数据分组时发生延迟:750毫秒!接收方收到了序号为4的分组!该数据分组正是接收方所期待的,接收方接受了它并准备回送对应的ack!发送方收到了ack,序号为4并且开始加以确认!发送方现在开始第一次发送序号为5的数据分组当前窗口内的分组情况为:第0号窗口里面存放的是序号为6的马上待发送的数据分组!第1号窗口已经空了,并且后续窗口、发送方没有要发送的数据分组了!第2号窗口已经空了,并且后续窗口、发送方没有要发送的数据分组了!发送数据分组时发生延迟:1200毫秒!序号为5的分组在传给接收方途中发生了丢包!发送方开始重新发送序号为 5的数据分组序号为5的分组在传给接收方途中发生了丢包!发送方开始重新发送序号为 5的数据分组发送数据分组时发生延迟:1200毫秒!接收方收到了序号为5的分组!该数据分组正是接收方所期待的,接收方接受了它并准备回送对应的ack!发送方收到了ack,序号为5并且开始加以确认!发送方现在开始第一次发送序号为6的数据分组当前窗口内的分组情况为:第0号窗口已经空了,并且后续窗口、发送方没有要发送的数据分组了!第1号窗口已经空了,并且后续窗口、发送方没有要发送的数据分组了!第2号窗口已经空了,并且后续窗口、发送方没有要发送的数据分组了!发送数据分组时发生延迟:750毫秒!序号为6的分组在传给接收方途中发生了丢包!发送方开始重新发送序号为 6的数据分组发送数据分组时发生延迟:3000毫秒!序号为6的分组在传给接收方途中发生了丢包!发送方开始重新发送序号为 6的数据分组发送数据分组时发生延迟:3000毫秒!接收方收到了序号为6的分组!该数据分组正是接收方所期待的,接收方接受了它并准备回送对应的ack!计时超时!!(未丢包但是时间超过2秒)发送方准备重发序号为6的数据分组!发送方开始重新发送序号为 6的数据分组发送数据分组时发生延迟:750毫秒!接收方收到了序号为6的分组!该数据分组不是接收方所期待的,该分组将被丢弃,接收方准备回送最后接受的数据分组对应的ack!发送方收到了ack,序号为6并且开始加以确认!当前窗口内的分组情况为:第0号窗口已经空了,并且后续窗口、发送方没有要发送的数据分组了!第1号窗口已经空了,并且后续窗口、发送方没有要发送的数据分组了!第2号窗口已经空了,并且后续窗口、发送方没有要发送的数据分组了!发送数据分组时发生延迟:300毫秒!序号为7的分组在传给接收方途中发生了丢包!以下是每个数据分组被发送过的次数的统计结果序号为0的数据分组被发送过的次数为: 1序号为1的数据分组被发送过的次数为: 1序号为2的数据分组被发送过的次数为: 2序号为3的数据分组被发送过的次数为: 1序号为4的数据分组被发送过的次数为: 1序号为5的数据分组被发送过的次数为: 3序号为6的数据分组被发送过的次数为: 4(六)问题总结:1. 编写gbn算法,要注意的是在模拟丢包的情况下,要在接收端阻止ack的回传和在发送端发现ack超时的情况下进行该包的重发,要注意整个7个包的传送组织结构和顺序;2. 进行模拟时,注意标注丢包及正确收到的flag值;3. 测试时应多运行几次,观察在各种随机传送情况下的总体的发送情况,来验证是否能满足gbn协议。
(七)附录:java源代码:【gbn.java】import .*;import java.util.random;import java.io.*;public class gbn extends thread{static void senddelay(int x) throws interruptedexception{if(x==1) {sleep(300); system.out.println(发送数据分组时发生延迟:300毫秒!); } else if(x==2) {sleep(750); system.out.println(发送数据分组时发生延迟:750毫秒!);}else if(x==3) {sleep(1200); system.out.println(发送数据分组时发生延迟:1200毫秒!); }else if(x==4) {sleep(3000); system.out.println(发送数据分组时发生延迟:3000毫秒!); }else;}public static void main(string[] args) throws ioexception, interruptedexception{sender s=new sender();receiver re=new receiver();s.start();re.run(s); //发送端启动 //接收端启动 //延迟处理sleep(1000);int[] retimes=new int[7];//计算每个分组被发送的次数for(int i=0;i<7;i++)retimes[i]=0;for(int i=0;i<=s.sign.length;i++){数据分组);retimes[s.localack+1]++;int ran=new random().nextint(3);int randelay=new random().nextint(5);s.time();senddelay(randelay); //设置随机值,模拟数据传输延迟if(ran!=1)re.receive(s.localack+1,s); //设置随机值,模拟数据丢包过程while(i>s.localack+1){ //数据包顺次发送//尚有未确认的数据包,重发!system.out.println(发送方开始重新发送序号为 +(s.localack+1)+的 elsesystem.out.println(序号为+(s.localack+1)+的分组在传给接收方途中发生了丢包!); }if(i!=s.sign.length){system.out.println();system.out.println(发送方现在开始第一次发送序号为+i+的数据分组); retimes[i]++;for(int k=0;k<3;k++){s.windowsign[k]++;}}system.out.println();system.out.println(当前窗口内的分组情况为:);for(int p=0;p<3;p++){if(s.windowsign[p]<=6)system.out.println(第+p+号窗口里面存放的是序号为+s.windowsign[p]+的马上待发送的数据分组!);elsesystem.out.println(第+p+号窗口已经空了,并且后续窗口、发送方没有要发送的数据分组了!);}system.out.println();int ran=new random().nextint(3);int randelay=new random().nextint(5);s.time(); //计时开始(2秒时间)senddelay(randelay);if(ran!=1)re.receive(s.sign[i],s);else包!);} system.out.println(序号为+i+的分组在传给接收方途中发生了丢system.out.println();system.out.println(以下是每个数据分组被发送过的次数的统计结果);for(int i=0;i<7;i++) //显示关于每个数据包发送次数的统计表system.out.println(序号为+i+的数据分组被发送过的次数为: +retimes[i]);system.exit(0);}}【receiver.java】public class receiver extends thread{public int lastdata;public sender sender;public void run(sender s){}void receive(int data, sender s){sender=s; //发送方的参数传递 system.out.println(接收方收到了序号为+data+的分组!); if(data!=0){ if(data==lastdata+1){//数据包序号校验,若连续则是正确/所期待的 sender=s; system.out.println(接收方开始接收分组数据!);system.out.println(该数据分组正是接收方所期待的,接收方接受了它并准备回送对应的ack!);lastdata=data; //更新本地保存的数据包序号变量} respond(lastdata); //回送该正确接收的数据包对应的ackelse{system.out.println(该数据分组不是接收方所期待的,该分组将被丢弃,接收方准备回送最后接受的数据分组对应的ack!);respond(lastdata); //若不是所期待的数据包则丢弃并且重发上一次的ack} else{ }system.out.println(该数据分组正是接收方所期待的,接收方接受了它并准备回送对应的ack!);lastdata=data;respond(lastdata); //首次接收数据包并且回送ack} }void respond(int ack){if(sender.litime.limit<20){ ack=lastdata; sender.getack(ack); } else{system.out.println(计时超时!!(未丢包但是时间超过2秒)发送方准备重发序号为+ack+的数据分组!);sender.switches=1; //如果超时,设置超时状态并显示警告 }}}【sender.java】import java.util.timer;import java.util.timertask;public class sender extends thread{public int windowsize=3; //发送方窗口长度设为3public string[]data={data1,data2,data3,data4,data5,data6,data7}; //模拟七个数据包 public int sign[]={0,1,2,3,4,5,6}; //为7个数据包标号public int localack=-1;public timers litime=null;public int switches=0;public int windowsign[];//当前窗口内待发的数据分组的序号public int acksign = 0; //为0表示收到正确ack,为1表示收到错误的ack,必须重发! public sender(){windowsign = new int[windowsize]; //给窗口分配指定大小的空间for(int i=0;i<3;i++)}public void run(){}public void getack(int ack){system.out.println(发送方收到了ack,序号为+ack+并且开始加以确认!); if(ack!=localack+1){system.out.println(经验证,这不是发送方正期待的ack,立刻重发序号为+(localack+1)+的数据分组!);acksign=1;}else{localack=ack; //表示正确确认了ackacksign=0; } system.out.println(发送方开始发送分组数据!); windowsign[i]=sign[i]; //窗口初始化时存放前3个序号}public void time(){}【timers.java】import java.util.timertask;public class timers extends timertask {public int switches;public int limit;public void run(){if(limit<20)limit++; switches=0; //标志初始化为0 litime=new timers(); timer limit=new timer(); limit.schedule(litime, 0,100); }else {}}public timers(){}} switches=0; limit=0; //启动计时器时全部初始化 switches=-1; this.cancel(); //开关为-1表示超时,并且停止计时器。