滑动窗口协议实验报告
一、
实验目的
计算机网络的数据链路层协议保证通信双方在有差错的通信线路上进行无差错的数据传输,是
计算机网络各层协议中通信控制功能最典型的一种协议。
本实验实现一个数据链路层协议的数据传送部分,目的在于更好地理解数据链路层协议中的“滑
动窗口”技术的基本工作原理,掌握计算机网络协议的基本实现技术。
二、
实验要求
在一个数据链路层的模拟实现环境中,用 C 语言实现下面三个数据链路层协议。
1)1 比特滑动窗口协议
2)回退 N 帧滑动窗口协议
3)选择性重传协议
三、
实验内容
充分理解滑动窗口协议,根据滑动窗口协议,模拟滑动窗口协议中发送端的功能,对系统发送
的帧进行缓存并加入窗口等待确认,并在超时或者错误时对部分帧进行重传。
编写停等及退回 N 滑动窗口协议函数,响应系统的发送请求、接收帧消息以及超时消息,并根
据滑动窗口协议进行相应处理。
编写选择性重传协议函数,响应系统的发送请求、接受帧消息以及错误消息,并根据滑动窗口
协议进行相应处理。
四、
实验结果及讨论
实验代码见附录。实验结果如下图所示。
停等协议实验很顺利,但是做回退 N 帧实验时,起初实验服务器超时,选择重传实验也是一样。
在进行调试的过程中,发现回退 N 帧确认机制是累计确认,并且对于 timeout 的处理是将发送
窗口内的所有帧都重传一遍,而不是从超时的帧开始重传,修改后,不再报服务器超时的错,但是
实验结果为失败。重新检查代码,发现在 MSG_TYPE_RECEIVE 中一个变量写错了,修改后实验结果
成功。
同样地,修改了选择重传接收代码中的变量错误,实验结果也成功了。
课堂中老师并没有仔细讲回退 N 帧确认机制,通过本实验对该协议有了更深入的理解。
另外,对回退 N 帧超时重传机制理解的不同,导致实验过程不顺利。实际上,按照课上的例子,
可以理解为对发送窗口全部重传,也可以理解为从 timeout 帧开始重传。事实上,应该是从 timeout
帧开始传,本实验中将发送窗口中全部发送实验才成功,这可能是实验平台的一个 bug。
附录:
#include "sysinclude.h"
#include
extern void SendFRAMEPacket(unsigned char* pData, unsigned int len);
using namespace std;
#define WINDOW_SIZE_STOP_WAIT 1
#define WINDOW_SIZE_BACK_N_FRAME 4
typedef enum {data,ack,nak} frame_kind;
typedef struct frame_head
{
};
frame_kind kind; //帧类型
unsigned int seq; //序列号
unsigned int ack; //确认号
unsigned char data[100]; //数据
typedef struct frame
{
};
frame_head head; //帧头
unsigned int size; //数据的大小
typedef struct localFrame
{
};
frame* fr;
int size;
deque stop_wait_send_queue;
//停等协议发送队列
deque back_n_send_queue;
//回退 n 帧发送队列
deque choice_resend_send_queue;
//选择重传发送队列
int current_window_stop_wait = 0;
//停等协议当前窗口大小
int current_window_back_n = 0;
//回退 n 帧当前窗口大小
int current_window_choice_resend = 0;
//选择重传当前窗口大小
/*
* 停等协议测试函数
*/
int stud_slide_window_stop_and_wait(char *pBuffer, int bufferSize, UINT8 messageType)
{
unsigned int ack;
unsigned int seq;
localFrame buffer;
localFrame send;
//缓存
//发送帧
localFrame receive;
//接收的对应帧
if(messageType == MSG_TYPE_TIMEOUT)
{
}
seq = ntohl(*(unsigned int*)pBuffer);
//取出超时帧的序列号
send = stop_wait_send_queue.front();
//超时重发队列头部的帧
if(seq == send.fr->head.seq)
{
}
SendFRAMEPacket((unsigned char*)send.fr,(unsigned int)send.size);
return 0;
if(messageType == MSG_TYPE_SEND)
{
buffer.fr = new frame;
*buffer.fr = *(frame*)pBuffer; //缓存要发送的帧
buffer.size = bufferSize;
stop_wait_send_queue.push_back(buffer);//将要发送的帧加入发送队列
if(current_window_stop_wait < WINDOW_SIZE_STOP_WAIT)//发送窗口为空时,发送该帧
{
}
current_window_stop_wait++;
send = stop_wait_send_queue.front();
SendFRAMEPacket((unsigned char*)send.fr,(unsigned int)send.size);
return 0;
}
if(messageType == MSG_TYPE_RECEIVE)
{
ack = ((frame*)pBuffer)->head.ack;
if(stop_wait_send_queue.size() != 0)
{
receive = stop_wait_send_queue.front();
if(ack == receive.fr->head.seq)
{
}
stop_wait_send_queue.pop_front();
if(stop_wait_send_queue.size() != 0)//若发送队列不为空,且接收正确,发送下一
{
}
send = stop_wait_send_queue.front();
SendFRAMEPacket((unsigned char*)send.fr,(unsigned int)send.size);
current_window_stop_wait--;
//若发送队列为空则将发送窗口置为空
帧
}
else
{
}
return 0;
}
return 0;
}
/*
先判断 messageType,是 TIMEOUT 的话读 pBuffer 的前 4 个字节,在停等协议中应该是最
后一个字节,判断是 0 还是 1;如果的 SEND 的话,创建一个变量保存 pBuffer 所指向的 4
内容,并调用 SendFRAMEPacket 发送。
*/
/*
* 回退 n 帧测试函数
*/
int stud_slide_window_back_n_frame(char *pBuffer, int bufferSize, UINT8 messageType)
{
unsigned int ack;
unsigned int seq;
localFrame buffer;
localFrame send;
localFrame receive;
int ack_frame = 0;
//缓存
//发送帧
//接收的对应帧
//ack 帧对应的帧
if(messageType == MSG_TYPE_TIMEOUT)
{
for(int i = 0; i < WINDOW_SIZE_BACK_N_FRAME && i < back_n_send_queue.size(); i++)
send = back_n_send_queue[i];
SendFRAMEPacket((unsigned char*)send.fr,(unsigned int)send.size);
{
}
/*
seq = ntohl(*(unsigned int*)pBuffer);
//取出超时帧的序列号
//send = back_n_send_queue.front();
//超时重发队列头部的帧
if(seq == send.fr->head.seq)
{
}//
SendFRAMEPacket((unsigned char*)send.fr,(unsigned int)send.size);
int timeout_frame = 0;
for(;
timeout_frame
< WINDOW_SIZE_BACK_N_FRAME
&&
timeout_frame
<
back_n_send_queue.size(); timeout_frame++)
{
}
send = back_n_send_queue[timeout_frame];
if(send.fr->head.seq == seq)
{
}
break;
for(int i = timeout_frame; i < WINDOW_SIZE_BACK_N_FRAME && i < back_n_send_queue.size();
i++)
{
}
*/
send = back_n_send_queue[i];
SendFRAMEPacket((unsigned char*)send.fr,(unsigned int)send.size);
return 0;
}
if(messageType == MSG_TYPE_SEND)
{
buffer.fr = new frame;
*buffer.fr = *(frame*)pBuffer; //缓存要发送的帧
buffer.size = bufferSize;
back_n_send_queue.push_back(buffer);
//将要发送的帧加入发送队列
if(current_window_back_n < WINDOW_SIZE_BACK_N_FRAME)//发送窗口未满时,发送该帧
{
}
current_window_back_n++;
send = back_n_send_queue.back();
SendFRAMEPacket((unsigned char*)send.fr,(unsigned int)send.size);
return 0;
}
if(messageType == MSG_TYPE_RECEIVE)
{
ack = ((frame*)pBuffer)->head.ack;
for
(ack_frame = 0; ack_frame < WINDOW_SIZE_BACK_N_FRAME && ack_frame <
back_n_send_queue.size(); ack_frame++)
//找出 ack 对应的帧
receive = back_n_send_queue[ack_frame];
if(ack == receive.fr->head.seq)
break;
{
}
if(ack_frame == WINDOW_SIZE_BACK_N_FRAME || ack_frame == back_n_send_queue.size())
//排除 ack 帧不在发送窗口的情况
{
}
ack_frame = -1;
int i = 0;
while(i <= ack_frame) //将 ack 对应的帧及其之前的帧移出发送队列
{
}
back_n_send_queue.pop_front();
current_window_back_n--;
i++;
for
(i
=
current_window_back_n;
i
< WINDOW_SIZE_BACK_N_FRAME && i
<
back_n_send_queue.size() && current_window_back_n < WINDOW_SIZE_BACK_N_FRAME; i++)