TCP RDT 3.0 停等协议的模拟(失败了!!!)

说明

这是一份关于大学计算机网络的作业,作业要求是根据rdt 3.0 协议 实现一个可靠传输的模拟程序,程序是老师给好的,当然这个程序是不全的,需要自己去修改完善。需要改动的代码文件是altbit.c,其他的文件都是正确的

程序会有四个测试来检验程序是否完成,我已经通过3个测试,其中有一个测试没有被通过,百思不得其解,希望路过的大神帮我瞅两眼,大恩大德,来世再报。

信息量有点大。。。

具体内容如下(原文是英文,自己翻译的不好):

背景

You've been hired by "Net Source", a company specialising in networking software. Their programmer, Mue, who was working on an implementation of the Alternating Bit protocol has caught the flu and the implementation must absolutely be finished in the next week. Most of the implementation is complete and Mue left comments for parts still to be completed. As the code isn't finished, Mue hasn't completed testing yet.
Mue is an experienced C programmer and there is nothing wrong with the C syntax or structure of the code that Mue has written. So you don't have to correct any C syntax errors, your job is to finish the code, correct any protocol errors and test it. Your boss realises you're not a C expert, but at least you've programmed in some language with C like syntax (e.g. Java, C++) before so you're the closest they have as an expert. Mue has written a C hints for programmers of C like languages programmers sheet which explains what you need to know about C that is different than Java. She's confident that if you stick to the sheet, anything else you need to add or change would be the same if you wrote it in Java.

你已经被一家专门从事网络软件的公司聘用了。他们的程序员,Mue,致力于实现一个比特交替协议,他已经感染了流感,并且必须在下周完成。大部分的实现都是完整的,Mue留下了一些有待完成的部分。由于代码还没有完成,Mue还没有完成测试。Mue是一个经验丰富的C语言程序员,并且他已经写的代码没有任何语法或则结构错误。所以你不需要修改任何C语法错误,你的工作就是完成代码,纠正一些协议错误并且完成测试,你的老板意识到你不是一个有经验的C语言专家,但是至少之前你已经使用过一些类似C语言语法(例如java,c++)的语言,所以你是他们最接近的专家,Mue已经写了一篇 《C hints for programmers of C like languages programmers》说明了你需要了解一些关于C与Java相比的不同,他相信你在写java时,如果你按照手册/文档的说明去做,无论你添加还是修改代码,都是类似的情况

【这里提到的《C hints for programmers of C like languages programmers》我就不放上来,我觉得c语言应该是必须掌握的】

The testing system 测试系统

To help isolate and demonstrate the behaviour of the sender and receiver, you have a simulation system at your disposal. The overall structure of the environment is shown below:

为了帮助隔离和演示发送方和接收方的行为,您可以使用一个模拟系统。环境的总体结构如下所示

图片描述

There are two hosts (A and B). An application on host A is sending messages to an application on host B.
The application messages (layer 5) on host A are sent to layer 4 (transport) on host A, which implements Alternating Bit for reliable delivery. Layer 4 creates packets and sends them to the network (layer 3). The network transfers (unreliably) these packets to host B where they are handed to the transport layer (Alternating Bit receiver) and if not corrupt or out of order, the message is extracted and delivered to the receiving application (layer 5) on host B.

有两台主机(A和B)。一个应用程序在主机A上发信息给在主机B上的应用程序。主机A上的应用程序消息(第5层)被发送到主机A的第4层(传输),它实现了交替的比特来进行可靠的交付。4创建数据包并将它们发送给网络层(第三层)。网络传输(不可靠)这些包给主机B传输层(交替接收器),如果不是损坏的或次序混乱的,信息将被提取并交付给在主机B(5层)上的接收应用程序。

Mue has supplied the code for the Alternating Bit sender procedures A_output(), A_init(), A_input(), and A_timerinterrupt(). She has also written the code for the Alternating Bit receiver procedures B_input() and B_init(). At this stage, only unidirectional transfer of data (from A to B) is required, so B does not need to implement B_timerinterrupt or B_output. Of course, B will have to send ACK packets to A to acknowledge receipt of data.

Mue提供了比特交替发送器程序A_output()、A_init()、A_input()和A_timerinterrupt()的代码。她还编写了比特交替接收程序B_input()和B_init()的代码。在这个阶段,只需要单向的数据传输(从A到B),所以B不需要实现 B_timerinterrupt或B_output。当然,B必须向A发送ACK数据包以确认接收到数据。

The routines are detailed below. Such procedures in real-life would be part of the operating system, and would be called by other procedures in the operating system. In the simulator the simulator will call and be called by procedures that emulate the network environment and operating system.

例程如下所示。在现实生活中,这样的程序将是操作系统的一部分,并且将被操作系统中的其他程序调用。在模拟器中,模拟器将调用并被模拟网络环境和操作系统的过程调用。

