计算机网络课程设计实验报告

实 验 报 告

课程名称:计算机网络实验 实验名称:ARQ协议模拟实现 院系:计算机科学学院 实验日期:2010-11-10 班级:07通信工程 实验报告日期:2010-11-10 姓名:谭才盛 学号:071101450037

老师批阅签字:_______

实验内容:

编写两个计算机程序p1,p2。

p1模拟发送方:首先从界面读取待发送字符(每接受一个字符的输入),保存到文件exe1.txt中,并启动计时器;

p2模拟接收方,它从exe1.txt中查找是否有新字符到来,并提供模拟界面给用户选择:

1. Ack-->接收该字符

2. NAK--〉丢弃

3. 无反应--〉导致超时

将用户选择的结果记录到exe2.txt中;接收的字符保存到exe3.txt 中。

由于可视化操作不精通,这次实验过程在C++环境下模拟运行。

运行环境:Visual C++ 6.0

编程思路:新建三个文本文件,每次运行程序开始阶段使系统随机产生100个随机数,并使其转化为字符格式,让P1从界面读取字符,输送到exe1,txt文件中。并启动计时器记录传输时间。读入exe1.txt,若发现有新字符,则提供模拟界面给用户选择接收还是丢弃;若无反应,则输出超时信息。将用户选择的结果记录到exe2.txt中;接收的字符保存到exe3.txt 中。文本文件读写一律采用追加模式。

实验步骤:

1.分析ARQ协议模拟实现的一般流程,画出流程图:

(ARQ协议流程图)

2.结合C++文件操作部分编写源程序:

源程序如下:

#include "iostream.h"

#include "fstream.h"

#include "stdlib.h"

#include "iomanip.h"

计算机网络课程设计实验报告

#include "time.h"

#include "string.h"

void main()

