扬州大学计算机网络课程设计 模拟滑动窗口的实现.docx
《扬州大学计算机网络课程设计 模拟滑动窗口的实现.docx》由会员分享,可在线阅读,更多相关《扬州大学计算机网络课程设计 模拟滑动窗口的实现.docx(16页珍藏版)》请在冰豆网上搜索。
扬州大学计算机网络课程设计模拟滑动窗口的实现
课程设计报告
课程:
计算机通信与网络
题目:
滑动窗口协议的模拟实现
班级:
物联1301
学号:
131408119
姓名:
滕玲
一、任务描述
1.深入理解计算机网络中可靠传输的实现原理。
2.编写两个程序:
一个模拟提供可靠传输服务的发送实体;一个模拟提供可靠传输服务的接收实体。
3.撰写课程设计报告
二、可靠传输的实现原理
1.理想的数据传输:
假定1:
链路是理想的传输信道(不会出错和丢失)
假定2:
不管发方以多快的速率发送数据,收方总来得及收下,并及时上交主机(接收缓冲区容量无限大永不会溢出或收发速率精确相等)
2.差错控制方法=确认+重发
a.为了防止数据传输中出错,在发送完一个分组后,必须暂时保留已发送的分组的副本,直到收到接收方的肯定确认。
b.为了防止数据丢失,导致发送方死等,发送方在发出一个分组的同时,启动一个计时器,如果超时还没有收到确认,则主动重发。
c.为了防止因确认丢失导致的重复接收,对每个协议数据单元编号,接收方通过编号判断分组是否重复分组。
3.流量控制方法
a.流量控制要求发送方根据接收方的能力调整发送速度。
b.最保守的做法是每发出一个协议数据单元,仅当等到接收方的许可确认,再发送下一个协议数据单元。
但是这样做的话,发送效率很低。
即使接收方足够快,也要经过两点间传播时延的2倍时间,才能发出去一个协议数据单元。
c.可以不像上面一样将接收方按最坏情形设想,连续发送多个协议数据单元数。
如果设想正确,在两点间传播时延的2倍时间内,就可以发送多个协议数据单元,提高发送效率。
当然,由于发出去的每个协议数据单元都有可能遇到问题需要重发,发送方必须设置多个发送缓冲区以保存副本,发送缓冲区的个数决定了发送速率。
当缓冲区满,即使有用户请求到达,也不会发送。
d.实用的可靠传输协议既要进行差错控制,又要进行流量控制。
流量控制的加入,给发送方实体增加了一种可能情形:
当用户发送请求到达,发送实体因为发送缓冲区满,拒绝发送。
e.滑动窗口协议实现了上述可靠传输原理。
4.滑动窗口协议
a.原理:
所有滑动窗口协议中,每个外出协议数据单元包含一个序号:
0某一最大值,如序号长度为n,最大值通常为2n-1。
发送方在任何时刻,决定一个用户请求是否被满足的条件是是否有缓冲区存放副本。
随着数据的发送和确认的到达,已经发出的协议数据单元和允许发送的协议数据单元的个数总等于发送缓冲区大小,并且它们的序号变化的规律如同在序号序列上滑动窗口一样。
而为了防止重复接收,接收方维持着已经看到过的序号/期望看到的序号,随着数据不断到达,这个序号也不断变化,变化的规律如同在序号序列上滑动窗口一样。
b.类型:
停等协议:
发送窗口=1,接收窗口=1
退后N帧协议:
发送窗口>1,接收窗口=1
选择重传协议:
发送窗口>1,接收窗口>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=(structsendq_slot*)malloc(sizeof(structsendq_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=(structsendq_slot*)malloc(sizeof(structsendq_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;
}
swpstate1.hdr.acknum=0;//ACK清空
swpstate1.hdr.flags=0;//存储缓冲池清空
printf("最近收到的ACK的帧序号:
%ld\n",swpstate1.lar);
//输出最近收到的ACK帧序号
printf("最近发送的帧序号(发送新帧后):
%ld\n",swpstate1.lfs);
//输出最近发送帧序号
四、接收方模拟程序的设计与实现
1.接收方的接收原则从总体上看是先判断输入的数据帧是否在接收范围之内,若是,则继续判断是否符合其他接收条件;若不是,则马上丢弃该数据帧,不再进行其他条件的判断。
structsendq_slot*sendq_rear,*sendp,*p3,*p4;//设定变量
structrecvq_slot*recvp,*recvq_rear,*p1,*p2;
if(swpstate1.hdr.flags==0)
//上次输入的数据帧被放置在缓存区,输入区被清空
{
do//如果继续接收数据帧则实施下面循环
{
printf("请输入收到的数据帧号:
");
scanf("%ld",&a);
if(a>=swpstate1.nfe&&a<=swpstate1.lfs)//判断数据帧应被接收或缓存
{
if(swpstate1.head==NULL)
{
recvp=recvq_rear=(structrecvq_slot*)malloc(sizeof(structrecvq_slot));
recvp->next=NULL;
swpstate1.head=recvp;
}
else
if(swpstate1.head!
=NULL)
{
recvp=(structrecvq_slot*)malloc(sizeof(structrecvq_slot));
recvp->next=NULL;
recvq_rear->next=recvp;
recvq_rear=recvp;
}
}
else
{
printf("所输数据不在接收窗口内!
");
break;//跳出该循环
}
2.若输入数据帧在接收范围内则继续判断并进行以下循环。
recvp->msg=a;
if(recvp->msg==swpstate1.nfe)//是否放入缓存判断
recvp->received=1;
else
recvp->received=0;
--swpstate1.rws;
if(recvp->received==1)//数据帧被接收,则进行下面语句
{a=a-1;
do
{a=a+1;
if(swpstate1.head==NULL)
break;
p1=swpstate1.head;
flag=0;
while((a!
=p1->msg)&&(p1->next!
=NULL))
{
p2=p1;p1=p1->next;
}
if(a==p1->msg)
{
flag=1;
if(p1==swpstate1.head)
swpstate1.head=swpstate1.head->next;
elsep2->next=p1->next;
swpstate1.nfe=a+1;
swpstate1.hdr.acknum=a+1;
swpstate1.hdr.flags=1;
}
}while(flag==1);
}
printf("ACK号(期待的下一帧的序号):
%ld\n",swpstate1.nfe);
printf("没按序接受的序号:
\n");
p1=swpstate1.head;
while(p1!
=NULL)
{
printf("%ld\t",p1->msg);
p1=p1->next;
}
3.当接收完一个数据帧时,我们可以选择终止下面的继续接收,也可以选择继续接收。
如果继续接收,那么程序跳到判断循环,继续判断是否接收下一个数据帧,原理与上面相当。
while((swpstate1.rws>0)&&(b==1));
if(swpstate1.hdr.flags==1)
{p3=swpstate1.sendq->next;
flag=0;
while((swpstate1.hdr.acknum)!
=p3->msg&&p3->next!
=NULL)
{p4=p3;p3=p3->next;}
if(swpstate1.hdr.acknum==p3->msg)
{
flag=1;
if(p3->msg==swpstate1.sendq->next->msg)
swpstate1.sendq->next=p3;
elseswpstate1.sendq->next=p3;
}
swpstate1.sws=swpstate1.sws+(swpstate1.sendq->next->msg-swpstate1.lar);
swpstate1.lar=swpstate1.sendq->next->msg;
swpstate1.hdr.seqnum=swpstate1.hdr.acknum;
printf("最近收到的ACK的帧序号(收到ACK后):
%ld\n",swpstate1.lar);
printf("最近发送的帧序号(此时还未发送新的数据):
%ld\n",swpstate1.lfs);
}
五、总结
1.程序改进与完善
该滑动窗口协议模拟程序还有进一步完善的余地,例如可以对以下一些地方进行改进:
a.改模拟程序通过命令行来实行,缺少形象直观的界面,希望日后可以设计比较简单明了的界面来展示程序的主要模拟功能。
b.现在我们模拟的只是一些用序号代名的简单的帧,建议可以改善程序,使该系统可以模拟更接近真实的数据帧。
这样可以联系实际更直观地去验证滑动窗口协议。
c.整体来说,滑动窗口在实验中是固定的,这样便于处理帧号和ACK的确认以及存储。
但建议可以在日后改进使其变成动态的。
2.设计总结
以上为我所设计的滑动窗口模拟程序。
在程序设计时,我首先了解了其相关原理并且参考了很多与此有关的书籍。
例如,李建中和张冬冬的《滑动窗口规模的动态调整算法》,王栩、李建中、王伟平的《基于滑动窗口的数据流压缩技术及连续查询处理方法》等等。
与此同时我还向同学请教了很多问题,它经过多次修改和整理,可以基本实现所需功能,但因为水平有限,此程序中也存在一定的问题,这需要以后更进一步的改进。
我希望通过本次的课程设计能够端正我的学习态度,锻炼我的文字表达和语言表达能力,提高我的动手实践能力。
附源程序:
#include
#include
#include
main()
{
structswphdr
{
unsignedlongseqnum;
unsignedlongacknum;
unsignedshortflags;/*flag=0表示这是一个数据帧,flag=1表示这是一个ACK帧*/
};
structsendq_slot
{
unsignedshorttimeout;/*1时表示计时启动,0时表示已收到确认*/
unsignedlongmsg;
structsendq_slot*next;
};
structrecvq_slot
{
unsignedshortreceived;/*1表示顺序接收,0表示错序接受*/
unsignedlongmsg;
structrecvq_slot*next;
};
structswpstate
{
unsignedlonglar;
unsignedlonglfs;
unsignedlongsws;
structswphdrhdr;
unsignedlongrws;
structsendq_slot*sendq;
unsignedlongnfe;
structrecvq_slot*head;
}swpstate1;
unsignedlonga;
intflag,b;
/*发送*/
structsendq_slot*sendq_rear,*sendp,*p3,*p4;
structrecvq_slot*recvp,*recvq_rear,*p1,*p2;
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=(structsendq_slot*)malloc(sizeof(structsendq_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)
{
sendp=(structsendq_slot*)malloc(sizeof(structsendq_slot));
if(!
sendp)exit
(1);
sendp->msg=swpstate1.lfs+1;
sendp->timeout=1;
sendp->next=NULL;
sendq_rear->next=sendp;
sendq_rear=sendp;
--swpstate1.sws;
++swpstate1.lfs;
}
swpstate1.hdr.acknum=0;
swpstate1.hdr.flags=0;
printf("最近收到的ACK的帧序号:
%ld\n",swpstate1.lar);
printf("最近发送的帧序号(发送新帧后):
%ld\n",swpstate1.lfs);
/*接收*/
if(swpstate1.hdr.flags==0)
{
do
{
printf("请输入收到的数据帧号:
");
scanf("%ld",&a);
if(a>=swpstate1.nfe&&a<=swpstate1.lfs)
{
if(swpstate1.head==NULL)
{
recvp=recvq_rear=(structrecvq_slot*)malloc(sizeof(structrecvq_slot));
recvp->next=NULL;
swpstate1.head=recvp;
}
else
if(swpstate1.head!
=NULL)
{
recvp=(structrecvq_slot*)malloc(sizeof(structrecvq_slot));
recvp->next=NULL;
recvq_rear->next=recvp;
recvq_rear=recvp;
}
}
else
{
printf("所输数据不在接收窗口内!
");
break;
}
recvp->msg=a;
if(recvp->msg==swpstate1.nfe)
recvp->received=1;
else
recvp->received=0;
--swpstate1.rws;
if(recvp->received==1)
{
a=a-1;
do
{
a=a+1;
if(swpstate1.head==NULL)
break;
p1=swpstate1.head;
flag=0;
while((a!
=p1->msg)&&(p1->next!
=NULL))
{
p2=p1;p1=p1->next;
}
if(a==p1->msg)
{
flag=1;
if(p1==swpstate1.head)
swpstate1.head=swpstate1.head->next;
elsep2->next=p1->next;
swpstate1.nfe=a+1;
swpstate1.hdr.acknum=a+1;
swpstate1.hdr.flags=1;
}
}while(flag==1);
}
printf("ACK号(期待的下一帧的序号):
%ld\n",swpstate1.nfe);
printf("没按序接受的序号:
\n");
p1=swpstate1.head;
while(p1!
=NULL)
{
printf("%ld\t",p1->msg);
p1=p1->next;
}
printf("\n");
printf("继续接收帧?
若是,输入1,若不是,输入0.\n");
scanf("%d",&b);
}while((swpstate1.rws>0)&&(b==1));
}
if(swpstate1.hdr.flags==1)
{
p3=swpstate1.sendq->next;
flag=0;
while((swpstate1.hdr.acknum)!
=p3->msg&&p3->next!
=NULL)
{
p4=p3;p3=p3->next;
}
if(swpstate1.hdr.acknum==p3->msg)
{
flag=1;
if(p3->msg==swpstate1.sendq->next->msg)
swpstate1.sendq->next=p3;
elseswpstate1.sendq->next=p3;
}
swpstate1.sws=swpstate1.sws+(swpstate1.sendq->next->msg-swpstate1.lar);
swpstate1.lar=swpstate1.sendq->next->msg;
swpstate1.hdr.seqnum=swpstate1.hdr.acknum;
printf("最近收到的ACK的帧序号(收到ACK后):
%ld\n",swpstate1.lar);
printf("最近发送的帧序号(此时还未发送新的数据):
%ld\n",swpstate1.lfs);
}
printf("继续执行吗?
是请输入1\n");
scanf("%d",&b);
}while(b==1&&swpstate1.sws>0);
}