A_output(message), where message is a structure of type struct msg, containing data to be sent to B. This routine will be called whenever the upper layer application at the sending side (A) has a message to send. It is the job of the Alternating Bit protocol to insure that the data in such a message is delivered in-order, and correctly, to the receiving side upper layer.

A_output(message), ·消息是类型struct msg的结构,包含要发送到B的数据。每当发送端(A)上的上层应用程序有消息要发送时,这个例程将被调用。这是比特交替协议的工作,以确保这样一条消息中的数据按顺序正确地传递给接收方上层。

A_input(packet), where packet is a structure of type struct pkt . This routine will be called whenever a packet sent from B (i.e., as a result of a tolayer3() being called by a B procedure) arrives at A. packet is the (possibly corrupted) packet sent from B.

A_input(packet),其中信息包是类型struct pkt的结构。这个例程将在从B发送的数据包时被调用(例如由于一个tolayer3()被B过程调用)到达A。包是从B发送的(可能损坏的)数据包。

A_timerinterrupt() This routine will be called when A's timer expires (thus generating a timer interrupt). This routine controls the retransmission of packets. See starttimer() and stoptimer() below for how the timer is started and stopped.

A_timerinterrupt()这个例程将在A的计时器到期时调用(从而产生一个计时器中断)。这个例程控制数据包的重新传输。请参阅下面的starttimer()和stoptimer(),了解定时器是如何启动和停止的。

A_init() This routine will be called once, before any other A-side routines are called. It is used to do any required initialization.
A_init()这个例程将在任何其他的a例程被调用之前被调用一次。它被用来做任何必需的初始化。

B_input(packet), where packet is a structure of type struct pkt . This routine will be called whenever a packet sent from A (i.e., as a result of a tolayer3() being called by a A-side procedure) arrives at B. The packet is the (possibly corrupted) packet sent from A.

B_input(packet),其中包是类型struct pkt的结构。这个例程将在从A发送的数据包(即:由于tolayer3()被A方程序调用)到达B的时候被调用.数据包是来自A的(可能损坏的)数据包。

B_init() This routine will be called once, before any other B-side routines are called. It is used to do any required initialization.

B_init()这个例程将在任何其他的b端例程被调用之前被调用一次。它被用来做任何必需的初始化。

The unit of data passed between the application layer and the Alternating Bit (transport layer) protocol is a message, which is declared as:

在应用程序层和比特交替(传输层)协议之间传递的数据单元是一条消息,它被声明为:

struct msg{
    char data[20];
};
That is, data is stored in a msg structure which contains an array of 20 chars. A char is one byte. The sending entity will thus receive data in 20-byte chunks from the sending application; and the receiving entity should deliver 20-byte chunks to the receiving application.

也就是说,数据存储在msg结构中,其中包含20个字符数组。char是一个字节。因此,发送实体将从发送应用程序接收20字节块的数据;接收实体应该将20字节的块交付给接收应用程序。
The unit of data passed between Alternating Bit (transport layer) and the network layer is the packet, which is declared as:
在比特交替(传输层)和网络层之间传递的数据单元是包,它被声明为:

struct pkt{
    int seqnum;
    int acknum;
    int checksum;
    char payload[20];
};
The A_output routine fills in the payload field from the message data passed down from the Application layer. The other packet fields are used by the Alternating Bit protocol to insure reliable delivery, as we've seen in class.

A_output从应用程序层传递的消息数据中填充payload 字段。其他的包字段被比特交替协议使用,以确保可靠的交付,就像我们在课堂上看到的那样。

These functions implement what the sender and receiver should do when packets arrive.

这些函数实现了发送方和接收方在数据包到达时应该做的事情。

Software Interfaces 软件界面

The procedures described above implement the Alternating Bit tranport layer protocol. The following emulator procedures are called by the Alternating Bit procedures. They are explained here so you know how they fit in. They are not part of the Alternating Bit implementation and these routines work correctly. Do not modify them:

上面描述的过程实现了比特替换传输层协议。下面的模拟器程序由比特交替程序调用。它们在这里被解释,所以你知道它们是如何适应的。它们不是比特实现的一部分,这些例程正常工作。不要修改它们:

starttimer(calling_entity,increment), where calling_entity is either A (for starting the A-side timer) or B (for starting the B side timer), and increment is a float value indicating the amount of time that will pass before the timer interrupts. A's timer should only be started (or stopped) by A-side routines, and similarly for the B-side timer. To give you an idea of the appropriate increment value to use: a packet sent into the network takes an average of 5 time units to arrive at the other side when there are no other messages in the medium. You are free to experiment with different timeout values; but when handing in for mark submission linking, the timeout value must be set to 15.0

