版权声明:所有资料资源均应用作教育用途,请勿用作商业用途 https://blog.csdn.net/qq_38876114/article/details/84634830
一,实验流程
- 熟悉实验系统。熟悉实验系统的操作流程,帮助文档的debug功能等等
- 设计算法,实验分为三个函数,依次为等停式传输,回退N帧传输和选择性重传三种方式的实现
函数,其中,第一种等停式传输是第二种回退N传输的特例,第三种选择性传输和第二种传输方式的区别是在重传方面需要传送指定的帧,因此整个逻辑基本以第二个方法为框架进行修改。
对于第二个函数:做如下设计,- 保存一个链表
waiting
作为输入缓冲区,当数据无法发送时,存入输入缓冲区。 - 保存一个窗口数组
window
,窗口内保存看现在等待确认的已经发送的数据包。 - 保存一个窗口上下界的序号
upper,lower
使用模运算来确定当前的数据对应于window
中
的哪一个位置 - 对于发送命令,当前
upper,lower
之间的差已经达到最大的窗口,那么就存入waiting
链表中,否则就将其发送,并将其缓存至window
数组的upper
对应的位置,并且upper
增加 - 对于
receive
命令,判断到来的ack,找到对应的在window
数组中的数据包,将lower
到该位置的数据包丢弃,并且重新发送输入缓冲区waiting
的数据直到缓冲区为空或者窗口重新到达最大。 - 对于
timeout
命令,查看其返回ack
,如果在窗口内部,则重新发送没有收到确认的数据包
- 保存一个链表
- 对于第一个函数,将上述算法窗口设置为1即可
对于第三个函数,取消timeout
的选项,当收到receive
命令时,判断其是确认收到命令还是重发命令,如果是确认命令,和第二个函数操作相同,对于重发命令,则遍历窗口找到对应的seq
的数据包并且重新发送即可
二,实验遇到的问题
- 不要将数据帧的seq序列号和上述设计算法过程中的upper混淆,
- 对于数据frame中的size变量和发送函数中的bufferSize变量意义不同
- 超时回退N重发操作(这里是实验说明略有歧义?)需要重发整个窗口内所有数据,和说明略有不符。
- 实验说明上还有一点可能有一些歧义,在受到ack确认后重发操作实验说明上说重发一个数据,但是滑动窗口理应最大化窗口以达到最高的数据线路利用率,因此有些奇怪。但是尝试之后就可以确定时将发送窗口最大化。
实验代码
#include "sysinclude.h"
extern void SendFRAMEPacket(unsigned char* pData, unsigned int len);
#define WINDOW_SIZE_STOP_WAIT 1
#define WINDOW_SIZE_BACK_N_FRAME 4
#define MSG_TYPE_TIMEOUT 1
#define MSG_TYPE_SEND 2
#define MSG_TYPE_RECEIVE 3
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 Buffer {
frame* data;
int dataSize;
Buffer* nextBuffer;
};
typedef struct Window {
frame data;
int size;
};
Buffer* addFrameWait(Buffer* waiting, frame* pBuffer, int bufferSize) {
Buffer* b = (Buffer*)malloc(sizeof(Buffer));
b->data = (frame*)malloc(bufferSize);
memcpy(b->data, pBuffer, bufferSize);
b->dataSize = bufferSize;
b->nextBuffer = NULL;
if (waiting == NULL) {
waiting = b;
}
else {
Buffer* buf = waiting;
while (buf->nextBuffer != NULL) {
buf = buf->nextBuffer;
}
buf->nextBuffer = b;
}
return waiting;
}
/*
* 停等协议测试函数
*/
int stud_slide_window_stop_and_wait(char *pBuffer, int bufferSize, UINT8 messageType)
{
//可能重发窗口
static Window window[WINDOW_SIZE_STOP_WAIT];
//当前窗口上限,下限
static int upper = 0, lower = 0;
//待发送缓冲区
static Buffer* waiting = NULL;
switch (messageType) {
//发送数据包
case MSG_TYPE_SEND: {
//当前剩余窗口未打开,可以发送当前帧,并且将发送的帧放入待重发的缓冲区
if (upper - lower < WINDOW_SIZE_STOP_WAIT) {
printf("-----send a message,seq is %d\n", ntohl((((frame*)pBuffer)->head).seq));
SendFRAMEPacket((unsigned char*)pBuffer, bufferSize);
window[upper%WINDOW_SIZE_STOP_WAIT].data = *(frame*)pBuffer;
window[upper%WINDOW_SIZE_STOP_WAIT].size = bufferSize;
upper++;
}
//否则,代表窗口开到最大,将数据存入待发送列表,直接返回
else {
printf("-----buffer a message.seq is %d\n", ntohl(((frame*)pBuffer)->head.seq));
waiting = addFrameWait(waiting, (frame*)pBuffer, bufferSize);
}
return 0;
}
//收到ack
case MSG_TYPE_RECEIVE: {
//检查ack确认号
int ackBack = ntohl(((frame*)pBuffer)->head.ack);
printf("-----receive a message,ack is %d\n", ackBack);
int seqLoacl = ntohl(window[lower%WINDOW_SIZE_STOP_WAIT].data.head.seq);
//确认发送的帧已经收到,从缓冲区取出新的帧加入窗口lower%的位置,并将其发送
if (ackBack == seqLoacl) {
if (waiting != NULL) {
SendFRAMEPacket((unsigned char*)waiting->data, waiting->dataSize);
window[lower%WINDOW_SIZE_STOP_WAIT].data = *(waiting->data);
window[lower%WINDOW_SIZE_STOP_WAIT].size = waiting->dataSize;
waiting = waiting->nextBuffer;
upper++;
}
lower++;
}
break;
}
case MSG_TYPE_TIMEOUT: {
//直接发送窗口的帧
int timeoutNum = *(unsigned*)pBuffer;
printf("-----timeout a message,seq is %d\n", timeoutNum);
SendFRAMEPacket((unsigned char*)&(window[0].data), window[0].size);
break;
}
}
return 0;
}
/*
* 回退n帧测试函数
*/
int stud_slide_window_back_n_frame(char *pBuffer, int bufferSize, UINT8 messageType)
{
//可能重发窗口
static Window window[WINDOW_SIZE_BACK_N_FRAME];
//当前窗口上限,下限
static int upper = 0, lower = 0;
//待发送缓冲区
static Buffer* waiting = NULL;
switch (messageType) {
case MSG_TYPE_SEND: {
//当前剩余窗口未打开,可以发送当前帧,并且将发送的帧放入待重发的缓冲区
if (upper - lower < WINDOW_SIZE_BACK_N_FRAME) {
printf("-----send a message,seq is %d\n", ntohl((((frame*)pBuffer)->head).seq));
SendFRAMEPacket((unsigned char*)pBuffer, bufferSize);
window[upper%WINDOW_SIZE_BACK_N_FRAME].data = *(frame*)pBuffer;
window[upper%WINDOW_SIZE_BACK_N_FRAME].size = bufferSize;
upper++;
}
//否则,代表窗口开到最大,将数据存入待发送列表,直接返回
else {
printf("-----buffer a message.seq is %d\n", ntohl(((frame*)pBuffer)->head.seq));
waiting = addFrameWait(waiting, (frame*)pBuffer, bufferSize);
}
return 0;
}
case MSG_TYPE_RECEIVE: {
//检查ack
int ackBack = ntohl(((frame*)pBuffer)->head.ack);
printf("-----receive a message,ack is %d\n", ackBack);
//遍历当前窗口,对于小于ack的帧确认收到
for (int i = lower; i < upper; i++) {
int seqLocal = ntohl(window[i%WINDOW_SIZE_BACK_N_FRAME].data.head.seq);
//确认i帧已经收到,将lower和i之间的所有窗口释放并且发送新的多个数据
if (ackBack == seqLocal) {
lower = i + 1;
while (upper - lower < WINDOW_SIZE_BACK_N_FRAME && waiting != NULL) {
SendFRAMEPacket((unsigned char*)waiting->data, waiting->dataSize);
window[upper%WINDOW_SIZE_BACK_N_FRAME].data = *(waiting->data);
window[upper%WINDOW_SIZE_BACK_N_FRAME].size = waiting->dataSize;
upper++;
waiting = waiting->nextBuffer;
}
break;
}
}
break;
}
case MSG_TYPE_TIMEOUT: {
//检查timeout的帧号,将其之后的数据全部重发
unsigned timeOutSeq = *(unsigned*)pBuffer;
printf("-----timeout a frame seq is %d\n", timeOutSeq);
for (int i = lower; i < upper; i++) {
unsigned seqLocal = ntohl(window[i%WINDOW_SIZE_BACK_N_FRAME].data.head.seq);
if (seqLocal == timeOutSeq) {
printf("-----resend seq from %d to %d\n", lower, upper);
for (int j = lower; j < upper; j++) {
frame* fra = &(window[j%WINDOW_SIZE_BACK_N_FRAME].data);
int size = window[j%WINDOW_SIZE_BACK_N_FRAME].size;
SendFRAMEPacket((unsigned char*)fra, size);
}
break;
}
}
break;
}
}
return 0;
}
/*
* 选择性重传测试函数
*/
int stud_slide_window_choice_frame_resend(char *pBuffer, int bufferSize, UINT8 messageType)
{
//可能重发窗口
static Window window[WINDOW_SIZE_BACK_N_FRAME];
//当前窗口上限,下限
static int upper = 0, lower = 0;
//待发送缓冲区
static Buffer* waiting = NULL;
switch (messageType) {
case MSG_TYPE_SEND: {
//当前剩余窗口未打开,可以发送当前帧,并且将发送的帧放入待重发的缓冲区
if (upper - lower < WINDOW_SIZE_BACK_N_FRAME) {
printf("-----send a message,seq is %d\n", ntohl((((frame*)pBuffer)->head).seq));
SendFRAMEPacket((unsigned char*)pBuffer, bufferSize);
window[upper%WINDOW_SIZE_BACK_N_FRAME].data = *(frame*)pBuffer;
window[upper%WINDOW_SIZE_BACK_N_FRAME].size = bufferSize;
upper++;
}
//否则,代表窗口开到最大,将数据存入待发送列表,直接返回
else {
printf("-----buffer a message.seq is %d\n", ntohl(((frame*)pBuffer)->head.seq));
waiting = addFrameWait(waiting, (frame*)pBuffer, bufferSize);
}
return 0;
}
case MSG_TYPE_RECEIVE: {
int ackKind = ntohl(((frame*)pBuffer)->head.kind);
if (ackKind == ack) {
//检查ack
int ackBack = ntohl(((frame*)pBuffer)->head.ack);
printf("-----receive a message,ack is %d\n", ackBack);
//遍历当前窗口,对于小于ack的帧确认收到
for (int i = lower; i < upper; i++) {
int seqLocal = ntohl(window[i%WINDOW_SIZE_BACK_N_FRAME].data.head.seq);
//确认i帧已经收到,将lower和i之间的所有窗口释放并且发送新的多个数据
if (ackBack == seqLocal) {
lower = i + 1;
while (upper - lower < WINDOW_SIZE_BACK_N_FRAME && waiting != NULL) {
SendFRAMEPacket((unsigned char*)waiting->data, waiting->dataSize);
window[upper%WINDOW_SIZE_BACK_N_FRAME].data = *(waiting->data);
window[upper%WINDOW_SIZE_BACK_N_FRAME].size = waiting->dataSize;
upper++;
waiting = waiting->nextBuffer;
}
break;
}
}
}
else if (ackKind == nak) {
//重发nak号帧
int nakBack = ntohl(((frame*)pBuffer)->head.ack);
printf("-----resend message .seq is %d\n", nakBack);
for (int i = lower; i < upper; i++) {
frame* fra = &(window[i%WINDOW_SIZE_BACK_N_FRAME].data);
int size = window[i%WINDOW_SIZE_BACK_N_FRAME].size;
int seqLocal = ntohl(fra->head.seq);
if (seqLocal == nakBack) {
SendFRAMEPacket((unsigned char*)fra, size);
break;
}
}
}
break;
}
}
return 0;
}