资源描述
中文注释
编程实现一个GBN传输协议的发送方和接收方两程序,采用编程语言不限,要求能将发送――接收流程以及处理方法表现出来.
附源代码及注释
源代码:
三:GBN-CS.c
#include "GBN.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
extern int TRACE ; /* 用于跟踪 */
extern int nsim ; /* 当前第5层到第4层的信息数 */
extern int nsimmax; /*"msg"生成数量,直到停止,生成的最大信息数 */
extern float time; //时间
extern int packet_correct; //包的正确到达数
extern int packet_resent; //包的重传数
extern struct event *evlist; //事件
int main() //主函数
{
struct event *eventptr;//变量,结构体声明
struct msg msg2give;
struct pkt pkt2give;
int i,j;
char c;
init();//初始化
A_init();//A端初始化
B_init();//B端初始化
while (1) //循环,用于每个包的传送
{
eventptr = evlist; /* 从模拟器得到下一个事件 */
if (eventptr==NULL)
goto terminate;
evlist = evlist->next; /* 从事件清单改变该事件 */
if (evlist!=NULL)
evlist->prev=NULL;
if (TRACE >= 2)//打印与事件相关的信息
{
printf("\n事件时间: %f,",eventptr->evtime);
printf(" 类型: %d",eventptr->evtype);
if (eventptr->evtype==0)
printf(", 计时器中断: ");
else if (eventptr->evtype==1)
printf(", 来自第五层 ");
else
printf(", 来自第三层 ");
if (eventptr->eventity == A)//打印事件实体
{
printf(" 通信实体: %d A端\n",eventptr->eventity);
}
else
{
printf(" 通信实体: %d B端\n",eventptr->eventity);
}
}
time = eventptr->evtime; /* 更新下一个事件的时间 */
if (nsim==nsimmax)
break; /* 当输入包个数等于生成的最大包个数,模拟器停止 */
if (eventptr->evtype == FROM_LAYER5 ) //如果事件调用来自第五层
{
generate_next_arrival(); /* 为将来的包的到达做准备 */
/* 将相同的信息转换为字母字符串 */
j = nsim % 26;
for (i=0; i<20; i++)
msg2give.data[i] = 97 + j;
if (TRACE>2)
{
printf(" 学生发送的主要数据是: ");
for (i=0; i<20; i++)
printf("%c", msg2give.data[i]);//打印信息
printf("\n");
}
nsim++;
if (eventptr->eventity == A)//A端向外发送数据
{
A_output(msg2give);
}
else//B端向外发送数据
{
B_output(msg2give);
}
}
else if (eventptr->evtype == FROM_LAYER3)//如果事件调用来自第三层
{
pkt2give.seqnum = eventptr->pktptr->seqnum;
pkt2give.acknum = eventptr->pktptr->acknum;
pkt2give.checksum = eventptr->pktptr->checksum;
for (i=0; i<20; i++)
pkt2give.payload[i] = eventptr->pktptr->payload[i];
if (eventptr->eventity == A) /* A端交付包*/
A_input(pkt2give); /* 适当的实体*/
else /* B端交付包*/
B_input(pkt2give);
free(eventptr->pktptr); /* 释放包的缓存 */
}
else if (eventptr->evtype == TIMER_INTERRUPT)//计时器中断情况
{
if (eventptr->eventity == A)
A_timerinterrupt();
else
B_timerinterrupt();
}
else //校检,防止发生不可预料事件
{
printf("内部警告: 不可预知的事件类型!! \n");
}
free(eventptr);
}
terminate://终止
printf(" 模拟器停止在: %f s \n ,从第五层发送了 %d个包\n",time,nsim);
printf(" 正确发送包的个数: %d \n", packet_correct);
printf(" 重发包的个数: %d \n", packet_resent);
system("pause");//暂停
}
一.GBN.h
#pragma once
#include <stdio.h>
//基础功能模块的数据结构声明
#define BIDIRECTIONAL 1 /* 改变值1如果你需要写额外的可靠程序或B输出
程序(即B端系统)*/
/* "msg"结构体是由第五层(教师代码)转变成第四层的数据单元(学生代码)
*/
/*它包括了经由第五层向学生层协议通信实体交付的数据(字符)*/
struct msg
{ char data[20];
};
/* "pkt"结构体是第4层(学生代码)向第3层传送的数据单元(老师代码)。值
得注意的是预先定义的包结构,这一切学生必须遵守。*/
struct pkt
{
int seqnum;//顺序号
int acknum;//应答号
int checksum;//检查和
char payload[20];
};
#define WINDOWSIZE 8 //定义滑动窗口大小
#define MAXBUFSIZE 50//最大窗口大小
#define RTT 15.0//周游时间
#define NOTUSED 0//没用到
#define NACK -1//否定应答
#define TRUE 1
#define FALSE 0
#define A 0
#define B 1
//网络仿真部分数据结构声明
***********************************************************
struct event
{
float evtime; /* 事件时间 */
int evtype; /* 事件类型 */
int eventity; /* 事件实体*/
struct pkt *pktptr; /* 指向这个事件的指针(如果需要) */
struct event *prev; //前一事件
struct event *next; //后一事件
};
/* possible events: */
#define TIMER_INTERRUPT 0 //事件中断
#define FROM_LAYER5 1 //来自第五层
#define FROM_LAYER3 2 //来自第三层
#define OFF 0
#define ON 1
//基础功能模块的函数声明
*******************************************************************
void ComputeChecksum(struct pkt *packet);//计算校验和
int CheckCorrupted(struct pkt packet);//检查数据是否出错
void A_output( struct msg message);//A端向外发送数据
void A_input(struct pkt packet);//A端接收数据
void A_timerinterrupt();//A计时器超时
void A_init();//A端初始化
void B_output(struct msg message);
void B_input(struct pkt packet);
void B_timerinterrupt();
void B_init();
//网络仿真部分的函数声明
**************************************************
void init(); //初始化仿真器
float jimsrand();//随机数发生器[0,1]
//处理事件列表部分的函数声明
*********************************************
void generate_next_arrival();//产生下一个到达的分组
void insertevent(struct event *p);//向事件列表中插入一条新的事件
void printevlist();//打印事件列表
//********************************************************************
//**********************计时器模块***********************************
void stoptimer(int);//停止计时器
void starttimer(int,float);//启动计时器
//*********************************************************************
//**************************网络各层之间传送模块***********************
void tolayer3(int AorB,struct pkt packet);//向第3层发送信息
void tolayer5(int AorB,char datasent[20]);//向第5层发送信息
二:
#include "GBN.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
extern int TRACE = 1; /* 用于跟踪*/
extern int nsim = 0; /* 当前第5层到第4层的信息数*/
extern int nsimmax = 0; /* "msg"生成数量,直到停止*/
extern float time = 0.000;
float lossprob; /* 丢包可能性*/
float corruptprob; /* 包的数据位数丢失可能性*/
float lambda; /* 从第五层到达的比率 */
int ntolayer3; /* 送往第3层的包数量*/
static int nlost = 0; /* 在媒体中丢失的包数量*/
static int ncorrupt = 0; /* 在网络中被破坏的包*/
static int expectedseqnum = 0; /* 期待接收方发送的序号 */
static int nextseqnum; /* 下一次发送方要使用的包序号*/
static int base; /* 发送方头窗*/
struct pkt winbuf[WINDOWSIZE]; /* 窗口包缓冲区*/
static int winfront,winrear; /* 缓冲区的头尾指针*/
static int pktnum; /* 缓冲窗口包的数量*/
struct msg buffer[MAXBUFSIZE]; /* 发送方数据缓冲区*/
int buffront,bufrear; /* 缓冲区的头尾指针 */
static int msgnum; /* 缓冲窗口包的数量 */
//关于各种包的类型定义
//***********************************************************
int packet_lost =0; //包丢失
int packet_corrupt=0; //包破坏
int packet_sent =0; //包发送
extern int packet_correct=0;//正确包
extern int packet_resent =0;//重发包
int packet_timeout=0;//超时包
extern struct event *evlist = NULL; /* 事件清单 */
//相关接口函数的实现
//计算校验和,即脚链和1的补码和
void ComputeChecksum( struct pkt *packet)
{
int checksum;
int i;
checksum = packet->seqnum;
checksum = checksum + packet->acknum;//求脚链
for ( i=0; i<20; i++ ) //取最高位,按位相加
checksum = checksum + (int)(packet->payload[i]);
checksum = 0-checksum;//取反
packet->checksum = checksum;
}
//检查是否出错
int CheckCorrupted(struct pkt packet)
{
int checksum;
int i;
checksum = packet.seqnum;
checksum = checksum + packet.acknum;
for ( i=0; i<20; i++ )
checksum = checksum + (int)(packet.payload[i]);
if ( (packet.checksum+checksum) == 0 )//检查校检和是否相加为0
return (FALSE);
else
return (TRUE);
}
//A端向外发送数据
/* 被第五层调用,向另一端发送数据*/
void A_output(struct msg message)
{
int i;
struct pkt sendpkt;
/* 如果的发送方下次使用序号小于滑动窗口的最大序号,那么补发包*/
if ( nextseqnum < base+WINDOWSIZE )
{
printf("----A:新的包到达,发送窗口不满,新的包送往第3层!\n");
/* 创建包 */
sendpkt.seqnum = nextseqnum;
sendpkt.acknum = NOTUSED;
for ( i=0; i<20 ; i++ )
sendpkt.payload[i] = message.data[i];
/* 检查是否出错 */
ComputeChecksum (&sendpkt);
/* 送出包 */
tolayer3 (A, sendpkt);
/* 复制这些包到窗口缓冲区 */
winrear = (winrear+1)%WINDOWSIZE;
pktnum ++;
winbuf[winrear] = sendpkt;
for (i=0; i<20; i++)
winbuf[winrear].payload[i]= sendpkt.payload[i];
/* 更新状态变量 */
nextseqnum = nextseqnum+1;
starttimer(A,RTT);
B_input(sendpkt);
A_input(sendpkt);
}
/* 如果移动窗口包满*/
else
{
printf("----A:新的包到达,发送窗口已满!");
/* 如果缓冲区满,放弃并退出*/
if ( msgnum == MAXBUFSIZE)
{
printf (" 错误:发送缓冲区已满 !!!\n");
exit (1);
}
/* 否则,将信息送入缓冲区 */
else
{
printf("信息已送入缓冲区\n");
bufrear = (bufrear+1) % MAXBUFSIZE; //拥塞控制,防止拥塞,减小缓冲区
for (i=0; i<20; i++)
buffer[bufrear].data[i] = message.data[i];
msgnum ++;
}
}
}
//B端向外发送数据
/* 被第五层调用,发送数据到另一方,同A方相似 */
void B_output(struct msg message)
{
int i;
struct pkt sendpkt;
/* 如果发送窗口不满*/
if ( nextseqnum < base+WINDOWSIZE )
{
printf("----B:新的包到达,发送窗口不满,新的包送往第3层!!\n");
/* create packet */
sendpkt.seqnum = nextseqnum;
sendpkt.acknum = NOTUSED;
for ( i=0; i<20 ; i++ )
sendpkt.payload[i] = message.data[i];
/* computer checksum */
ComputeChecksum (&sendpkt);
/* send out packet */
tolayer3 (A, sendpkt);
A_input(sendpkt);
/* copy the packet to window packet buffer */
winrear = (winrear+1)%WINDOWSIZE;
pktnum ++;
winbuf[winrear] = sendpkt;
for (i=0; i<20; i++)
winbuf[winrear].payload[i]= sendpkt.payload[i];
/* if it is the first packet in window, start timeout */
//if ( base == nextseqnum )
//{
//starttimer(A,RTT);
//printf("----A: start a new timer!\n");
// }
/* update state variables */
nextseqnum = nextseqnum+1;
}
/* 如果发送窗口满*/
else
{
printf("----B:新的包到达,发送窗口已满!,");
/* if buffer full, give up and exit*/
if ( msgnum == MAXBUFSIZE)
{
printf (" 错误:发送缓冲区已满 ! \n");
exit (1);
}
/* otherwise, buffer the message */
else
{
printf("缓冲区满!\n");
bufrear = (bufrear+1) % MAXBUFSIZE;
for (i=0; i<20; i++)
buffer[bufrear].data[i] = message.data[i];
msgnum ++;
}
}
}
//A端接收数据
void A_input(struct pkt packet)
{
struct pkt sendpkt;
int i;
/* 如果收到没有被破坏的包并且确认包收到*/
if ( (CheckCorrupted(packet) == FALSE) && (packet.acknum != NACK) )
{
printf("----A: ACK(确认包) %d 被正确的收到,",packet.acknum);
packet_correct++;
/* 从窗口缓冲区删去被确认的包*/
winfront = (winfront+(packet.acknum+1-base)) % WINDOWSIZE;
pktnum = pktnum - (packet.acknum+1-base);
/* 改变窗口最小号确认包 */
base = packet.acknum+1;
stoptimer(A);
if ( base < nextseqnum)
{
//starttimer(A,RTT);
printf ("\n\n\n发送新的包!");
}
/* 如果缓冲区不空发送新的包*/
while ( (msgnum!=0) && (nextseqnum<base+WINDOWSIZE) )
{
/* 创建包*/
sendpkt.seqnum = nextseqnum;
sendpkt.acknum = NOTUSED;
buffront = (buffront+1) % MAXBUFSIZE;
for ( i=0; i<20 ; i++ )
sendpkt.payload[i] = buffer[buffront].data[i];
/* 和确认*/
ComputeChecksum (&sendpkt);
/*如果是滑动窗口的第一个包,开始超时检测*/
if ( base == nextseqnum ) //如果滑动窗口最小的包等于下一次要发送的序号
{
//starttimer(A,RTT);
printf ("发送新的包!\n");
}
/* 送出包 */
tolayer3 (A, sendpkt);
/* 复制包到窗口缓冲区 */
winrear = (winrear+1)%WINDOWSIZE;
winbuf[winrear] = sendpkt;
pktnum ++;
/* 更新状态变量 */
nextseqnum = nextseqnum+1;
/* 删除缓冲区的包 */
msgnum --;
}
}
else
printf ("----A: 收到否定应答,什么也没有完成!\n");
}
//B端接收数据*****************************************************一定要调用这个
/* 这只是单一的A到B的传送方式,没有B端的数据发出 */
/* 被第三层调用,但数据到达B端第4层*/
void B_input(struct pkt packet)
{
struct pkt sendpkt;
int i;
/*如果收到没有被破坏的包并且确认包收到 */
if ( (CheckCorrupted(packet) == FALSE) && (packet.seqnum == expectedseqnum))
{
printf("\n----B: 包正确的到达,发送ACK确认包\n",packet.seqnum);
/* 发送应答包 */
/* create packet */
sendpkt.seqnum = NOTUSED;
sendpkt.acknum = expectedseqnum;
for ( i=0; i<20 ; i++ )
sendpkt.payload[i] = '0';
/* computer checksum */
ComputeChecksum (&sendpkt);
/* send out packet */
//tolayer3 (B, sendpkt);
/* update state variables */
expectedseqnum = expectedseqnum+1;
printf("----B:expectedseqnum (期待下一次的发送序号)= %d\n",expectedseqnum);
/* 发送包到第五层 */
//tolayer5(B,packet.payload);
}
/* 否则,抛弃包,发送否定应答*/
else
{
printf("----B: 包 %d 损坏或者不是我所需要的, 发送否定应答!\n",packet.seqnum);
/* 创建包 */
sendpkt.seqnum = NOTUSED;
sendpkt.acknum = NACK;
for ( i=0; i<20 ; i++ )
sendpkt.payload[i] = '0';
/* 校检和检查 */
ComputeChecksum (&sendpkt);
/* 发出包 */
tolayer3 (B, sendpkt);
}
}
//A计时器超时
/* A超时被调用 */
void A_timerinterrupt()
{
int i;
printf("----A:超时!!!重新发包!\n");
/* start timer */
starttimer(A,RTT);
/* 重新发送所有未被确认的包*/
for ( i=1; i<=pktnum; i++ )
{
packet_resent++;
tolayer3(A,winbuf[(winfront+i)%WINDOWSIZE]);
}
}
//B计时器超时
/* called when B's timer goes off */
void B_timerinterrupt()
{
int i;
printf("----B: 超时!!!重新发包!\n");
/* start timer */
starttimer(B,RTT);
/* 重新发送所有未被确认的包 */
for ( i=1; i<=pktnum; i++ )
{
packet_resent++;
tolayer3(B,winbuf[(winfront+i)%WINDOWSIZE]);
}
}
//A端初始化
/* entity A routines are called. You can use it to do any initialization */
void A_init()
{
base = 0;
nextseqnum = 0;
buffront = 0;
bufrear = 0;
msgnum = 0;
winfront = 0;
winrear = 0;
pktnum = 0;
}
//B端初始化
/* entity B routines are called. You can use it to do any initialization */
void B_init()
{
expectedseqnum = 0;
}
//初始化仿真器
void init() /* initialize the simulator */
{
int i;
float sum, avg;
float jimsrand();
FILE *fp;
fp = fopen ("parameter.txt","r");
printf("----- Stop and Wait Network Simulator Version 1.1 -------- \n\n");
printf(" -----停止等待网络模拟器版本1.1 -------- \n\n");
printf("回车输入包的个数给虚拟器: ");
//fscanf(fp,"%d",&nsimmax);
scanf("%d",&nsimmax);
printf("\n输入包被丢失的可能性[输入 0.0 表示不丢失]: ");
//fscanf(fp, "%f",&lossprob);
scanf("%f",&lossprob);
printf("\n输入包被损坏的可能性[输入 0.0 表示不损坏]:");
//fscanf(fp,"%f",&corruptprob);
scanf("%f",&corruptprob);
printf("\n输入包从发送方第五层到达的平均时间[ > 0.0]: ");
//fscanf(fp,"%f",&lambda);
scanf("%f",&lambda);
printf("\n输入跟踪: ");
//fscanf(f
展开阅读全文