calling_entity要么是A(用于启动A-side定时器)或B(用于启动B侧计时器),而增量是一个浮点值,指示计时器中断之前的时间量。A的计时器只能由A侧例程启动(或停止),而b端计时器也同样如此。为了让您了解适当的增值价值:发送到网络的数据包在介质中没有其他消息时,平均需要5个时间单位到达另一端。您可以自由地尝试不同的超时值;但是在提交mark提交链接时,超时值必须设置为15.0

Note that starttimer() is not restarttimer(). If a timer is already running it must be stopped before it is started. Calling starttimer when the timer is already running, or calling stoptimer when the timer is not running, indicates an error in the protocol behaviour and will result in an error message.

请注意starttimer()不是restarttimer()。如果一个计时器已经运行,那么它必须在启动之前停止。当定时器已经运行时调用starttimer,或者在定时器没有运行时调用stoptimer,在协议行为中指出一个错误,并将导致一条错误消息。

The starttimer() call should occur immediately after the tolayer3() call to send the packet being timed.

starttimer()调用应该在tolayer3()调用之后立即发生,以发送计时的数据包。

stoptimer(calling_entity), where calling_entity is either A (for stopping the A-side timer) or B (for stopping the B side timer).

stoptimer(calling_entity),其中calling_entity 是A(用于停止A侧计时器)或B(用于停止B侧计时器)。

tolayer3(calling_entity,packet), where calling_entity is either A (for the A-side send) or B (for the B side send), and packet is a structure of type struct pkt. Calling this routine will cause the packet to be sent into the network, destined for the other entity.

tolayer3(calling_entity,packet),其中calling_entity要么是A(对于A端发送)要么是B(对于B端发送),packet是类型struct pkt的结构。调用这个例程将导致数据包被发送到网络,并被发送到另一个实体。

tolayer5(calling_entity,message), where calling_entity is either A (for A-side delivery to layer 5) or B (for B-side delivery to layer 5), and message is a structure of type msg. With unidirectional data transfer, you would only be calling this with calling_entity equal to B (delivery to the B-side). Calling this routine will cause data to be passed up to layer 5.

其中calling_entity要么是A(用于向第5层交付)或B(B端交付到第5层),而消息是msg类型的结构。有了单向的数据传输,您只能调用callingentity等于B(向B端交付)。调用这个例程将导致数据被传递到第5层。

The emulator code is in the file emulator.c

emulator 的代码文件是emulator.c

The incorrect implementation of Alternating Bit is in the file altbit.c

Alternating Bit(比特交替)的实现代码在文件altbit.c中

There are also two header files emulator.h and altbit.h

还有两个头文件是emulator.h 和altbit.h

which define the procedures and shared variables used in the program.
You should download the 4 files and examine the code in altbit.c with the description above and your knowledge of Alternating Bit and make sure you understand how the program fits together. You do not need to understand emulator.c; but if you want to know how the emulator works, you are welcome to look at the code.
To build the program use the command:

您应该下载4个文件并检查altbit中的代码。用上面的描述和你对交替位的知识,确保你理解程序是如何组合在一起的。您不需要理解emulator.c;但是如果您想知道模拟器是如何工作的,欢迎您查看代码。
要构建程序,请使用以下命令:

gcc -ansi -Wall -pedantic emulator.c altbit.c 
The executable program will be called a.out. To run the program, type:

可执行程序将被称为a.out。要运行这个程序,输入:

./a.out

The simulated network environment

模拟的网络环境

The medium is capable of corrupting and losing packets. It will not reorder packets. When you compile and run the resulting program, you will be asked to specify values regarding the simulated network environment:

这种媒介有能力损坏和丢失数据包。它不会重新排序数据包。当你编译并运行结果程序时,你将被要求指定关于模拟网络环境的值:

Number of messages to simulate.The emulator will stop generating messages as soon as this number of messages have been passed down from layer 5.

要模拟的消息数量。当这些消息从第5层传递下来时,模拟器将停止生成消息。

Loss. You are asked to specify a packet loss probability. A value of 0.1 would mean that one in ten packets (on average) are lost.
损失。你被要求指定一个包丢失的概率。0.1的值意味着每10个包中的1个(平均)丢失。

Corruption. You are asked to specify a packet corruption probability. A value of 0.2 would mean that one in five packets (on average) are corrupted. Note that the contents of payload, sequence or ack field can be corrupted.

损坏。你被要求指定一个包的损坏概率。0.2的值意味着每5个包中就有一个(平均)被损坏。注意,有效负载、序列或ack字段的内容可能被破坏。

