文章目录
前引
各位好 我写到这里其实我也还是挺意外的 因为其实我是想在CS144
中再手撕TCP
的 但是没想到《计算机自顶向下》里面居然有这个小实验 那既然有 哪里有不做的道理 反正CS144
的实验也是迟早要完成的
我的预计本来是把Socket编程+Wireshark实验+CS144
完成了 配套着《计算机自顶向下》+之前快速看完的哈工大课程 我们就愉快的完成计算机网络的学习 哈哈哈 但是其实发现要完成的还是相当多的
Lab1 实现可靠的传输协议
Lab1 文档查阅(友情提供下载链接)
方便大家下载相关文档 下面是查阅链接
Programming Assignment 5: Implementing a Reliable Transport Protocol
prog2.c代码
下面是查阅的文档截图 详细的大家还是点进去仔细看看吧 这里我用的是谷歌的自带翻译 所以查阅起来比较方便阅读
我们大概要完成的任务就是下图中的从A->B的信息传递 只需要完成单向的 哈哈 不是说单项的B就不向A传递信息了 B如果不向A握手那还怎么连接呢
文档中指示了我们需要完成下面五个函数
A_output(message)
,其中message是一个msg类型的结构体 ,包含要发送到B端的数据。每当发送端 (A) 的上层有消息要发送时,就会调用此例程。您的协议的工作是确保此类消息中的数据按顺序正确传递到接收方上层。
A_input(packet)
,其中packet是pkt类型的结构体 。每当从 B 端发送的数据包(即,作为B 端过程执行的tolayer3()的结果)到达 A 端时,将调用该例程。数据包是从 B 端发送的(可能已损坏的)数据包。
A_timerinterrupt()
当 A 的定时器超时(从而产生定时器中断)时,将调用该例程。您可能希望使用此例程来控制数据包的重新传输。请参阅下面的starttimer() 和stoptimer()以了解计时器的启动和停止方式。
A_init()
在调用任何其他 A 端例程之前,该例程将被调用一次。它可用于执行任何所需的初始化。
B_input(packet)
,其中packet是pkt类型的结构体 。每当从 A 端发送的数据包(即,作为A 端过程执行的tolayer3()的结果)到达 B 端时,将调用该例程。数据包是从 A 端发送的(可能已损坏的)数据包。
B_init()
在调用任何其他 B 端例程之前,该例程将被调用一次。它可用于执行任何所需的初始化。
查看c语言参数声明的说明
我刚刚在查看官方文档的时候 看到了下面的c语言参数代码让我百思不得其解 因为我实在没看到过这样子的声明
这个是什么啊 我是真的这样想的 第一个既没有返回值 参数还是这样设置的 第二个是int AorB
char datasent[20]
tolayer5(AorB,datasent)
int AorB;
char datasent[20];
{
int i;
if (TRACE>2) {
printf(" TOLAYER5: data received: ");
for (i=0; i<20; i++)
printf("%c",datasent[i]);
printf("\n");
}
}
之后我试了一下 函数如下 如果没有返回值的话 默认为return 0
#include <stdio.h>
f(a)
int a;
{
printf("nono\n");
}
main()
{
int a = f(a);
printf("goog %d\n",a);
return;
}
原模版代码(个人整理格式后)
尽管不得不说 这个网络模拟器和调试的地方 确实提供模板的老师写的很好 但是不得不说 确实太乱了 - - 乱到很多时候 我读代码都不忍直视 读都读不下去 实在是受不了了
于是在大概花了个5-6分钟左右 我把格式做了优化 看起来也整洁很多
#include <stdio.h>
/* ******************************************************************
ALTERNATING BIT AND GO-BACK-N NETWORK EMULATOR: VERSION 1.1 J.F.Kurose
This code should be used for PA2, unidirectional or bidirectional
data transfer protocols (from A to B. Bidirectional transfer of data
is for extra credit and is not required). Network properties:
- one way network delay averages five time units (longer if there
are other messages in the channel for GBN), but can be larger
- packets can be corrupted (either the header or the data portion)
or lost, according to user-defined probabilities
- packets will be delivered in the order in which they were sent
(although some can be lost).
**********************************************************************/
#define BIDIRECTIONAL 0 /* change to 1 if you're doing extra credit */
/* and write a routine called B_output */
/* a "msg" is the data unit passed from layer 5 (teachers code) to layer */
/* 4 (students' code). It contains the data (characters) to be delivered */
/* to layer 5 via the students transport level protocol entities. */
struct msg
{
char data[20];
};
/* a packet is the data unit passed from layer 4 (students code) to layer */
/* 3 (teachers code). Note the pre-defined packet structure, which all */
/* students must follow. */
struct pkt
{
int seqnum;
int acknum;
int checksum;
char payload[20];
};
/********* STUDENTS WRITE THE NEXT SEVEN ROUTINES *********/
#define send_cnts_MAX 3
#define send_time_MAX 6
struct msg A_msg;
int A_sendcnts;
float A_sendtime;
/* called from layer 5, passed the data to be sent to other side */
A_output(message)
struct msg message;
{
starttimerr(A,A_sendtime);
}
B_output(message) /* need be completed only for extra credit */
struct msg message;
{
}
/* called from layer 3, when a packet arrives for layer 4 */
A_input(packet)
struct pkt packet;
{
}
/* called when A's timer goes off */
A_timerinterrupt()
{
}
/* the following routine will be called once (only) before any other */
/* entity A routines are called. You can use it to do any initialization */
A_init()
{
int i;
for(i=0;i<20;++i)
A_msg[i] = '\0';
A_sendcnts = 0;
A_sendtime = 0;
}
/* Note that with simplex transfer from a-to-B, there is no B_output() */
/* called from layer 3, when a packet arrives for layer 4 at B*/
B_input(packet)
struct pkt packet;
{
}
/* called when B's timer goes off */
B_timerinterrupt()
{
}
/* the following rouytine will be called once (only) before any other */
/* entity B routines are called. You can use it to do any initialization */
B_init()
{
}
/*****************************************************************
***************** NETWORK EMULATION CODE STARTS BELOW ***********
The code below emulates the layer 3 and below network environment:
- emulates the tranmission and delivery (possibly with bit-level corruption
and packet loss) of packets across the layer 3/4 interface
- handles the starting/stopping of a timer, and generates timer
interrupts (resulting in calling students timer handler).
- generates message to be sent (passed from later 5 to 4)
THERE IS NOT REASON THAT ANY STUDENT SHOULD HAVE TO READ OR UNDERSTAND
THE CODE BELOW. YOU SHOLD NOT TOUCH, OR REFERENCE (in your code) ANY
OF THE DATA STRUCTURES BELOW. If you're interested in how I designed
the emulator, you're welcome to look at the code - but again, you should have
to, and you defeinitely should not have to modify
******************************************************************/
struct event {
float evtime; /* event time */
int evtype; /* event type code */
int eventity; /* entity where event occurs */
struct pkt *pktptr; /* ptr to packet (if any) assoc w/ this event */
struct event *prev;
struct event *next;
};
struct event *evlist = NULL; /* the event list */
/* possible events: */
int TRACE = 1; /* for my debugging */
int nsim = 0; /* number of messages from 5 to 4 so far */
int nsimmax = 0; /* number of msgs to generate, then stop */
float time = 0.000;
float lossprob; /* probability that a packet is dropped */
float corruptprob; /* probability that one bit is packet is flipped */
float lambda; /* arrival rate of messages from layer 5 */
int ntolayer3; /* number sent into layer 3 */
int nlost; /* number lost in media */
int ncorrupt; /* number corrupted by media*/
main()
{
struct event *eventptr;
struct msg msg2give;
struct pkt pkt2give;
int i,j;
char c;
init();
A_init();
B_init();
while (1)
{
eventptr = evlist; /* get next event to simulate */
if (eventptr==NULL)
goto terminate;
evlist = evlist->next; /* remove this event from event list */
if (evlist!=NULL)
evlist->prev=NULL;
if (TRACE>=2)
{
printf("\nEVENT time: %f,",eventptr->evtime);
printf(" type: %d",eventptr->evtype);
if (eventptr->evtype==0)
printf(", timerinterrupt ");
else if (eventptr->evtype==1)
printf(", fromlayer5 ");
else
printf(", fromlayer3 ");
printf(" entity: %d\n",eventptr->eventity);
}
time = eventptr->evtime; /* update time to next event time */
if (nsim==nsimmax)
break; /* all done with simulation */
if (eventptr->evtype == FROM_LAYER5)
{
generate_next_arrival(); /* set up future arrival */
/* fill in msg to give with string of same letter */
j = nsim % 26;
for (i=0; i<20; i++)
msg2give.data[i] = 97 + j;
if (TRACE>2)
{
printf(" MAINLOOP: data given to student: ");
for (i=0; i<20; i++)
printf("%c", msg2give.data[i]);
printf("\n");
}
nsim++;
if (eventptr->eventity == A)
A_output(msg2give);
else
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) /* deliver packet by calling */
A_input(pkt2give); /* appropriate entity */
else
B_input(pkt2give);
free(eventptr->pktptr); /* free the memory for packet */
}
else if (eventptr->evtype == TIMER_INTERRUPT)
{
if (eventptr->eventity == A)
A_timerinterrupt();
else
B_timerinterrupt();
}
else printf("INTERNAL PANIC: unknown event type \n");
free(eventptr);
}
terminate:
printf(" Simulator terminated at time %f\n after sending %d msgs from layer5\n",time,nsim);
}
init() /* initialize the simulator */
{
int i;
float sum, avg;
float jimsrand();
printf("----- Stop and Wait Network Simulator Version 1.1 -------- \n\n");
printf("Enter the number of messages to simulate: ");
scanf("%d",&nsimmax); // 发送数据包数目
printf("Enter packet loss probability [enter 0.0 for no loss]:");
scanf("%f",&lossprob); // 丢包率
printf("Enter packet corruption probability [0.0 for no corruption]:");
scanf("%f",&corruptprob); // 包损坏率
printf("Enter average time between messages from sender's layer5 [ > 0.0]:");
scanf("%f",&lambda); // 平均物理层传输时间
printf("Enter TRACE:");
scanf("%d",&TRACE); // 追踪 用于调试的
srand(9999); /* init random number generator */
sum = 0.0; /* test random number generator for students */
for (i=0; i<1000; i++)
sum=sum+jimsrand(); /* jimsrand() should be uniform in [0,1] */
avg = sum/1000.0; // 随机概率 估计值在0.25 ~ 0.75
if (avg < 0.25 || avg > 0.75)
{
printf("It is likely that random number generation on your machine\n" );
printf("is different from what this emulator expects. Please take\n");
printf("a look at the routine jimsrand() in the emulator code. Sorry. \n");
exit(0);
}
ntolayer3 = 0;
nlost = 0;
ncorrupt = 0;
time=0.0; /* initialize time to 0.0 */
generate_next_arrival(); /* initialize event list */
}
/****************************************************************************/
/* jimsrand(): return a float in range [0,1]. The routine below is used to */
/* isolate all random number generation in one location. We assume that the*/
/* system-supplied rand() function return an int in therange [0,mmm] */
/****************************************************************************/
float jimsrand()
{
double mmm = 2147483647; /* largest int - MACHINE DEPENDENT!!!!!!!! */
float x; /* individual students may need to change mmm */
x = rand()/mmm; /* x should be uniform in [0,1] */
return(x);
}
/********************* EVENT HANDLINE ROUTINES *******/
/* The next set of routines handle the event list */
/*****************************************************/
generate_next_arrival()
{
double x,log(),ceil();
struct event *evptr;
char *malloc();
float ttime;
int tempint;
if (TRACE>2)
printf(" GENERATE NEXT ARRIVAL: creating new arrival\n");
x = lambda*jimsrand()*2; /* x is uniform on [0,2*lambda] */
/* having mean of lambda */
evptr = (struct event *)malloc(sizeof(struct event));
evptr->evtime = time + x;
evptr->evtype = FROM_LAYER5;
if (BIDIRECTIONAL && (jimsrand()>0.5) )
evptr->eventity = B;
else
evptr->eventity = A;
insertevent(evptr);
}
// 由之前生成的包中的eventtime决定 传输顺序 在队列中的什么位置
insertevent(p)
struct event *p;
{
struct event *q,*qold;
if (TRACE>2)
{
printf(" INSERTEVENT: time is %lf\n",time);
printf(" INSERTEVENT: future time will be %lf\n",p->evtime);
}
q = evlist; /* q points to header of list in which p struct inserted */
if (q==NULL) /* list is empty */
{
evlist=p;
p->next=NULL;
p->prev=NULL;
}
else
{
for (qold = q; q !=NULL && p->evtime > q->evtime; q=q->next)
qold=q;
if (q==NULL) /* end of list */
{
qold->next = p;
p->prev = qold;
p->next = NULL;
}
else if (q==evlist) /* front of list */
{
p->next=evlist;
p->prev=NULL;
p->next->prev=p;
evlist = p;
}
else /* middle of list */
{
p->next=q;
p->prev=q->prev;
q->prev->next=p;
q->prev=p;
}
}
}
printevlist()
{
struct event *q;
int i;
printf("--------------\nEvent List Follows:\n");
for(q = evlist; q!=NULL; q=q->next)
printf("Event time: %f, type: %d entity: %d\n",q->evtime,q->evtype,q->eventity);
printf("--------------\n");
}
/********************** Student-callable ROUTINES ***********************/
/* called by students routine to cancel a previously-started timer */
stoptimer(AorB)
int AorB; /* A or B is trying to stop timer */
{
struct event *q,*qold;
if (TRACE>2)
printf(" STOP TIMER: stopping timer at %f\n",time);
/* for (q=evlist; q!=NULL && q->next!=NULL; q = q->next) */
for (q=evlist; q!=NULL ; q = q->next)
{
/* remove this event */
if ( (q->evtype==TIMER_INTERRUPT && q->eventity==AorB) )
{
if (q->next==NULL && q->prev==NULL)
evlist=NULL; /* remove first and only event on list */
else if (q->next==NULL) /* end of list - there is one in front */
q->prev->next = NULL;
else if (q==evlist) /* front of list - there must be event after */
{
q->next->prev=NULL;
evlist = q->next;
}
else /* middle of list */
{
q->next->prev = q->prev;
q->prev->next = q->next;
}
free(q);
return;
}
}
printf("Warning: unable to cancel your timer. It wasn't running.\n");
}
starttimer(AorB,increment)
int AorB; /* A or B is trying to stop timer */
float increment;
{
struct event *q;
struct event *evptr;
char *malloc();
if (TRACE>2)
printf(" START TIMER: starting timer at %f\n",time);
/* be nice: check to see if timer is already started, if so, then warn */
/* for (q=evlist; q!=NULL && q->next!=NULL; q = q->next) */
for (q=evlist; q!=NULL ; q = q->next)
{
if (q->evtype==TIMER_INTERRUPT && q->eventity==AorB )
{
printf("Warning: attempt to start a timer that is already started\n");
return;
}
}
/* create future event for when timer goes off */
evptr = (struct event *)malloc(sizeof(struct event));
evptr->evtime = time + increment;
evptr->evtype = TIMER_INTERRUPT;
evptr->eventity = AorB;
insertevent(evptr);
}
/************************** TOLAYER3 ***************/
tolayer3(AorB,packet)
int AorB; /* A or B is trying to stop timer */
struct pkt packet;
{
struct pkt *mypktptr;
struct event *evptr,*q;
char *malloc();
float lastime, x, jimsrand();
int i;
ntolayer3++;
/* simulate losses: */
if (jimsrand() < lossprob)
{
nlost++;
if (TRACE>0)
printf(" TOLAYER3: packet being lost\n");
return;
}
/* make a copy of the packet student just gave me since he/she may decide */
/* to do something with the packet after we return back to him/her */
mypktptr = (struct pkt *)malloc(sizeof(struct pkt));
mypktptr->seqnum = packet.seqnum;
mypktptr->acknum = packet.acknum;
mypktptr->checksum = packet.checksum;
for (i=0; i<20; i++)
mypktptr->payload[i] = packet.payload[i];
if (TRACE>2)
{
printf(" TOLAYER3: seq: %d, ack %d, check: %d ", mypktptr->seqnum,
mypktptr->acknum, mypktptr->checksum);
for (i=0; i<20; i++)
printf("%c",mypktptr->payload[i]);
printf("\n");
}
/* create future event for arrival of packet at the other side */
evptr = (struct event *)malloc(sizeof(struct event));
evptr->evtype = FROM_LAYER3; /* packet will pop out from layer3 */
evptr->eventity = (AorB+1) % 2; /* event occurs at other entity */
evptr->pktptr = mypktptr; /* save ptr to my copy of packet */
/* finally, compute the arrival time of packet at the other end.
medium can not reorder, so make sure packet arrives between 1 and 10
time units after the latest arrival time of packets
currently in the medium on their way to the destination */
lastime = time;
/* for (q=evlist; q!=NULL && q->next!=NULL; q = q->next) */
for (q=evlist; q!=NULL ; q = q->next)
{
if ( (q->evtype==FROM_LAYER3 && q->eventity==evptr->eventity) )
lastime = q->evtime;
}
evptr->evtime = lastime + 1 + 9*jimsrand();
/* simulate corruption: */
if (jimsrand() < corruptprob)
{
ncorrupt++;
if ( (x = jimsrand()) < .75)
mypktptr->payload[0]='Z'; /* corrupt payload */
else if (x < .875)
mypktptr->seqnum = 999999;
else
mypktptr->acknum = 999999;
if (TRACE>0)
printf(" TOLAYER3: packet being corrupted\n");
}
if (TRACE>2)
printf(" TOLAYER3: scheduling arrival on other side\n");
insertevent(evptr);
}
tolayer5(AorB,datasent)
int AorB;
char datasent[20];
{
int i;
if (TRACE>2)
{
printf(" TOLAYER5: data received: ");
for (i=0; i<20; i++)
printf("%c",datasent[i]);
printf("\n");
}
}
原模板代码(稍加分析)
有什么说什么 做这个Lab的老师写的代码也太不规范了吧
不知道是不是年代很久以前? 还是怎么样 我光理解很多地方都理解了很久 - - 真的很看不懂一些地方的赋值 初始化 命名方式
真的很想改一些地方的 但是源于感谢这个Lab老师编写的代码 还有老师在代码的注释行专门强调了 不允许修改下面的代码 既然这样 源于尊重和理解 我们就不改了
但是最后还是改了 因为一些需求
首先我想用累积确认 还有仅用ACK
确认最后一次已经收到的包 这样的话就可以不用ACK和NAK了
我是想先把交替位协议版本
给做了 再做有流水线的GBN滑动窗口
反正刚开始它让我们要用NAK 和 ACK
我就用了 之后的话 对于GBN
我再改吧
对了 这里我实现的仅实现了单边的交流 没有实现双边的- - 能力有限 对于双边的或者更好的 就留在CS144
那里来弄吧
对于刚开始拿到这个代码的hxd来说 确实挺难的 因为各个地方都没有思路 这里的话 我对每个代码块做一个小说明吧 因为基本上代码我都是看完了再来写的
A_output(message)
A的应用层到传输层 Layer5->Layer3
协议包装msg
包装成数据包packet
A_input(packet)
A的传输层处理数据包 对于坏包或者检测错误 用于处理 不需要回传给应用层
A_timerinterrupt()
A的计时器 如果超时的话 则进入A_timerinterrupt
处理 进入函数处理由一个队列(老师自己做的模拟器 后面会写)自动处理
A_init()
最开始的初始化 我初始化的很少 处理了一些全局变量
B_init()
最开始的初始化B 对于交替位处理 我基本没怎么做处理
B_input(packet)
B的传输层处理数据包 负责给A传输ACK
那些的 如果包数据正确 即向第五层应用层发送
说了一下我们需要编写的五个函数
如果对于双方都有交互的话 其实对于B的超时器还是得弄一个 但是我们现在先做单边的 交替位实验 就省了
我先说一下这个实验的网络模拟器的大致怎么回事
光看我这个文字是对整个Lab没有一个大致的了解的 我们得一步步看主函数里面的流程 先是初始化各种参数 参数包括 总共发多少条信息
丢包率
坏包率
两条信息预计发送的间隔
调试信息等级
先说一下 两条信息预计发送间隔
是怎么回事 我们的信息发送是根据一个evlist
来的 对于超时器 其实也是一个event
他是根据任务发生时间来排序的 这里再提一嘴 我们的消息发送过去一般是5的时间单位 两条信息时间间隔是 我们的evlist
自动循环的 也就是如果设置为20 那我们的信息也就是自动平均20往外发送一条新的
而不是受我们控制 当然如果是对于重传的那些 当然是我们控制 例如 我们此刻为10
的时间单位 要发送的信息5
秒后到达 那就是预计15
秒后到 那我们的发送任务就插入任务队列 根据时间排序 如果我们的定时器就在发送的时候启动 我们自己设置多少秒后停止定时器 启动A_timerinterrupt
比如12
秒 因为来回大概是5*2=10
秒 定时器就按照任务终止时间插入到任务队列进去了
上面写了很多 但原理就是这样 对于丢包
包损坏
这些都是随机数产生的 详情请见tolayer3
是怎么控制 丢包和包损坏的
然后就是自己填了 刚开始调试的时候可以填写10 0 0 100 3
看看信息seq ack
是否正确 然后再坏包 看看自己的重传是否成功了 把这些做了 大概就对于交替位协议版本有个认识了
交替位协议版本 代码实现
这个Lab分两篇博客来发 因为这篇博客已经快3w字了 当然是包括代码的字数 编辑字的时候开始有点点卡顿了 - - 所以分两篇来吧 这样看起来也更好看一点
这里贴了我实现的GBN
的博客链接
实现可靠的传输协议(下)(GBN版本)
这里写一下我的交替位协议版本代码
实现思路
首先是A发送给B包 则即刻开始计时器 发送过去文档中说了 一般都是5个时间单位 则我们代码设置的超时时间为12个时间单位
由于我们的信息发送是由evlist
自动控制的 如果两个信息发送时间间隔太短的话 就不存在交替的情况了 如果忽略 不发那条信息的话 也不好 所以我们就设置长一点 按照文档推荐的1000
来设置 意思就是发送完第一条消息后 1000
秒后再发第二条 中间有充足的时间重传 ACK
确认
我们的交替位思路如下
A->B
即刻打开超时器
B->A
如果checksum
不相等则将ack
位置为0 相等的话 ack
位置为1 acknum = A_packet.seq+1
表示确认
A收到消息有下面几种情况
第一个就是发送出去丢包 那就是超时器到了时间自己重发 第二个就是接受到的ack位为0 即nak
或者 checksum
错了 立即关闭之前的超时器 并重新设置超时器 立即重发 第三个就是真的由于延迟 晚到了 还是会被超时器重发
A如果正确收到消息后 我们即关闭超时器 停止即可 等到evlist 下一次的自动发送
但是考虑下面这种情况 如果A->B
成功发送且无错 仅因为延迟问题晚到了 后超时器又重发消息 之后连续两个ack
我们对于定时器我们每次正确收到消息的时候就自动关闭之前所有的超时器 那么第二次ack
就没有超时器关闭
对于这个情况 我们的程序 发送printf("Warning: unable to cancel your timer. It wasn't running.\n");
哈哈 其实没有超时器关就没有呗 反正都ack
了 关都关完了 该关的也关了 也就不管了 对程序也没有影响
好了 就说那么多 有点困了 - - 下面直接放代码 大家凑合着看
睡醒了忽然忘了有点东西忘了说了 模板中的一些代码我也还是改了部分的 比如我的数据包里面多了个ack
有些地方根据个人需求来改 毕竟程序是自己跑的嘛 哈哈哈 去吃晚饭了!
#include <stdio.h>
/* ******************************************************************
ALTERNATING BIT AND GO-BACK-N NETWORK EMULATOR: VERSION 1.1 J.F.Kurose
This code should be used for PA2, unidirectional or bidirectional
data transfer protocols (from A to B. Bidirectional transfer of data
is for extra credit and is not required). Network properties:
- one way network delay averages five time units (longer if there
are other messages in the channel for GBN), but can be larger
- packets can be corrupted (either the header or the data portion)
or lost, according to user-defined probabilities
- packets will be delivered in the order in which they were sent
(although some can be lost).
**********************************************************************/
#define BIDIRECTIONAL 0 /* change to 1 if you're doing extra credit */
/* and write a routine called B_output */
#define TIMER_INTERRUPT 0
#define FROM_LAYER5 1
#define FROM_LAYER3 2
#define OFF 0
#define ON 1
#define A 0
#define B 1
typedef int bool;
#define true 1
#define false 0
/* a "msg" is the data unit passed from layer 5 (teachers code) to layer */
/* 4 (students' code). It contains the data (characters) to be delivered */
/* to layer 5 via the students transport level protocol entities. */
struct msg
{
char data[20];
};
/* a packet is the data unit passed from layer 4 (students code) to layer */
/* 3 (teachers code). Note the pre-defined packet structure, which all */
/* students must follow. */
struct pkt
{
int seqnum;
int acknum;
int checksum;
int ack;
char payload[20];
};
/********* STUDENTS WRITE THE NEXT SEVEN ROUTINES *********/
const float send_time_MAX = 12.0;
struct pkt A_packet;
int A_sendseq;
int B_acknum;
/* called from layer 5, passed the data to be sent to other side */
A_output(message)
struct msg message;
{
int i,checksum = 0;
for(i=0;i<20;++i)
{
checksum += message.data[i];
A_packet.payload[i] = message.data[i];
}
A_packet.seqnum = A_sendseq;
A_packet.acknum = 0;
A_packet.ack = 1;
A_packet.checksum = checksum + A_packet.seqnum + A_packet.acknum + A_packet.ack;
starttimer(A,send_time_MAX);
tolayer3(A,A_packet);
}
/* called from layer 3, when a packet arrives for layer 4 */
A_input(packet)
struct pkt packet;
{
int checksum = 0,i;
for(i=0;i<20;++i)
checksum += packet.payload[i];
checksum += (packet.ack + packet.acknum + packet.seqnum);
if(checksum == packet.checksum && packet.ack)
{
A_sendseq += (packet.acknum - A_sendseq);
stoptimer(A);
}
else if(!packet.ack || packet.acknum != A_sendseq + 1 || checksum != packet.checksum)
{
stoptimer(A);
starttimer(A,send_time_MAX);
tolayer3(A,A_packet);
}
}
/* called when A's timer goes off */
A_timerinterrupt()
{
starttimer(A,send_time_MAX);
tolayer3(A,A_packet);
}
/* the following routine will be called once (only) before any other */
/* entity A routines are called. You can use it to do any initialization */
A_init()
{
A_sendseq = 0;
}
/* the following rouytine will be called once (only) before any other */
/* entity B routines are called. You can use it to do any initialization */
B_init()
{
B_acknum = -1;
}
/* Note that with simplex transfer from a-to-B, there is no B_output() */
/* called from layer 3, when a packet arrives for layer 4 at B*/
B_input(packet)
struct pkt packet;
{
int i,checksum = 0;
for(i=0;i<20;++i)
checksum += packet.payload[i];
checksum += (packet.seqnum + packet.acknum + packet.ack);
struct pkt* p = (struct pkt*)malloc(sizeof(struct pkt));
struct pkt send_packet = *p;
for(i=0;i<20;++i)
send_packet.payload[i] = '\0';
if(checksum == packet.checksum)
{
if(B_acknum < packet.seqnum+1) tolayer5(B,packet.payload);
send_packet.seqnum = 1;
send_packet.acknum = packet.seqnum + 1;
send_packet.ack = 1;
B_acknum = send_packet.acknum;
send_packet.checksum = send_packet.seqnum + send_packet.acknum + send_packet.ack;
}
else
{
send_packet.seqnum = 1;
send_packet.acknum = packet.seqnum + 1;
send_packet.ack = 0;
send_packet.checksum = send_packet.seqnum + send_packet.acknum + send_packet.ack;
}
tolayer3(B,send_packet);
}
B_output(message) /* need be completed only for extra credit */
struct msg message;
{
}
/* called when B's timer goes off */
B_timerinterrupt()
{
}
/*****************************************************************
***************** NETWORK EMULATION CODE STARTS BELOW ***********
The code below emulates the layer 3 and below network environment:
- emulates the tranmission and delivery (possibly with bit-level corruption
and packet loss) of packets across the layer 3/4 interface
- handles the starting/stopping of a timer, and generates timer
interrupts (resulting in calling students timer handler).
- generates message to be sent (passed from later 5 to 4)
THERE IS NOT REASON THAT ANY STUDENT SHOULD HAVE TO READ OR UNDERSTAND
THE CODE BELOW. YOU SHOLD NOT TOUCH, OR REFERENCE (in your code) ANY
OF THE DATA STRUCTURES BELOW. If you're interested in how I designed
the emulator, you're welcome to look at the code - but again, you should have
to, and you defeinitely should not have to modify
******************************************************************/
struct event {
float evtime; /* event time */
int evtype; /* event type code */
int eventity; /* entity where event occurs */
struct pkt *pktptr; /* ptr to packet (if any) assoc w/ this event */
struct event *prev;
struct event *next;
};
struct event *evlist = NULL; /* the event list */
int TRACE = 1; /* for my debugging */
int nsim = 0; /* number of messages from 5 to 4 so far */
int nsimmax = 0; /* number of msgs to generate, then stop */
float time = 0.000;
float lossprob; /* probability that a packet is dropped */
float corruptprob; /* probability that one bit is packet is flipped */
float lambda; /* arrival rate of messages from layer 5 */
int ntolayer3; /* number sent into layer 3 */
int nlost; /* number lost in media */
int ncorrupt; /* number corrupted by media*/
main()
{
struct event *eventptr;
struct msg msg2give;
struct pkt pkt2give;
int i,j;
char c;
init();
A_init();
B_init();
while (1)
{
eventptr = evlist; /* get next event to simulate */
if (eventptr==NULL)
goto terminate;
evlist = evlist->next; /* remove this event from event list */
if (evlist!=NULL)
evlist->prev=NULL;
if (TRACE>=2)
{
printf("\nEVENT time: %f,",eventptr->evtime);
printf(" type: %d",eventptr->evtype);
if (eventptr->evtype==0)
printf(", timerinterrupt ");
else if (eventptr->evtype==1)
printf(", fromlayer5 ");
else
printf(", fromlayer3 ");
printf(" entity: %d\n",eventptr->eventity);
}
time = eventptr->evtime; /* update time to next event time */
if (nsim==nsimmax)
break; /* all done with simulation */
if (eventptr->evtype == FROM_LAYER5)
{
generate_next_arrival(); /* set up future arrival */
/* fill in msg to give with string of same letter */
j = nsim % 26;
for (i=0; i<20; i++)
msg2give.data[i] = 97 + j;
if (TRACE>2)
{
printf(" MAINLOOP: data given to student: ");
for (i=0; i<20; i++)
printf("%c", msg2give.data[i]);
printf("\n");
}
nsim++;
if (eventptr->eventity == A)
A_output(msg2give);
else
B_output(msg2give);
}
else if (eventptr->evtype == FROM_LAYER3)
{
pkt2give.seqnum = eventptr->pktptr->seqnum;
pkt2give.acknum = eventptr->pktptr->acknum;
pkt2give.checksum = eventptr->pktptr->checksum;
pkt2give.ack = eventptr->pktptr->ack;
for (i=0; i<20; i++)
pkt2give.payload[i] = eventptr->pktptr->payload[i];
if (eventptr->eventity == A) /* deliver packet by calling */
A_input(pkt2give); /* appropriate entity */
else
B_input(pkt2give);
free(eventptr->pktptr); /* free the memory for packet */
}
else if (eventptr->evtype == TIMER_INTERRUPT)
{
if (eventptr->eventity == A)
A_timerinterrupt();
else
B_timerinterrupt();
}
else printf("INTERNAL PANIC: unknown event type \n");
free(eventptr);
}
terminate:
printf(" Simulator terminated at time %f\n after sending %d msgs from layer5\n",time,nsim);
}
init() /* initialize the simulator */
{
int i;
float sum, avg;
float jimsrand();
printf("----- Stop and Wait Network Simulator Version 1.1 -------- \n\n");
printf("Enter the number of messages to simulate: ");
scanf("%d",&nsimmax); // 发送数据包数目
printf("Enter packet loss probability [enter 0.0 for no loss]:");
scanf("%f",&lossprob); // 丢包率
printf("Enter packet corruption probability [0.0 for no corruption]:");
scanf("%f",&corruptprob); // 包损坏率
printf("Enter average time between messages from sender's layer5 [ > 0.0]:");
scanf("%f",&lambda); // 平均物理层传输时间
printf("Enter TRACE:");
scanf("%d",&TRACE); // 追踪 用于调试的
srand(9999); /* init random number generator */
sum = 0.0; /* test random number generator for students */
for (i=0; i<1000; i++)
sum=sum+jimsrand(); /* jimsrand() should be uniform in [0,1] */
avg = sum/1000.0; // 随机概率 估计值在0.25 ~ 0.75
if (avg < 0.25 || avg > 0.75)
{
printf("It is likely that random number generation on your machine\n" );
printf("is different from what this emulator expects. Please take\n");
printf("a look at the routine jimsrand() in the emulator code. Sorry. \n");
exit(0);
}
ntolayer3 = 0;
nlost = 0;
ncorrupt = 0;
time=0.0; /* initialize time to 0.0 */
generate_next_arrival(); /* initialize event list */
}
/****************************************************************************/
/* jimsrand(): return a float in range [0,1]. The routine below is used to */
/* isolate all random number generation in one location. We assume that the*/
/* system-supplied rand() function return an int in therange [0,mmm] */
/****************************************************************************/
float jimsrand()
{
double mmm = 0x7fff; /* largest int - MACHINE DEPENDENT!!!!!!!! */
float x; /* individual students may need to change mmm */
x = rand()/mmm; /* x should be uniform in [0,1] */
return(x);
}
/********************* EVENT HANDLINE ROUTINES *******/
/* The next set of routines handle the event list */
/*****************************************************/
generate_next_arrival()
{
double x,log(),ceil();
struct event *evptr;
char *malloc();
float ttime;
int tempint;
if (TRACE>2)
printf(" GENERATE NEXT ARRIVAL: creating new arrival\n");
x = lambda*jimsrand()*2; /* x is uniform on [0,2*lambda] */
/* having mean of lambda */
evptr = (struct event *)malloc(sizeof(struct event));
evptr->evtime = time + x;
evptr->evtype = FROM_LAYER5;
if (BIDIRECTIONAL && (jimsrand()>0.5) )
evptr->eventity = B;
else
evptr->eventity = A;
insertevent(evptr);
}
// 由之前生成的包中的eventtime决定 传输顺序 在队列中的什么位置
insertevent(p)
struct event *p;
{
struct event *q,*qold;
if (TRACE>2)
{
printf(" INSERTEVENT: time is %lf\n",time);
printf(" INSERTEVENT: future time will be %lf\n",p->evtime);
}
q = evlist; /* q points to header of list in which p struct inserted */
if (q==NULL) /* list is empty */
{
evlist=p;
p->next=NULL;
p->prev=NULL;
}
else
{
for (qold = q; q !=NULL && p->evtime > q->evtime; q=q->next)
qold=q;
if (q==NULL) /* end of list */
{
qold->next = p;
p->prev = qold;
p->next = NULL;
}
else if (q==evlist) /* front of list */
{
p->next=evlist;
p->prev=NULL;
p->next->prev=p;
evlist = p;
}
else /* middle of list */
{
p->next=q;
p->prev=q->prev;
q->prev->next=p;
q->prev=p;
}
}
}
printevlist()
{
struct event *q;
int i;
printf("--------------\nEvent List Follows:\n");
for(q = evlist; q!=NULL; q=q->next)
printf("Event time: %f, type: %d entity: %d\n",q->evtime,q->evtype,q->eventity);
printf("--------------\n");
}
/********************** Student-callable ROUTINES ***********************/
/* called by students routine to cancel a previously-started timer */
stoptimer(AorB)
int AorB; /* A or B is trying to stop timer */
{
struct event *q,*qold;
if (TRACE>2)
printf(" STOP TIMER: stopping timer at %f\n",time);
/* for (q=evlist; q!=NULL && q->next!=NULL; q = q->next) */
for (q=evlist; q!=NULL ; q = q->next)
{
/* remove this event */
if ( (q->evtype==TIMER_INTERRUPT && q->eventity==AorB) )
{
if (q->next==NULL && q->prev==NULL)
evlist=NULL; /* remove first and only event on list */
else if (q->next==NULL) /* end of list - there is one in front */
q->prev->next = NULL;
else if (q==evlist) /* front of list - there must be event after */
{
q->next->prev=NULL;
evlist = q->next;
}
else /* middle of list */
{
q->next->prev = q->prev;
q->prev->next = q->next;
}
free(q);
return;
}
}
printf("Warning: unable to cancel your timer. It wasn't running.\n");
}
starttimer(AorB,increment)
int AorB; /* A or B is trying to stop timer */
float increment;
{
struct event *q;
struct event *evptr;
char *malloc();
if (TRACE>2)
printf(" START TIMER: starting timer at %f\n",time);
/* be nice: check to see if timer is already started, if so, then warn */
/* for (q=evlist; q!=NULL && q->next!=NULL; q = q->next) */
for (q=evlist; q!=NULL ; q = q->next)
{
if (q->evtype==TIMER_INTERRUPT && q->eventity==AorB )
{
printf("Warning: attempt to start a timer that is already started\n");
return;
}
}
/* create future event for when timer goes off */
evptr = (struct event *)malloc(sizeof(struct event));
evptr->evtime = time + increment;
evptr->evtype = TIMER_INTERRUPT;
evptr->eventity = AorB;
insertevent(evptr);
}
/************************** TOLAYER3 ***************/
tolayer3(AorB,packet)
int AorB; /* A or B is trying to stop timer */
struct pkt packet;
{
struct pkt *mypktptr;
struct event *evptr,*q;
char *malloc();
float lastime, x, jimsrand();
int i;
ntolayer3++;
/* simulate losses: */
if (jimsrand() < lossprob)
{
nlost++;
if (TRACE>0)
printf(" TOLAYER3: packet being lost\n");
return;
}
/* make a copy of the packet student just gave me since he/she may decide */
/* to do something with the packet after we return back to him/her */
mypktptr = (struct pkt *)malloc(sizeof(struct pkt));
mypktptr->seqnum = packet.seqnum;
mypktptr->acknum = packet.acknum;
mypktptr->ack = packet.ack;
mypktptr->checksum = packet.checksum;
for (i=0; i<20; i++)
mypktptr->payload[i] = packet.payload[i];
if (TRACE>2)
{
printf(" TOLAYER3: seq: %d, acknum %d, check: %d ,ack %d ", mypktptr->seqnum,
mypktptr->acknum, mypktptr->checksum,mypktptr->ack);
for (i=0; i<20; i++)
printf("%c",mypktptr->payload[i]);
printf("\n");
}
/* create future event for arrival of packet at the other side */
evptr = (struct event *)malloc(sizeof(struct event));
evptr->evtype = FROM_LAYER3; /* packet will pop out from layer3 */
evptr->eventity = (AorB+1) % 2; /* event occurs at other entity */
evptr->pktptr = mypktptr; /* save ptr to my copy of packet */
/* finally, compute the arrival time of packet at the other end.
medium can not reorder, so make sure packet arrives between 1 and 10
time units after the latest arrival time of packets
currently in the medium on their way to the destination */
lastime = time;
/* for (q=evlist; q!=NULL && q->next!=NULL; q = q->next) */
for (q=evlist; q!=NULL ; q = q->next)
{
if ( (q->evtype==FROM_LAYER3 && q->eventity==evptr->eventity) )
lastime = q->evtime;
}
evptr->evtime = lastime + 1 + 9*jimsrand();
/* simulate corruption: */
if (jimsrand() < corruptprob)
{
ncorrupt++;
if ( (x = jimsrand()) < .75)
mypktptr->payload[0]='Z'; /* corrupt payload */
else if (x < .875)
mypktptr->seqnum = 999999;
else
mypktptr->acknum = 999999;
if (TRACE>0)
printf(" TOLAYER3: packet being corrupted\n");
}
if (TRACE>2)
printf(" TOLAYER3: scheduling arrival on other side\n");
insertevent(evptr);
}
tolayer5(AorB,datasent)
int AorB;
char datasent[20];
{
int i;
if (TRACE>2)
{
printf(" TOLAYER5: data received: ");
for (i=0; i<20; i++)
printf("%c",datasent[i]);
printf("\n");
}
}
交替位协议版本 实现效果
其实我还是调试了很久才弄出来的- - 因为各种问题
首先得理解程序 才能写 然后自己得看各种地方出没出错 然后得注意细节 还得按照程序原本的发送方式发送 理解了才能写的出来 调试出来 下面就是大概的流程了
下面找一段非常典型的来发出来吧
理一下下面发生了什么吧
786s 发送包 预计795s到达
786s 开始计时器 预计798s重发
786s 包损坏 795s 包不仅损坏还丢失了
798s 重发定时器启动 立马开始重发 重启定时器 810s再次重发
798s 刚发的包又丢失了
810s 再次重启的定时器启动 预计811s 到达 定时器同时再次启动 预计822s重发
811s 到达 且包正常
.....
由于我的丢包率 和坏包率设置的很高好像两个都是百分之30
就出现了上面的情况 由于两条信息的间隔我设置的很高 好像是1000 就不会存在还在重发就会发出第二条新消息的情况 反正到最后就发出去了 …
这个例子挑的好像不是很好 但还是能看得出来定时器的重要性和时间设置的重要性 比特位交互的效率肯定是很低的 最常用的还是需要滑动窗口 那这篇博客就先到这里 ^^ GBN
下一篇博客写