计算机网络实验报告.docx
《计算机网络实验报告.docx》由会员分享,可在线阅读,更多相关《计算机网络实验报告.docx(18页珍藏版)》请在冰豆网上搜索。
计算机网络实验报告
计算机网络
实验报告
专业:
班级:
学号:
姓名:
报告时间:
一.实验目的
1.通过上机编程模拟滑动窗口协议中的协议5——后退n帧协议,熟悉和掌握协议5的基本概念、基本原理以及实现方法和过程。
2.通过上机编程模拟滑动窗口协议中的协议6——选择性重传协议,熟悉和掌握协议6的基本概念、基本原理以及实现方法和过程,并与协议5进行对比,加深对滑动窗口协议的理解和认识。
二.实验内容
在Linux、WindowsNT下编程模拟实现滑动窗口协议的1bit滑动窗口协议,需要分别实现发送方功能与接收方功能。
一、实验说明
a)窗口机制
滑动窗口协议的基本原理就是在任意时刻,发送方都维持了一个连续的允许发送的帧的序号,称为发送窗口;同时,接收方也维持了一个连续的允许接收的帧的序号,称为接收窗口。
发送窗口和接收窗口的序号的上下界不一定要一样,甚至大小也可以不同。
不同的滑动窗口协议窗口大小一般不同。
发送方窗口内的序列号代表了那些已经被发送,但是还没有被确认的帧,或者是那些可以被发送的帧。
分析:
①初始态,发送方没有帧发出,发送窗口前后沿相重合。
接收方0号窗口打开,等待接收0号帧;②发送方打开0号窗口,表示已发出0帧但尚确认返回信息。
此时接收窗口状态不变;③发送方打开0、1号窗口,表示0、1号帧均在等待确认之列。
至此,发送方打开的窗口数已达规定限度,在未收到新的确认返回帧之前,发送方将暂停发送新的数据帧。
接收窗口此时状态仍未变;④接收方已收到0号帧,0号窗口关闭,1号窗口打开,表示准备接收1号帧。
此时发送窗口状态不变;⑤发送方收到接收方发来的0号帧确认返回信息,关闭0号窗口,表示从重发表中删除0号帧。
此时接收窗口状态仍不变;⑥发送方继续发送2号帧,2号窗口打开,表示2号帧也纳入待确认之列。
至此,发送方打开的窗口又已达规定限度,在未收到新的确认返回帧之前,发送方将暂停发送新的数据帧,此时接收窗口状态仍不变;⑦接收方已收到1号帧,1号窗口关闭,2号窗口打开,表示准备接收2号帧。
此时发送窗口状态不变;⑧发送方收到接收方发来的1号帧收毕的确认信息,关闭1号窗口,表示从重发表中删除1号帧。
此时接收窗口状态仍不变。
b)后退n协议
由于停等协议要为每一个帧进行确认后才继续发送下一帧,大大降低了信道利用率,因此又提出了后退n协议。
后退n协议中,发送方在发完一个数据帧后,不停下来等待应答帧,而是连续发送若干个数据帧,即使在连续发送过程中收到了接收方发来的应答帧,也可以继续发送。
且发送方在每发送完一个数据帧时都要设置超时定时器。
只要在所设置的超时时间内仍收到确认帧,就要重发相应的数据帧。
如:
当发送方发送了N个帧后,若发现该N帧的前一个帧在计时器超时后仍未返回其确认信息,则该帧被判为出错或丢失,此时发送方就不得不重新发送出错帧及其后的N帧。
从这里不难看出,后退n协议一方面因连续发送数据帧而提高了效率,但另一方面,在重传时又必须把原来已正确传送过的数据帧进行重传(仅因这些数据帧之前有一个数据帧出了错),这种做法又使传送效率降低。
由此可见,若传输信道的传输质量很差因而误码率较大时,连续测协议不一定优于停止等待协议。
此协议中的发送窗口的大小为k,接收窗口仍是1。
c)选择重传协议
在后退n协议中,接收方若发现错误帧就不再接收后续的帧,即使是正确到达的帧,这显然是一种浪费。
另一种效率更高的策略是当接收方发现某帧出错后,其后继续送来的正确的帧虽然不能立即递交给接收方的高层,但接收方仍可收下来,存放在一个缓冲区中,同时要求发送方重新传送出错的那一帧。
一旦收到重新传来的帧后,就可以原已存于缓冲区中的其余帧一并按正确的顺序递交高层。
这种方法称为选择重发(SELECTICEREPEAT),其工作过程如图所示。
显然,选择重发减少了浪费,但要求接收方有足够大的缓冲区空间。
三、实验结果
在发送端,通过接收方不发送应答(不校验,不保存,不送主机)的方式来模拟发送方发送数据超时。
发送方要能处理超时,就需要另外开辟线程来接收确认信号,发送方发送数据帧后10秒内收不到回应认为发送超时。
为了模拟发送端接收ack、nak超时,在收到接收端回复的数据帧时,以20%的概率不处理确认结果,并且再次发送该数据帧,从而模拟接收端发送回复(nak、ack)超时。
接收端收到数据帧后,以20%的概率模拟数据校验错误,此时丢弃该帧,并发送nak回应。
同样,以20%的概率模拟接收方接收数据超时,此时不处理接收的数据帧,也不发送ack和nak,让发送方自行检测发送超时。
本实验中假定从主机取数据帧和送数据帧到主机都是足够快的,为了模拟出各种效果,加大了超时、校验错的概率。
为了看到效果,使用了Sleep()函数。
发送方连续发送数据5分钟后会提示是否继续发送,如果输入’q’或’Q’则终止发送进程。
步骤:
1.在VS6下创建两个win32控制台工程,SlideWindowReceiver和SlideWindowSender,设置好环境变量,使得作业文件夹组织形式符合实验要求。
另外,需要给两个工程都添加ws2_32.lib这个库文件。
2.编写代码,调试运行。
由于需要用到SOCKET,使用.c作为源码文件的后缀名称时,报出了很多错误,改用.cpp作为后缀名称后能够顺利编译通过。
程序的运行效果:
图1:
启动接收窗口、发送窗口,建立连接并发送数据帧的效果图。
图中第一帧发送成功,检测到第二帧是错误的,发送方重传过程又出现了一次超时。
图2:
超时重发的情况。
四.源程序
include"common.h"
#defineRECEIVE_MAX_LENGTH100
#defineSEND_MAX_LENGTH100
//void(interruptfar*oldhandler)();
voidmain()
{
Begin:
WORDwVersionRequested;
WSADATAwsaData;
interr;
wVersionRequested=MAKEWORD(1,1);
err=WSAStartup(wVersionRequested,&wsaData);
if(err!
=0)
{
Sleep(SLEEPMS);
return;
}
if(LOBYTE(wsaData.wVersion)!
=1
||HIBYTE(wsaData.wVersion)!
=1)
{
WSACleanup();
Sleep(SLEEPMS);
return;
}
SOCKETsocksrv=socket(AF_INET,SOCK_STREAM,0);//监听的套接字
SOCKADDR_INsocketadd;
socketadd.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
socketadd.sin_family=AF_INET;
socketadd.sin_port=htons(7001);
if(SOCKET_ERROR==bind(socksrv,(SOCKADDR*)&socketadd,sizeof(SOCKADDR)))
{
printf("binderr\n");
WSACleanup();
Sleep(SLEEPMS);
return;
}
if(SOCKET_ERROR==listen(socksrv,5))
{
printf("listenerr");
WSACleanup();
Sleep(SLEEPMS);
return;
}
SOCKADDR_INsockclient;
intlen=sizeof(SOCKADDR);
SOCKETsockconn=accept(socksrv,(SOCKADDR*)&sockclient,&len);//建立连接的套节字
if(INVALID_SOCKET==sockconn)
{
printf("accerr\n");
WSACleanup();
Sleep(SLEEPMS);
return;
}
charsendData[SEND_MAX_LENGTH];
memset(sendData,0,SEND_MAX_LENGTH);
sprintf(sendData,"%s","hellosender,i\'mreceiver");
if(SOCKET_ERROR==send(sockconn,sendData,strlen(sendData)+1,0))
{
printf("sendwelcomeerr\n");
WSACleanup();
closesocket(sockconn);
Sleep(SLEEPMS);
return;
}
chargetData[RECEIVE_MAX_LENGTH];
memset(getData,0,RECEIVE_MAX_LENGTH);
recv(sockconn,getData,RECEIVE_MAX_LENGTH,0);
printf("%s\n",getData);
printf("1bit滑动窗口协议:
接收方,接收窗口=1\n");
LinkQueueQueueQ;
InitLine(&QueueQ);
framepacketreceive;//data
framepacketsend;//ack,nak
//初始化接收窗口
intcurw=GetFrameFromHost(&QueueQ);
intret=0;
while
(1)
{
//接收数据帧
memset(&packetreceive,0,sizeof(packetreceive));
Sleep(SLEEPMS);
printf("\n");
ret=recv(sockconn,(char*)&packetreceive,sizeof(packetreceive),0);
if(ret==SOCKET_ERROR||ret==SOCKET_DISCONN)
{
if(ret==SOCKET_ERROR)
{
cprintf("recverr!
Autocontinue\n");
//while(!
kbhit()){/*donothing*/};
continue;
}
else
{
cprintf("连接已断开,按q退出,其他键等待新的连接\n");
//while(!
(kbc=kbhit())){/*donothing*/};
intkbc=getch();
if(kbc=='q'||kbc=='Q')
break;
else
{
WSACleanup();
closesocket(sockconn);
Sleep(SLEEPMS);
gotoBegin;
}
}
}
srand((unsigned)time(NULL));
switch(rand()%5)//假定产生随机结果,20%的概率校验错误或接收发送方超时
{
case0:
printf("framereceived:
%d,%d,校验错误\n",packetreceive.head.seq,packetreceive.size);
memset(&packetsend,0,sizeof(packetsend));
memcpy(&packetsend,&packetreceive,sizeof(packetreceive));
packetsend.head.ack=packetreceive.head.seq;
packetsend.head.seq=curw;
packetsend.head.kind=nak;
printf("发送否认帧NAK:
%d\n",packetreceive.head.seq);
break;
case1:
packetsend.head.kind=tout;//没有使用多线程,直接发送超时帧来模拟未发送方发送数据超时或者接收方发送确认超时
printf("发送方发送数据超时(模拟):
%d\n",packetreceive.head.seq);
break;
default:
printf("framereceived:
%d,%d,校验正确\n",packetreceive.head.seq,packetreceive.size);
if(packetreceive.head.seq==(QueueAnswer(&QueueQ,curw)).head.ack)//上一帧的重发
{
printf("上一帧的重发,直接发送确认帧ACK:
%d\n",packetreceive.head.seq);
}
else
{
printf("newframereceived:
%d,%d\n",packetreceive.head.seq,packetreceive.size);
curw=DeLine(&QueueQ,&packetreceive,curw);//将新帧保存待送往主机
memset(&packetsend,0,sizeof(packetsend));
packetsend=QueueAnswer(&QueueQ,curw);//待发送的确认帧
printf("发送确认帧ACK:
%d\n",packetreceive.head.seq);
}
packetsend.head.kind=ack;
break;
}
/*if(packetreceive.head.seq==(QueueAnswer(&QueueQ,curw)).head.ack)//上一帧的重发
{
if(packetsend.head.kind==nak)
{
printf("framere-received:
%d,%d,再次接收和校验\n",packetreceive.head.seq,packetreceive.size);
ReLine(&QueueQ,&packetreceive,curw);
packetsend.head.kind=ack;
printf("校验通过,发送确认帧ACK:
%d\n",packetreceive.head.seq);
}
else
{
printf("framere-received:
%d,%d,丢弃\n",packetreceive.head.seq,packetreceive.size);
packetsend.head.kind=ack;
printf("发送确认帧ACK:
%d\n",packetreceive.head.seq);
}
}
else//新数据帧,给发送方发送随机结果:
ack/nak/tout
{
printf("newframereceived:
%d,%d\n",packetreceive.head.seq,packetreceive.size);
curw=DeLine(&QueueQ,&packetreceive,curw);//将新帧保存待送往主机
memset(&packetsend,0,sizeof(packetsend));
packetsend=QueueAnswer(&QueueQ,curw);//待发送的确认帧
srand((unsigned)time(NULL));
switch(rand()%3)
{
case0:
packetsend.head.kind=ack;
printf("发送确认帧ACK:
%d\n",packetreceive.head.seq);
break;
case1:
packetsend.head.kind=nak;
printf("发送否认帧NAK:
%d\n",packetreceive.head.seq);
break;
case2:
packetsend.head.kind=tout;//没有使用多线程,直接发送超时帧来模拟未发送方发送数据超时或者接收方发送确认超时
printf("发送方发送数据超时或者接收方发送回复超时(模拟):
%d\n",packetreceive.head.seq);
break;
}
}
if(packetreceive.head.kind!
=data)printf("seqerr:
%d\n",packetreceive.head.seq);*/
//Sleep(SLEEPMS);
if(packetsend.head.kind==tout)continue;//客户端使用多线程判断超时
ret=send(sockconn,(char*)&packetsend,sizeof(packetsend),0);
if(ret==SOCKET_ERROR)
{
cprintf("senderr!
Autocontinue\n");
//while(!
kbhit()){/*donothing*/};
continue;
}
}
cprintf("Pressanykeytoquit.\n");
while(!
kbhit()){/*donothing*/};
Sleep(SLEEPMS);
cprintf("====Bye====\n");
WSACleanup();
closesocket(sockconn);
Sleep(SLEEPMS);
}
#include"common.h"
#defineRECEIVE_MAX_LENGTH100
#defineSEND_MAX_LENGTH100
CRITICAL_SECTIONgCS;
SOCKETsocketClient;
intret;
voidmain()
{
cprintf("connecttoreceiver...\n");
Begin:
WORDwVersionRequested;
WSADATAwsaData;
interr;
wVersionRequested=MAKEWORD(1,1);
err=WSAStartup(wVersionRequested,&wsaData);
if(err!
=0)
{
Sleep(SLEEPMS);
return;
}
if(LOBYTE(wsaData.wVersion)!
=1||HIBYTE(wsaData.wVersion)!
=1)
{
WSACleanup();
Sleep(SLEEPMS);
return;
}
/*SOCKET*/socketClient=socket(AF_INET,SOCK_STREAM,0);
SOCKADDR_INclientadd;
clientadd.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
clientadd.sin_family=AF_INET;
clientadd.sin_port=htons(7001);
if(SOCKET_ERROR==connect(socketClient,(SOCKADDR*)&clientadd,sizeof(SOCKADDR)))
{
//clrscr();
//cprintf("conerr,waitconnecttoreceiver\n");
WSACleanup();
Sleep(SLEEPMS);
//return;
gotoBegin;
}
chargetData[RECEIVE_MAX_LENGTH];
memset(getData,0,RECEIVE_MAX_LENGTH);
if(recv(socketClient,getData,RECEIVE_MAX_LENGTH,0)==SOCKET_ERROR)
{
printf("recverr\n");
}
else
{
printf("%s\n",getData);
}
charsendData[SEND_MAX_LENGTH];
memset(sendData,0,SEND_MAX_LENGTH);
strcpy(sendData,"helloreceiver,i\'msender");
if(SOCKET_ERROR==send(socketClient,sendData,strlen(sendData)+1,0))
{
printf("sendwelcomeerr\n");
WSACleanup();
closesocket(socketClient);
Sleep(SLEEPMS);
return;
}
cprintf("Pressanykeytocontinue\n");
while(!
kbhit()){/*donothing*/};
Sleep(SLEEPMS);
printf("1bit滑动窗口协议:
发送方,发送窗口=1\n");
LinkQueueQueueQ;
InitLine(&QueueQ);
framepacketsend;//data
framepacketreceive;//ack,nak
unsignedlongtick=GetTickCount();
intret=0;
HAND