Tracing. Setting a tracing value of 1 or 2 will print out useful information about what is going on inside the emulation (e.g., what's happening to packets and timers). A tracing value of 0 will turn this off. A tracing value greater than 2 will display all sorts of messages that detail what is happening inside the emulation code as well. A tracing value of 2 may be helpful to you in debugging your code. You should keep in mind that real implementors do not have underlying networks that provide such nice information about what is going to happen to their packets!

跟踪。设置1或2的跟踪值将打印出关于仿真中正在发生的事情的有用信息(例如,数据包和计时器发生了什么)。跟踪值为0将关闭该值。一个大于2的跟踪值将显示各种消息,这些消息详细描述了仿真代码中发生的事情。在调试代码时,跟踪值2可能对您有帮助。您应该记住,真正的实现者没有底层网络,提供关于他们的数据包将发生什么事情的良好信息!

Average time between messages from sender's layer5. You can set this value to any non-zero, positive value. Note that the smaller the value you choose, the faster packets will be generated.

从发送者的layer5发送消息的平均时间。你可以把这个值设置成任何非零的,正的值。请注意,您选择的值越小,就会生成更快的数据包。

Take a moment to experiment with the simulated environment and look at the events that occur. Try sending a few packets with no loss or corruption. Does the protocol work properly? Try with just loss. Try with just corruption. Some of the errors with the Alternating Bit implementation should be apparent.

花点时间来试验一下模拟环境,看看发生的事件。试着发送一些没有损失或损坏的包。协议是否正常工作?尝试只有损失。比特交替协议实现的一些错误应该是显而易见的。

What to do 要做什么

The implementation of Alternating Bit should be identical to that described in your book (page 244/245, Extended FSM description of Alternating Bit sender and receiver are shown).

比特交替的实现应该与您的书中所描述的相同(第244/245页,扩展的FSM描述,比特交替发送者和接收方的描述)。

There may be errors in the protocol. You will need to test it. The errors are only in the altbit.c file. The other three files are correct and do not need to be modified. None of the mistakes are with the programming language or the data structures, they are all errors in the protocol behaviour. The program will compile and run as written; but the sender and receiver do not follow the Alternating Bit protocol.

在协议中可能有错误。你需要对它进行测试。这些错误只存在于altbit.c文件中。其他三个文件是正确的,不需要修改。这些错误都不是编程语言或数据结构的错误,它们都是协议行为中的错误。该程序将按照编写的方式进行编译和运行;但是发送方和接收方不遵循比特交替协议。

This part has 4 tests which test that you have correctly finished the code. Since mark submission link will show you what should have happened if you submit an incorrect solution,there is a 1 mark penalty for each submission you make after the first. You should test and correct any errors before submission. If you rely on mark submission link to do your testing, then your mark will reflect this. However, if you are really stuck, realise that this is a small percentage of the marks for each test, and the output should help clarify any misconception. We have provided an Oracle (see details below in the Handing In section) which will tell you if you pass the tests or not; but will give you no other information. There is no mark penalty to use the Oracle.

这部分有4个测试,测试您是否正确地完成了代码。由于mark提交链接将向您展示如果提交了一个不正确的解决方案,应该发生什么,那么在第一次提交之后,每个提交的提交都将受到1个标记的惩罚。您应该在提交之前测试并纠正任何错误。如果您依赖于mark提交链接来进行测试,那么您的标记将反映这一点。然而,如果你真的被困住了,要意识到这只是每个测试的一小部分分数,而输出应该有助于澄清任何误解。我们已经提供了一个Oracle(请参阅下面的详细信息),它将告诉您是否通过了测试;但不会给你任何其他信息。使用Oracle是没有任何标记的。

I suggest the following strategy for this practical. In each step I'd recommend focussing on getting the receiver behaving correctly first and then turn your attention to the sender (the receiver behaviour is simpler and therefore easier to get right and test). :

我建议采取以下策略。在每一个步骤中,我建议集中于让接收方先正确地操作,然后将注意力转移到发送方(接收方的行为更简单,因此更容易获得正确和测试)。:

1.Compile the code and do a simple test sending just one packet with no corruption or loss. Does both the sender and receiver behave properly in this scenario? If not fix any problems. At this stage you should be able to pass test 1 (you can check with Oracle). If not, look at what your sender and receiver do in your test. (10 marks)

编译代码并做一个简单的测试,只发送一个包,不存在任何损坏或损失。在这个场景中,发送方和接收方都表现得很好吗?如果不解决任何问题。在这个阶段,您应该能够通过测试1(您可以与Oracle进行核对【这里提到Oracle是一个可以检查自己代码是否正确的系统,它能显示出哪些测试没有通过,但是不会提示具体是哪里错误】)。如果没有,看看您的发送者和接收者在您的测试中做了什么。(10分)

2.Fill in the code items numbered 1. Now run some small tests with loss. Does your sender follow the protocol properly when a packet is lost? If not, fix the behaviour. You should now be able to pass test 2.(10 marks)

填写编号为1的代码项。现在运行一些带有损失的小测试。当数据包丢失时,发送方是否正确地遵循协议?如果没有,那就修正行为。您现在应该能够通过测试2。(10分)

3.Fill in the code items number 2 (check if ACK is a duplicate ACK) and 3 (B receives wrong sequence number or corrupt packet). When you have this correct you should be able to pass test 3. (20 marks)
填写代码项2(检查ACK是否为重复的ACK)和3(B接收错误的序列号或损坏的数据包)。当你有这个正确的时候,你应该能够通过测试3。(20分)

4.Fill in the code item numbered 4. Now run a small test with corruption. Do your sender and receiver detect corruption when it occurs and do they react correctly when a corrupted packet or ACK is received? You should now be able to pass test 4. (10 marks)

填写编号为4的代码项。现在用损坏来做一个小测试。当收到损坏的数据包或ACK时,您的发送方和接收方是否会检测到损坏,并且在收到损坏的数据包时是否正确地做出反应?您现在应该能够通过测试4。(10分)

代码

altbit.h

extern void A_init(void);
extern void B_init(void);
extern void A_input(struct pkt);
extern void B_input(struct pkt);
extern void A_output(struct msg);
extern void A_timerinterrupt(void);

/* included for extension to bidirectional communication */
#define BIDIRECTIONAL 0       /*  0 = A->B  1 =  A<->B */
extern void B_output(struct msg);
extern void B_timerinterrupt(void);

altbit.c

#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include "emulator.h"
#include "altbit.h"

/* ******************************************************************
   Unfinished Alternating bit protocol.  Adapted from
   ALTERNATING BIT AND GO-BACK-N NETWORK EMULATOR: VERSION 1.1  J.F.Kurose

   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).

   Modifications (6/6/2008 - CLP): 
   - removed bidirectional code and other code not used by prac. 
   - fixed C style to adhere to current programming style
   (7/8/2009 - CLP)
   - converted to Alt Bit
**********************************************************************/

#define RTT  15.0       /* round trip time.  MUST BE SET TO 15.0 when submitting assignment */
#define WINDOWSIZE 1    /* alternating bit only allows one unacked packet */
#define NOTINUSE (-1)   /* used to fill header fields that are not being used 用于填充未被使用的头字段 */

/* generic procedure to compute the checksum of a packet.  Used by both sender and receiver  
   the simulator will overwrite part of your packet with 'z's.  It will not overwrite your 
   original checksum.  This procedure must generate a different checksum to the original if
   the packet is corrupted.
*/
/**计算校验和*/
int ComputeChecksum(struct pkt packet)
{
  int checksum = 0;

  /****** 4. FILL IN CODE to calculate the checksum of packet 填入代码来计算数据包的校验和*****/
  ///
  for (int i = 0; i < 20;i++) {
      checksum += packet.payload[i];
  }
  checksum += packet.acknum;
  checksum += packet.seqnum;
  checksum = ~checksum;
  ///

  return checksum;
}

/**检查一个包是不是损坏的*/
bool IsCorrupted(struct pkt packet)
{
  if (packet.checksum == ComputeChecksum(packet))
    return (false);
  else
    return (true);
}

/********* Sender (A) variables and functions ************/

static struct pkt buffer[WINDOWSIZE];  /* array for storing packets waiting for ACK */
static int windowfirst, windowlast;    /* array indexes of the first/last packet awaiting ACK */
static int windowcount;                /* the number of packets currently awaiting an ACK 当前等待ACK的数据包的数量 */
static int A_nextseqnum;               /* the next sequence number to be used by the sender */

/* called from layer 5 (application layer), passed the message to be sent to other side */
void A_output(struct msg message)
{
  struct pkt sendpkt;
  int i;

  /* if not blocked waiting on ACK */
  if ( windowcount < WINDOWSIZE) {
    if (TRACE > 1)
      printf("----A: New message arrives, send window is not full, send new messge to layer3!\n");

    /* create packet */
    sendpkt.seqnum = A_nextseqnum;
    sendpkt.acknum = NOTINUSE;
    for ( i=0; i<20 ; i++ ) 
      sendpkt.payload[i] = message.data[i];
    sendpkt.checksum = ComputeChecksum(sendpkt); 

    /* put packet in window buffer */
    /* windowlast will always be 0 for alternating bit; but not for GoBackN */
    windowlast = (windowlast + 1) % WINDOWSIZE; 
    buffer[windowlast] = sendpkt;
    for (i=0; i<20; i++)
      buffer[windowlast].payload[i]=sendpkt.payload[i];  /* copy the array */
    windowcount++;

    /* send out packet */
    if (TRACE > 0)
      printf("Sending packet %d to layer 3\n", sendpkt.seqnum);
    tolayer3 (A, sendpkt);
    /**** 1. FILL IN CODE There's something else A needs to do when it sends a packet. *****/
    /////////
    starttimer(A, RTT);
    /////////
    A_nextseqnum = (A_nextseqnum + 1) % 2;  /* we only have seqnum 0 and 1 */
  }
  /* if blocked,  window is full */
  else {
    if (TRACE > 0)
      printf("----A: New message arrives, send window is full\n");
    window_full++;
  }
}


/* called from layer 3, when a packet arrives for layer 4 
   In this practical this will always be an ACK as B never sends data.
*/
void A_input(struct pkt packet)
{

  /* if received ACK is not corrupted 如果收到的ACK没有损坏*/ 
  if (!IsCorrupted(packet)) {
    if (TRACE > 0)
      printf("----A: uncorrupted ACK %d is received\n",packet.acknum);
    total_ACKs_received++;

    /* check if new ACK or duplicate 检查ACK是新的还是原来的*/
    if (packet.acknum!= A_nextseqnum) {    /**** 2. FILL IN CODE replace TRUE with test whether this is a new ACK ***/
      /* packet is a new ACK */
      if (TRACE > 0)
        printf("----A: ACK %d is not a duplicate\n",packet.acknum);
      new_ACKs++;

      /* delete the acked packets from window buffer 从缓存窗口中删除已经确认的包 */
      windowcount--;

      /***** 1. FILL IN CODE  What else needs to be done when an ACK arrives
       besides removing the packet from the window?  ****/
      ////
      stoptimer(A);
      ////
    }
    else
      if (TRACE > 0)
        printf ("----A: duplicate ACK received, do nothing!\n");
  }
  else 
    if (TRACE > 0)
      printf ("----A: corrupted ACK is received, do nothing!\n");
}

/* called when A's timer goes off */
void A_timerinterrupt(void)
{

  if (TRACE > 0)
    printf("----A: time out,resend packets!\n");

  if (TRACE > 0)
    printf ("---A: resending packet %d\n", (buffer[windowfirst]).seqnum);
  tolayer3(A,buffer[windowfirst]);
  /**** 1. FILL IN CODE What state should the timer be in at this point? *****/
  ////
  starttimer(A, RTT);
  /////
  packets_resent++;
}       



/* the following routine will be called once (only) before any other */
/* entity A routines are called. You can use it to do any initialization */
void A_init(void)
{
  /* initialise A's window, buffer and sequence number */
  A_nextseqnum = 0;  /* A starts with seq num 0, do not change this */
  windowfirst = 0;
  windowlast = -1;   /* windowlast is where the last packet sent is stored.  
             new packets are placed in winlast + 1 
             so initially this is set to -1           */
  windowcount = 0;
}



/********* Receiver (B)  variables and procedures ************/

static int expectedseqnum; /* the sequence number expected next by the receiver */
static int B_nextseqnum;   /* the sequence number for the next packets sent by B */


/* called from layer 3, when a packet arrives for layer 4 at B*/
void B_input(struct pkt packet)
{
  struct pkt sendpkt;
  int i;

  /* if not corrupted and received packet is in order */
  if  ( (!IsCorrupted(packet))  && (packet.seqnum == expectedseqnum) ) {
    if (TRACE > 0)
      printf("----B: packet %d is correctly received, send ACK!\n",packet.seqnum);
    packets_received++;

    /* deliver to receiving application */
    tolayer5(B, packet.payload);

    /* send an ACK for the received packet */
    sendpkt.acknum = expectedseqnum;

    /* update state variables */
    expectedseqnum = (expectedseqnum + 1) % 2;        
  }
  else {
    /* packet is corrupted or out of order */
    if (TRACE > 0) 
      printf("----B: packet corrupted or not expected sequence number, resend ACK!\n");
    /***** 3. FILL IN CODE  What ACK number should be sent if the packet
       was corrupted or out of order? *******/ 
    /////
    sendpkt.acknum = (expectedseqnum + 1) % 2;
    /////

  }

  /* create packet */
  sendpkt.seqnum = B_nextseqnum;
  B_nextseqnum = (B_nextseqnum + 1) % 2;
    
  /* we don't have any data to send.  fill payload with 0's */
  for ( i=0; i<20 ; i++ ) 
    sendpkt.payload[i] = '0';  

  /* computer checksum */
  sendpkt.checksum = ComputeChecksum(sendpkt); 

  /* send out packet */
  tolayer3 (B, sendpkt);
}

/* the following routine will be called once (only) before any other */
/* entity B routines are called. You can use it to do any initialization */
void B_init(void)
{
  expectedseqnum = 0;
  B_nextseqnum = 1;
}

/******************************************************************************
 * The following functions need be completed only for bi-directional messages *
 * They do not need to be completed for this practical                        *
 *****************************************************************************/

/* Note that with simplex transfer from a-to-B, there is no B_output() */
void B_output(struct msg message)  
{
}

/* called when B's timer goes off */
void B_timerinterrupt(void)
{
}

emulator.h

extern int TRACE;

/* statistics updated by GBN */
extern int total_ACKs_received;
extern int packets_resent;       /* count of the number of packets resent  */
extern int new_ACKs;      /* count of the number of acks correctly received */
extern int packets_received;  /* count of the packets received by receiver */
extern int window_full; /* count of the number of messages dropped due to full window */

#define   A    0
#define   B    1

/* 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];
};

/* send to A or B (int), packet to send */
extern void tolayer3(int, struct pkt);  

/* deliver to A or B (int), data to deliver */
extern void tolayer5(int, char[20]); 

/* start timer at A or B (int), increment */
extern void starttimer(int, double);       

/* stop timer at A or B (int) */
extern void stoptimer(int);               

emulator.c

#define _CRT_SECURE_NO_WARNINGS
/* ***** THIS FILE SHOULD NOT BE MODIFIED ****************************
   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
   This file contains the code that emulates the network.  It does not
   implement any of the Go-Back-N protocol.
   ********************************************************************

   ******************************************************************
   ALTERNATING BIT AND GO-BACK-N NETWORK EMULATOR: VERSION 1.1  J.F.Kurose
   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)

   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).

   Modifications (6/6/2008 - CLP): 
   - removed bidirectional GBN code and other code not used by prac. 
   - removed hard coded maximum random number, use library defined
   RAND_MAX value 
   - simulator stops when no events are left rather than stopping as
   soon as n packets are sent.
   - fixed C style to adhere to current programming style

   ********************************************************************* */
#include <stdlib.h>
#include <stdio.h>
#include "emulator.h"
#include "altbit.h"



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:可能事件 */
#define  TIMER_INTERRUPT 0  
#define  FROM_LAYER5     1
#define  FROM_LAYER3     2

#define  OFF             0
#define  ON              1

int TRACE = 3;

/* statistics updated by GBN */
int window_full;   /* count of the number of messages dropped due to full window */
int total_ACKs_received;
int packets_resent;       /* count of the number of packets resent  */
int new_ACKs;           /* count of the number of acks correctly received */
int packets_received;  /* count of the packets received by receiver */

/* statistics updated by emulator */
static int packets_lost;  
static int packets_corrupt;
static int packets_sent;
static int packets_timeout;
static int messages_delivered;

static int nsim = 0;              /* number of messages from 5 to 4 so far */ 
static int nsimmax = 0;           /* number of msgs to generate, then stop */
static float time = 0.000;
static float lossprob;            /* probability that a packet is dropped  */
static float corruptprob;   /* probability that one bit is packet is flipped */
static int corruptdirection; /* A->B A<-B or bidirectional corruption/loss */
static float lambda;        /* arrival rate of messages from layer 5 */   
static int   ntolayer3;           /* number sent into layer 3 */
static int   nlost;               /* number lost in media */
static int ncorrupt;              /* number corrupted by media*/

/****************************************************************************/
/* jimsrand(): return a double 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]        */
/****************************************************************************/
/**返回一个0-1的double类型的随机数*/
double jimsrand(void) 
{
  /**RAND_MAX 指的是 C 语言标准库 中定义的一个宏。经预编译阶段处理后,
  它展开为一个整数类型的常量表达式。RAND_MAX 是 中伪随机数生成函数 rand 
  所能返回的最大数值。这意味着,任何一次对 rand 的调用,都将得到一个 0~RAND_MAX 之间
  的伪随机数。 */
  double mmm = RAND_MAX;     /* largest int  - MACHINE DEPENDENT!!!!!!!!   */
  double x;                   
  x = rand()/mmm;            /* x should be uniform in [0,1](x应该在0,1之间的随机数) */
  if (TRACE > 3)
    printf("RANDOM NUMBER GENERAION CALLED: %f\n", x);
  return(x);
}  

/********************* EVENT HANDLINE ROUTINES *******/
/*  The next set of routines handle the event list   */
/*****************************************************/

/**插入事件*/
void insertevent(struct event *p)
{
  struct event *q,*qold;

  if (TRACE>2) {
    printf("            INSERTEVENT: time is %f\n",time);
    printf("            INSERTEVENT: future time will be %f\n",p->evtime); 
  }
  q = evlist;     /* q points to front 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;
    }
  }
}

void generate_next_arrival(void)
{
  double x;
  struct event *evptr;

  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 = malloc(sizeof(struct event));
  if (evptr == 0) {
    printf("memory allocation for event failed.");
    exit(EXIT_FAILURE);
  }
  evptr->evtime =  time + x;
  evptr->evtype =  FROM_LAYER5;
  if (BIDIRECTIONAL && (jimsrand()>0.5) )
    evptr->eventity = B;
  else
    evptr->eventity = A;
  insertevent(evptr);
} 

void printevlist(void)
{
  struct event *q;
  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");
}

/**初始化模拟器*/
void init(void)                         /* initialize the simulator */
{
  float sum, avg;
  int i;

  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);
  if (lossprob != 0.0 || corruptprob != 0.0) {
    //如果你想要损失或腐败只发生在一个方向上,选择方向
    printf("If you want loss or corruption to only occur in one direction, choose the direction: 0 A->B, 1 A<-B, 2 A<->B (both directions) :");
    scanf("%d",&corruptdirection);
  }
  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+=jimsrand();    /* jimsrand() should be uniform in [0,1] */
  avg = sum/1000.0;
  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(EXIT_FAILURE);
  }

  /* initialise statistics */
  window_full = 0;
  total_ACKs_received = 0;
  packets_resent = 0;
  new_ACKs = 0;
  packets_received = 0;
  packets_lost = 0;  
  packets_corrupt = 0;
  packets_sent = 0;
  packets_timeout = 0;
  messages_delivered = 0;

  ntolayer3 = 0;
  nlost = 0;
  ncorrupt = 0;

  time=0.0;                    /* initialize time to 0.0 */
  generate_next_arrival();     /* initialize event list */
}

