课程设计5---滑动窗口协议模拟程序的设计与实现 姓名: 学号: 一、 目标任务 (1) 了解网络协议编程的基本知识; (2) 了解滑动窗口协议的工作机制; (3) 使用编程语言编写一个滑动窗口协议的模拟程序,按要求实现程序。 二、 编程语言 C语言 三、 滑动窗口协议介绍 3.1 滑动窗口协议工作原理 TCP协议在工作时,如果发送端的TCP协议软件每传输一个数据分组后,必须等待接收端的确认才能够发送下一个分组,由于网络传输的时延,将有大量时间被用于等待确认,导致传输效率低下。为此TCP在进行数据传输时使用了滑动窗口机制。
TCP滑动窗口用来暂存两台计算机间要传送的数据分组。每台运行TCP协议的计算机有两个滑动窗口:一个用于数据发送,另一个用于数据接收。发送端待发数据分组在缓冲区排队等待送出。被滑动窗口框入的分组,是可以在未收到接收确认的情况下最多送出的部分。滑动窗口左端标志X的分组,是已经被接收端确认收到的分组。随着新的确认到来,窗口不断向右滑动。
TCP协议软件依靠滑动窗口机制解决传输效率和流量控制问题。它可以在收到确认信息之前发送多个数据分组。这种机制使得网络通信处于忙碌状态,提高了整个网络的吞吐率,它还解决了端到端的通信流量控制问题,允许接收端在拥有容纳足够数据的缓冲之前对传输进行限制。在实际运行中,TCP滑动窗口的大小是可以随时调整的。收发端TCP协议软件在进行分组确认通信时,还交换滑动窗口控制信息,使得双方滑动窗口大小可以根据需要动态变化,达到在提高数据传输效率的同时,防止拥塞的发生。 称窗口左边沿向右边沿靠近为窗口合拢,这种现象发生在数据被发送和确认时。 当窗口右边沿向右移动时将允许发送更多的数据,称之为窗口张开。这种现象发生在另一端的接收进程读取已经确认的数据并释放了TCP的接收缓存时。
当右边沿向左移动时,称为窗口收缩。Host Requirements RFC强烈建议不要使用这种方式。但TCP必须能够在某一端产生这种情况时进行处理。
如果左边沿到达右边沿,则称其为一个零窗口。 3.2 滑动窗口算法 滑动窗口算法工作过程如下: 首先,发送方为每1帧赋一个序号(sequence number),记作SeqNum。现在,我们忽略SeqNum是由有限大小的头部字段实现的事实,而假设它能无限增大。发送方维护3个变量:发送窗口大小(send window size),记作SWS,给出发送方能够发 送但未确认的帧数的上界; LAR表示最近收到的确认帧(last acknowledgement received)的序号;LFS表示最近发送的帧(last frame sent)的序号,发送方还维持如下的不变式:LAR-LFS≤SWS 。
图3-1 滑动窗口算法的时间线 当一个确认到达时,发送方向右移动LAR,从而允许发送方发送另一帧。同时,发送方为所发的每个帧设置一个定时器,如果定时器在ACK到达之前超时,则重发此帧。注意:发送方必须存储最多SWS个帧,因为在它们得到确认之前必须准备重发。接收方维护下面3个变量:接收窗口大小(receive window size),记为RWS,给出接收方所能接收的无序帧数目的上界;LAF表示可接收帧(largest acceptable frame)的序号;LFR表示最近收到的帧(last frame rece ived)的序号。接收方也维持如下不变式:LFS-LAR≤SWS 图3-2 接收方的滑动窗口 当一个具有顺序号SeqNum的帧到达时,接收方采取如下行动:如果SeqNum≤LFR或SeqNum> LAF,那么帧不在接收窗口内,于是被丢弃;如果LFR<SeqNum≤LAF,那么帧在接收窗口内,于是被接收。现在接收方需要决定是否发送一个ACK。设SeqNum To ACK表示未被确认帧的最大序号,则序号小于或等于SeqNum To ACK的帧都已收到。即使已经收到更高序号的分组,接收方仍确认SeqNum To ACK的接收。这种确认被称为是累积的(cumulative)。然后它设置LFA = SeqNum To ACK,并调整LFA = LFR + RWS。
图3-3 接收方的滑动窗口 窗口协议算法有三个功能: 在不可靠链路上可靠地传输帧 保持帧的传输顺序 支持流量控制 四、 设计方案及分析 4.1 窗口机制总体设计及分析
图4-1 发送方和接收方状态示意图
LFR 设计分析: (1) 初始态,发送方没有帧发出,发送窗口前后沿相重合。接收方0号窗口打开,等待接收0号帧;
(2) 发送方打开0号窗口,表示已发出0帧但尚未确认返回信息。此时接收窗口状态不变; (3) 发送方打开0、1号窗口,表示0、1号帧均在等待确认之列。至此,发送方打开的窗口数已达规定限度,在未收到新的确认返回帧之前,发送方将暂停发送新的数据帧。接收窗口此时状态仍未变;
(4) 接收方已收到0号帧,0号窗口关闭,1号窗口打开,表示准备接收1号帧。此时发送窗口状态不变;
(5) 发送方收到接收方发来的0号帧确认返回信息,关闭0号窗口,表示从重发表中删除0号帧。此时接收窗口状态仍不变;
(6) 发送方继续发送2号帧,2号窗口打开,表示2号帧也纳入待确认之列。至此,发送方打开的窗口又已达规定限度,在未收到新的确认返回帧之前,发送方将暂停发送新的数据帧,此时接收窗口状态仍不变;
(7) 接收方已收到1号帧,1号窗口关闭,2号窗口打开,表示准备接收2号帧。此时发送窗口状态不变;
(8) 发送方收到接收方发来的1号帧收毕的确认信息,关闭1号窗口,表示从重发表中删除1号帧。此时接收窗口状态仍不变。
4.2 协议选择及分析 在设计过程中,我主要运用了选择重传协议,该协议能很好地弥补了1比特滑动窗口协议和后退n协议的缺点,是比较完善的滑动窗口协议。
在选择重传协议中,当接收方发现某帧出错后,其后继续送来的正确的帧虽然不能立即递交给接收方的高层,但接收方仍可收下来,存放在一个缓冲区中,同时要求发送方重新传送出错的那一帧。一旦收到重新传来的帧后,就可以原已存于缓冲区中的其余帧一并按正确的顺序递交高层。这种方法称为选择重发(SELECTICE REPEAT),其工作过程如图所示。显然,选择重发减少了浪费,但要求接收方有足够大的缓冲区空间。 图4-2 选择重传协议原理图 4.3 发送方与接收方设计流程 由于我设计的程序为模拟程序,因此我把发送方和接收方集合在同一版面上。它们各自的功能同时在同一版面上实现及显示。在程序实现后,我们可以通过在同一版面根据提示输入相关信息,即可得到模拟过程。
虽然只有一个版面,但是发送方和接收方的功能是清晰的、相对齐全的。发送方和接收方的设计流程如下:
图4-3 发送方与接收方设计流程 五、 关键代码 (1)发送方程序: 本程序设有四个变量:一是窗口大小变量,二是第一帧序列号变量,三是最近发送的帧变量,最后一个是最近收到的确认帧变量。 swpstate1.head=NULL; //变量初始值为空 swpstate1.sendq=sendq_rear=(structsendq_slot*)malloc(sizeof(structsendq_slot); if(!swpstate1.sendq) exit(1); sendq_rear->next=NULL; printf("请输入窗口大小:"); scanf("%ld",&swpstate1.sws); //输入窗口大小 swpstate1.rws=swpstate1.sws; //把窗口大小的值赋给变量 if (swpstate1.sws>0) { printf("请输入第一帧的序列号:"); scanf("%ld",&swpstate1.hdr.seqnum); //输入第一帧序列号 } swpstate1.nfe=swpstate1.hdr.seqnum; //把第一帧的值放进缓冲池内 sendp=(struct sendq_slot*) malloc (size of(struct sendq_slot)); if(!sendp) exit(1); sendp->msg=swpstate1.hdr.seqnum; sendp->timeout=1; sendp->next=NULL; sendq_rear->next=sendp; sendq_rear=sendp; --swpstate1.sws; swpstate1.lfs=swpstate1.hdr.seqnum; //最近发送的帧取值 swpstate1.lar=swpstate1.hdr.seqnum; //最近收到的确认帧取值 do{ while(swpstate1.sws>0) //当窗口大小大于0时,执行以下的循环 { sendp=(struct sendq_slot*)malloc(sizeof(struct sendq_slot)); if(!sendp) exit(1); sendp->msg=swpstate1.lfs+1; //如果输入的帧序号大于之前帧序号,那么窗口向前滑动 sendp->timeout=1; //时延为1 sendp->next=NULL; sendq_rear->next=sendp; sendq_rear=sendp; --swpstate1.sws; ++swpstate1.lfs; }