{long curtime_front_Sender, curtime_rear_Sender,

curtime_front_Receiver, curtime_rear_Receiver;

curtime_front_Receiver=time(NULL);//定义时钟

char ch,choice,str[4], array[100];

int i,tag=0;//定义开关变量

fstream file1; fstream file2; fstream file3;

cout<<"打开文件: exe1,exe2,exe3"<<endl;

file1.open("exe1.txt",ios::in|ios::out|ios::app);

if(file1.fail())

{cout<<"不能打开文件:"<<"exe1"<<endl;

exit(0);

}

file2.open("exe2.txt",ios::in|ios::out|ios::app);

if(file2.fail())

{cout<<"不能打开文件:"<<"exe2"<<endl;

exit(0);

}

file3.open("exe3.txt",ios::in|ios::out|ios::app);

if(file3.fail())

{cout<<"不能打开文件:"<<"exe3"<<endl;

exit(0);

}

cout<<endl<<endl<<"\t\t\t ARQ协议模拟实验\n\n\n";

cout <<"\t\t\t1 随机产生字符\n\n\t\t\t2 接收 \n\n\t\t\t3 退出"<<endl; do

{cin>>choice;

switch(choice)

{case '1':

for(i=0;i<100;i++)//随机产生字符

{array[i]='a'+rand()%26;

cout<<setw(2)<<array[i];

file1.put(array[i]);//字符读入exe1.txt文件中 curtime_front_Sender=time(NULL);//时钟运行 }tag=1;

file1.close();

cout<<"字符已读入exe1.txt文件中."<<endl;

break;

case '2':

if(tag==1)

{cout<<"文件exe1.txt中有新字符到来."<<endl;

cout<<"输入Ack:接收字符\n";

cout<<"输入NAk:放弃字符\n"; do { cin.ignore(); cin.getline(str,4); if(strcmp(str,"Ack")==0) { file1.open("exe1.txt",ios::in|ios::out|ios::app); file1.get(ch); while(!file1.eof()) {file3.put(ch); curtime_rear_Receiver=time(NULL); if(curtime_rear_Receiver-curtime_front_Receiver>1)//超时是否判断 {curtime_front_Receiver=time(NULL); continue; }file2<<"接收该字符."; file1.get(ch); }cout<<"文件读写完毕.\n"; } else if(strcmp(str,"NAk")==0) cout<<"已经放弃这些字符.\n"; else if(strcmp(str,"NAk")!=0 && strcmp(str,"Ack")!=0 ) cout<<"输入错误,请从新输入:"<<endl; }while(strcmp(str,"NAk")!=0 && strcmp(str,"Ack")!=0 ); }else if(tag==0) cout<<"文件exe1.txt中没有新字符到来."<<endl; break; default:cout<<"谢谢使用."<<endl; } }while(choice!='3'); file1.close();file2.close();file3.close(); } 3.模拟实验,运行源程序. 实验中的问题及心得:

通过实验发现在课程学习中对很多方面只是一知半解,并没有深入的加以研究,致使在组织源程序过程中遇到了很多麻烦,例如对ARQ协议只是片面地了解,对其背景,发展过程,应用过程,作用范围并不是很了解,今后一定会在这方面加强认识。通过学习实践发现计算机网络这门课程虽然大部分都是理论课程,很少有实践类的,但其从当今最前沿的计算机科学理论成果出发,将整个网络世界的总体框架呈现在面前,使得网络协议描述表现的淋漓尽致,今后必将以百倍的热情投入到这门课程的学习中。

总结通过实验所掌握的内容: 1.连续ARQ协议的算法

#define MAX_SEQ 7 /* 应该为2^n-1 */

typedef enum {frame_arrival, cksum_error, timeout, network_layer_ready} event_type; #include protocal.h

static boolean between(seq_nr a, seq_nr b, seq_nr c)

{

/* 如果b落在a和c之间(含a不含c)返回true,否则返回false. */

if (((a<=b) && (b<c)) || ((c<a) && (a<=b)) || ((b<c) && (c<a)))

return(true);

else return(false);

}

static void send_data(seq_nr frame_nr, seq_nr frame_expected, packet buffer[]) {/* 构造和发送数据帧

frame s; /* 起始变量 */

s.info=buffer[frame_nr]; /* 插入分组到帧中 */

s.seq=frame_nr; /* 插入序号到帧中 */

s.ack=(frame_expected+MAX_SEQ) % (MAX_SEQ+1) /* 捎带应答 */ to_physical_layer(&s); /* 传送该帧 */

start_timer(frame_nr);

} /* 启动定时器 */

void protocal5(void)

{

seq_nr next_frame_to_send; /* MAX_SEQ>1; 用于外出流 */

seq_nr ack_expected; /* 还没有得到应答的最早的帧 */

seq_nr frame_expected; /* 进入流期望的下一帧 */

frame r; /* 初始变量 */

packet buffer[MAX_SEQ+1] /* 外出流的缓存 */

seq_nr nbuffered; /* 当前正在使用的输出缓存 */ event_type event;

enable_network_layer(); /* 允许 network_layer_ready 事件 */ ack_expected = 0; /* 下一个期望进入的应答 */

next_frame_to_send = 0; /* 下一个要送出的帧 */

frame_expected = 0; /* 期望进入的帧的序号 */

nbuffered = 0; /* 初始没有分组被缓存 */ while (true)

{

wait_for_event ( &event); /* 四种可能的事件,见上面event_type定义 */

switch (event)

{

case network_layer_ready; /* 网络层有一个分组要发送 */ /* 接收, 保存, 以及发送一个新的帧 */

from_network_layer(&buffer[next_frame-to_send]); /* 获得一个新的分组 */ nbuffered = nbuffered + 1; /* 增加发送方的窗口 */ send_data(next_frame_to_send, frame_expected, buffer); /* 发送帧 */ inc(next_frame_to_send); /* 发送方的窗口上界向前移动*/

break;

case frame_arrival: /* 一个数据帧或控制帧到达 */

from_physical_layer(&r); /* 从物理层得到一个进入的帧 */

if (r.seq == frame_expected)

{/* 所有的帧只能按序接收. */

to_network_layer(&r.info); /* 传递分组到网络层 */ inc(frame_expected); /* 接收方的窗口下界向前移动 */

} /* Ack n 意味着n-1,n-2,...等. */

while (between(ack_expected, r.ack, next_frame_to_send))

{/* 处理捎带应答 */

nbuffered = nbuffered + 1; /* 减少一个缓存的帧 */ stop_timer(ack_expected); /* 帧完好到达, 停止定时器 */ inc(ack_expected); /* 压缩发送窗口 */ }

break;

case cksum_err: break; /* 丢弃坏帧 */

case time_out: /* 重传所有超时的帧 */

next_frame_to_send = ack_expected; /* 开始重传 */

for (i = 1; i <= nbuffered; i ++)

{

send_data(next_frame_to_send, fram_expected, buffer); /* 重发1帧 */ inc(next_frame_to_send); /* 准备发送下一帧 */ }

}

if (nbuffered < MAX_SEQ)

enable_network_layer();

else

disable_network_layer();

}

}

连续ARQ协议采用的重要原理:

在简单停止等待协议的基础上,允许连续发送若干帧,在收到相应ACK后继续发送若干帧,用以提高传输效率。这时ACK及NAK也必须有对应的帧序号,才能够一一对应起来。 在发生差错时丢弃原已发送的所有后续帧,重发差错发生以后的所有帧,相当于完全返回重传。 信道较差时传输效率不高。 连续重发请求ARQ方案是指发送方可以连续发送一系列信息帧,即不用等前一帧被确认便可继续发送下一帧,效率大大提高。但在这种重发请求方案中,需要在发送方设置一个较大的缓冲存储空间(称作重发表),用以存放若干待确认的信息帧。当发送方到对某信息帧的确认帧后,便可从重发表中将该信息帧删除。所以,连续重发请求ARQ方案的链路传输效率大大提高,但相应地需要更大的缓冲存储空间。

在这一协议中,当发送站点发送完一个数据帧后,不是停下来等待应答帧,而是可以连续在发送若干个数据帧。如果在此过程中又收到了接收端发来的应答帧,那么还可以接着发送数据 帧。由于减少了等待时间,整个通信的吞吐量就提高了。

如结点A向结点B发送数据帧,当结点A发完0号帧时,并不等待,而是继续发送后续的1号帧、2号帧等。由于连续发送了许多帧,所以应答帧不仅要说明是对哪一帧进行确认或

否认,而且应答帧本身也必须编号。假设2号帧出了差错,于是结点B发送否认帧NAK2。当否认帧NAK2到达结点A时,结点A正在发送5号数据帧。当5号 帧发送完毕后,结点A才能进行2号帧的重发。

这里要注意两点:接受端只能按序接收数据帧。对于2号帧,结点B应答了NAK2,虽然接着又收到了三个正确的数据帧,但都必须将他们丢弃,因为这些帧的发送序号都不是顺序号;结点A在重传2号数据帧时,虽然已经发完了5号帧,但仍必须向回走,从2 号帧起进行重传。正因如此,连续ARQ又称为Go-back-N ARQ,意思是当出现差错必须重传时,要向回走N个帧,然后再开始重传。

GO-DACK-N策略的基本原理是,当接收方检测出失序的信息帧后,要求发送方重发最后一个正确接收的信息帧之后的所有未被确认的帧;或者当发送方发送了N个帧后,若发现该N帧的前一个帧在计时器超时后仍未返回其确认信息,则该帧被判为出错或丢失,此时发送方就不得不重新发送出错帧及其后的N帧。这就是GO-DACK-N(退回N)法名称的由来。因为对接收方来说,由于这一帧出错,就不能以正常的序号向它的高层递交数据,对其后发送来的N帧也可能都不能接收而丢弃。GO-DACK-N法操作过程如图7-7所示。图中假定发送完8号帧后,发现2号帧的确认返回信号在计时器超时后还未收到,则发送方只能退回到从2号帧开始重发以后所有已发的数据。

还有另一种情况。2号数据帧丢失,3号至5号数据帧虽然正确传送 到结点B,但也不得不被丢弃。当结点A发送5号数据帧的过程中,超时定时器设定的超 时时间到。因此,在5号数据帧发送完毕后,就回到2号数据帧进行重传。

在使用连续ARQ协议时,如果发送端一直没有受到对方的确认信息,那么实际上发送端并不能无限制的发送其数据。这是因为:当未被确认的数据帧的数目太多时,只要有一帧出了差错,就要有很多的 数据帧需要重传,这必然浪费很多时间。另外为了对所发送的大量数据帧进行编号,每个数据帧的发送序号也要占用较 多的比特数,这样又增加了一些不必要的开销。因此,在连续ARQ协议中必须将已发送出去、但未被确认的数据帧的数目加以限制, 这是本章后面将要 要的滑动窗口协议所要讨论的内容。

从原理不难看出,连续ARQ协议一方面因连续发送数据帧而提高了效率,但另一方 面,在重传时又必须把原来已正确传送过的数据帧进行重传(但仅因这些数据帧之前有 一个数据帧出了错),这样又使传送速率降低。由此可见,若传输信道的传输质量很差 而误码率较大时,连续ARQ协议不一定优于停止等待协议。

综上所述,连续重发请求ARQ方案的特点如下:

(1)发送方连续发送信息帧,而不必等待确认帧的返回;

(2)在重发表中保存所发送的每个帧的备份;

(3)重发表按先进先出(FIFO)队列规则操作;

(4)接收方对每一个正确收到的信息帧返回一个确认帧,每一个确认帧包含一个惟一的序号,随相应的确认帧返回;

(5)接收方保存一个接收次序表,包含最后正确收到的信息帧的序号。当发送方收到相应信息帧的确认后,从重发表中删除该信息帧的备份;

(6)当发送方检测出失序的确认帧(即第N号信息帧和第N+2号信息帧的确认帧已返回,而N+1号的确认帧未返回)后,便重发未被确认的信息帧。

相关推荐