/********************** Student-callable ROUTINES ***********************/

/* called by students routine to cancel a previously-started timer */
void stoptimer(int AorB)
/* A or B is trying to stop timer */
{
  struct event *q;

  if (TRACE>1)
    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) 
    if ( (q->evtype==TIMER_INTERRUPT  && q->eventity==AorB) ) { 
      /* remove this event */
      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");
}


void starttimer(int AorB, double increment)
/* A or B is trying to start timer */
{

  struct event *q;
  struct event *evptr;

  if (TRACE>1)
    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 = malloc(sizeof(struct event));
  if (evptr == 0) {
    printf("memory allocation for event failed.");
    exit(EXIT_FAILURE);
  }
  evptr->evtime =  time + increment;
  evptr->evtype =  TIMER_INTERRUPT;
   
 
  evptr->eventity = AorB;
  insertevent(evptr);
} 


/************************** TOLAYER3 ***************/
void tolayer3(int AorB, struct pkt packet)
/* A or B is sending to network  */
{
  struct pkt *mypktptr;
  struct event *evptr,*q;
  float lastime, x;
  int i;

  ntolayer3++;

  /* simulate losses: */
  if (jimsrand() < lossprob && (!(AorB == B && corruptdirection == A) && !(AorB == A && corruptdirection == B))) {
    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 = malloc(sizeof(struct pkt));
  if (mypktptr == 0) {
    printf("memory allocation for event failed.");
    exit(EXIT_FAILURE);
  }
  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 = malloc(sizeof(struct event));
  if (evptr == 0) {
    printf("memory allocation for event failed.");
    exit(EXIT_FAILURE);
  }
  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)  && (!(AorB == B && corruptdirection == A) && !(AorB == A && corruptdirection == B))) {
    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);
} 

void tolayer5(int AorB, char datasent[20])
{
  int i;  
  if (TRACE>2) {
    printf("          TOLAYER5: data received by application at ");
    if (AorB == A) 
      printf("A: ");
    else
      printf("B: ");
    for (i=0; i<20; i++)  
      printf("%c",datasent[i]);
    printf("\n");
  }
  messages_delivered++;
}

int main(void)
{
  struct event *eventptr;
  struct msg  msg2give;
  struct pkt  pkt2give;
   
  int i,j;

  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 (eventptr->evtype == FROM_LAYER5 ) {
      if (nsim < nsimmax) {
        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 (TRACE > 2)
          printf("          FROM_LAYER5: no more messages to send: \n");
    }
    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 释放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 attempting to send %d msgs from layer5\n",time,nsim);
  printf("number of messages dropped due to full window:  %d \n", window_full);
  printf("number of valid (not corrupt or duplicate) acknowledgements received at A:  %d \n", new_ACKs);
  printf("(note: a single acknowledgement may have acknowledged more than one packet - if cumulative acknowledgements are used)\n");
  printf("number of packet resends by A:  %d \n", packets_resent);
  printf("number of correct packets received at B:  %d \n", packets_received);
  printf("number of messages delivered to application:  %d \n", messages_delivered);
  system("pause");
  return EXIT_SUCCESS;
}

上面我已经按照要求填写了代码,在oracle测试结果是test1、2、4成功,test3失败。如下图所示:

图片描述

下面是书中的有限状态图

图片描述

阅读 6.5k
1 个回答

自己提的问题还是自己答吧:
在别人的暗示下我把
代码altbit.c中的A_input函数中的判断

if (packet.acknum!= A_nextseqnum)

改成

if (packet.acknum!= A_nextseqnum && windowcount>=WINDOWSIZE)

虽然通过了测试,但是还不知道为什么
希望路过的人知道了说一下
我也继续在思考