12、基本数据链路层协议(数据链路层)

1、基本数据链路层协议

引言

  • 在考察协议之前,先明确一下有关底层通信模型的基本假设是有必要的。首先我们假设物理层、数据链路层和网络层都是独立的进程,它们通过来回传递信息进行通信。如图所示,物理层进程和某些数据链路层进程运行在一个称为网络接口卡(NIC)的专用硬件上:链路层进程和操作系统的一部分运行在主CPU上,链路层进程的软件通常以设备驱动器的形式存在。然而,其他的实现方案也是有可能的。无论如何,将3层作为独立的进程讨论有助于使概念更清晰,同时也可强调每一层的独立性。
    在这里插入图片描述
  • 另一个假设是:如果机器A用一个可靠的、面向连接的服务向机器B发送一个长数据流(先不考虑B同时向A发送数据的情形)。假定A要发送的数据总是已经准备好,不必等待这些数据被生成。或者说,当数据链路层请求发送数据时,网络层总能立即满足数据链路层的要求。还应假设机器不会崩溃。
  • 数据链路层从网络层获取的数据包是纯粹的数据,数据包被完整地交到目标机器的网络层,虽然目标机器网络层可能将数据包的一部分解释为一个头,但这不属于数据链路层考虑的范围。
  • 假设有一个现成的代码库,其中to_physical_layer发送一帧,from_physical_layer接收一帧,这个过程负责核算附加校检和(这部分工作通常由硬件完成,例如它们可能使用CRC算法),所以我们无需关心数据链路层协议的这部分内容。目标机器数据链路层等待数据用过程调用wait_for_event(&event)标示,当确实发生了事情(比如到达了一个帧),过程返回并由event说明发生了什么事情。对于不同的协议,可能的事件集合也是不同的,每个协议都需要单独定义和描述事件集合。在实际中,数据链路层不会在一个严格的循环中等待事件,而是会接收一个中断;中断将使它终止当前工作,转而处理入境帧,为了简便起见,将忽略数据链路层内部所有并发进行的活动细节,假定它全部时间都在处理一个信道。
  • 当一帧到达接收方,校检和被重新计算。如果计算出的帧校检和不正确,则数据链路层会收到通知(event = cksum_err)。如果到达的帧没有任何损坏,数据链路层将收到通知(event = frame_arrival),因此它可以利用from_physical_layer得到该帧并处理。只要接收方的数据链路层获得了一个完好无损的帧,它就检查头部的控制信息;如果一切没有问题,它就将内嵌的数据包传递给网络层。(为什么网络层得不到任何帧头信息?自然是为了保持网络层和数据链路层的完全分离。当数据链路层协议和帧格式发生变化时,网络层软件可以不作任何改变。那么当一块新的NIC安装在计算机上时就没有任何问题)。
  • 图中给出了将要讨论的许多协议公用的一些声明(C语言)。这里定义了5个数据结构:boolean, seq_nr,packet, frame_kind和frame。boolean是一个枚举类型,可以取值true和false;seq_nr是一个小整数,用来对帧进行编号,这些序号从0开始,一直到MAX_SEQ(含),每个用到序号的协议都要定义它;packet是同一台机器上网络层和数据链路层之间,或者不同机器上的网络对等实体之间交换的信息单元。在我们的模型中,它总是包含MAX_PKT个字节数据,实际中它是可变的。一个帧由四个字段组成:kind,seq,ack和info,其中前三个包含了控制信息,最后一个可能包含了要被传输的实际数据,这些控制字段合起来称为帧头。kind字段指出来帧中是否有数据,因为有些协议需要区分只有控制信息的帧和同时包含控制与数据信息的帧。seq和ack分别用作序号和确认。数据帧的info字段包含了一个数据包;控制帧的info字段没有用处(某些实际的协议实现将会使用一个变长的info字段,而对于控制帧则完全忽略)。网络层从传输层获得一个报文,然后在该报文上增加一个网络层头,由此创建了一个数据包。该数据包被传递给数据链路层,然后被放到输出帧的info字段中。
#define MAX_PKT 1024 /* determines packet size in bytes */
typedef enum {false, true} boolean; /* boolean type */
typedef unsigned int seq_nr; /* sequence or ack numbers */
typedef struct {unsigned char data[MAX_PKT];} packet; /* packet definition */
typedef enum {data, ack, nak} frame_kind; /* frame kind definition */
typedef struct { /* frames are transported in this layer */
frame_kind kind; /* what kind of frame is it? */
seq_nr seq; /* sequence number */
seq_nr ack; /* acknowledgement number */
packet info; /* the network layer packet */
} frame;
/* Wait for an event to happen; return its type in event. */
void wait_for_event(event_type *event);
/* Fetch a packet from the network layer for transmission on the channel. */
void from_network_layer(packet *p);
/* Deliver information from an inbound frame to the network layer. */
void to_network_layer(packet *p);
/* Go get an inbound frame from the physical layer and copy it to r. */
void from_physical_layer(frame *r);
/* Pass the frame to the physical layer for transmission. */
void to_physical_layer(frame *s);
/* Start the clock running and enable the timeout event. */
void start_timer(seq_nr k);
/* Stop the clock and disable the timeout event. */
void stop_timer(seq_nr k);
/* Start an auxiliary timer and enable the ack timeout event. */
void start_ack_timer(void);
/* Stop the auxiliary timer and disable the ack timeout event. */
void stop_ack_timer(void);
/* Allow the network layer to cause a network layer ready event. */
void enable_network_layer(void);
/* Forbid the network layer from causing a network layer ready event. */
void disable_network_layer(void);
/* Macro inc is expanded in-line: increment k circularly. */
#define inc(k) if (k < MAX_SEQ) k = k + 1; else k = 0
  • 图中还列出了许多过程,这些库程序的细节与具体实现有关。wait_for_event是一个严格的循环过程,它等待事情的发生。过程to_network_layer和from_network_layer被数据链路层用来向网络层传递数据包或者从网络层接收数据包(即这些方法用来处理第二层和第三层的接口);from_physical_layer和to_physical_layer在数据链路层和物理层之间传递帧(处理第一层和第二层的接口)。在大多数协议中,我们假设信道是不可靠的,并且偶尔会丢失整个帧,所以我们需要启动一个内部计时器,如果在预设的时间间隔内没有回答,则时钟超时,数据链路层会收到一个中断信号。
  • 协议中如此实现:让wait_for_event返回event=timeout;过程star_timer和stop_timer分别打开和关闭计时器。在计时器运行同时,允许显示地调用star_timer重置时钟。过程star_ack_timer和stop_ack_timer控制一个辅助计时器,该定时器被用于在特定条件下产生确认。过程enable_network_layer和disable_network_layer用在较为复杂的协议中,在这样的协议中,我们不再假设网络层总是有数据发送。当数据链路层启动网络层后,它就允许网络层在有数据包发送时中断自己,用event=network_layer_ready来表示这种状态;当网络层被禁用后,这样的事情就不会发生。通过合适的时机启用禁用网络层,数据链路层就能有效避免网络层用大量数据包把自己淹没。
  • 帧序号总是在0到MAX_SEQ的范围内,不同协议的MAX_SEQ可以不相同。通常有必要对序号按循环加一处理(即MAX_SEQ之后是0)。宏inc可以执行这项序号递增任务,之所以把这项工作定义成宏是因为在帧处理的关键路径上都要用到该参数。限制网络性能的因素通常在于协议处理过程,所以把这种简单操作定义成宏并不会影响代码的可读性,却能够提高性能。上图代码中的声明是将要讨论的每个协议的一部分,为了节省空间和方便引用被提取出来排列在一起;在C语言中,合并的方法是,把这些定义放在一个特殊的头文件中,这里是protocol.h文件,然后在文件中使用C预处理器的#include设施将这些定义包含起来。

1.1一个乌托邦式的单工协议

  • 先考虑一个最简单的协议,它不需要考虑任何出错的情况。在这个协议中,数据只能单向传输。发送方和接收方的网络层总是处于准备就绪状态。数据处理的时间忽略不计。可用的缓存空间无限大。数据链路层之间的通信信道永远不会损坏帧或者丢失帧。实现如下。
/* Protocol 1 (Utopia) provides for data transmission in one direction only, from
sender to receiver. The communication channel is assumed to be error free
and the receiver is assumed to be able to process all the input infinitely quickly.
Consequently, the sender just sits in a loop pumping data out onto the line as
fast as it can. */
typedef enum {frame_arrival} event_type;
#include "protocol.h"
void sender1(void)
{
frame s; /* buffer for an outbound frame */
packet buffer; /* buffer for an outbound packet */
while (true) {
from_network_layer(&buffer); /* go get something to send */
s.info = buffer; /* copy it into s for transmission */
to_physical_layer(&s); /* send it on its way */
} /* Tomorrow, and tomorrow, and tomorrow,
Creeps in this petty pace from day to day
To the last syllable of recorded time.
– Macbeth, V, v */
}
void receiver1(void)
{
frame r;
event_type event; /* filled in by wait, but not used here */
while (true) {
wait_for_event(&event); /* only possibility is frame arrival */
from_physical_layer(&r); /* go get the inbound frame */
to_network_layer(&r.info); /* pass the data to the network layer */
}
}
  • 协议由两个单独的过程组成:一个发送和一个接收。发送过程运行在源机器的数据链路层上;接收过程运行在目标机器的数据链路层上。这里没有用到序号和确认,不需使用MAX_SEQ。唯一的事件类型是frame_arrival(即到达了一个完好无损的帧)。发送过程是一个无限的while循环,它尽可能快速地把数据放到线路上,循环体由三个动作组成:从网络层获取一个包、利用变量s构造一个出境帧,然后通过物理层发送该帧。这个协议只用到了帧结构中的info字段,因为其他字段都跟差错控制或者流量控制有关。接收过程很简单。开始时,它等待到达一个完好的帧;到达后,过程wait_for_event返回,其中的参数event被设置成frame_arrival;调用from_physical_layer将新到达的帧从硬件缓存区中删除,并放到变量r中,以便接收方的代码访问该帧;最后该帧的数据部分被传递到网络层,数据链路层返回继续等待下一帧的到来,即实际上它把自己挂起来,直到下一帧到来为止。
  • 乌托邦式的协议是不现实的,因为它不处理任何流量控制或者纠错工作。其处理过程接近于无确认的无连接服务,必须依赖更高层次来解决上述问题,即使无确认的无连接的服务也要做一些差错检测工作。

1.2无错信道上的单工-等式协议

  • 发送方以高于接收方能处理到达帧的速度发送帧,导致接收方被淹没,这种情形很容易出现。假设信道不会出错,数据流量还是单工。
  • 一种解决方法是建立足够强大的接收器,使其强大到能处理一个接着一个帧组成的连续流(或等价于把数据链路层定义得足够慢,慢到接收器的处理速度完全跟得上)。它必须有足够大的缓冲区和以线速运行的处理能力,而且必须能足够快地把所接收到的帧传递给网络层。然而这种方案需要专用的硬件,而且如果链路的使用率过低将浪费资源;这种方案只是发发送方太快这个问题转移到了其他地方,这里是网络层。
  • 更一般化的解决方案是让接收方给发送方提供反馈信息。接收方将数据包传递给网络层之后给发送方返回一个小的哑帧,实际上这一帧的作用是给发送方一个许可,允许它发送下一帧。发送方在发出一帧后,根据协议要求,它必须等待一段时间直到短哑帧(确认)到达,这种延缓就是流量控制协议的一个简单例子。发送方发送一帧,等待对方确认到达后才能继续发送,这样的协议称为停-等式协议。实现如下。
/* Protocol 2 (Stop-and-wait) also provides for a one-directional flow of data from
sender to receiver. The communication channel is once again assumed to be error
free, as in protocol 1. However, this time the receiver has only a finite buffer
capacity and a finite processing speed, so the protocol must explicitly prevent
the sender from flooding the receiver with data faster than it can be handled. */
typedef enum {frame_arrival} event_type;
#include "protocol.h"
void sender2(void)
{
frame s; /* buffer for an outbound frame */80
packet buffer; /* buffer for an outbound packet */
event_type event; /* frame arrival is the only possibility */
while (true) {
from_network_layer(&buffer); /* go get something to send */
s.info = buffer; /* copy it into s for transmission */
to_physical_layer(&s); /* bye-bye little frame */
wait_for_event(&event); /* do not proceed until given the go ahead */
}
}
void receiver2(void)
{
frame r, s; /* buffers for frames */
event_type event; /* frame arrival is the only possibility */
while (true) {
wait_for_event(&event); /* only possibility is frame arrival */
from_physical_layer(&r); /* go get the inbound frame */
to_network_layer(&r.info); /* pass the data to the network layer */
to_physical_layer(&s); /* send a dummy frame to awaken sender */
}
}
  • 虽然这个例子中的数据流量是单工的,即只是从发送方传到接收方,但是帧可以在两个方向上传送。因此,两个数据链路层之间的通信信道具备双向传输信息的能力。然而,这个协议限定了流量的严格交替关系:首先发送方发送一帧,然后接收方发送一帧:接着发送方发送另一帧,然后接收方发送另一帧,以此类推。这里采用一个半双工的物理信道就足够了。
  • 就像在协议1中那样,发送方首先从网络层获取一个数据包,用它构造一帧,然后发送出去。但现在,与协议1不同的是,发送方在开始下一轮循环从网络层获取下一个数据包之前必须等待确认帧的到来。发送方的数据链路层甚至根本不检查接收到的帧,因为只可能是来自接收方的确认。

1.3有错信道上的单工停-等式协议

  • 假设一帧在传输过程中被损坏,那么接收方应该在完整地接收到该帧才返回一个确认帧,否则就将帧丢弃,一段时间后,发送方的计时器超时,于是它再次发送该帧。但是如果确认帧丢失了,那么发送方就会传输一个重复帧,显然接收方需要加以区分。对于接收方来说,区分重复帧很显然的做法是让发送方在它发送的每个帧的头部放上一个序号,然后接收方可以检查它所接收到的每个帧的序号。
  • 因为协议必须正确,并且出于链路效率的考虑,包含在帧头中的序号字段可能很小,那么序号需要多少位表示?具体取决于具体的链路层协议,它们需要保证帧携带的序号必须足够大到保证协议能正确工作。在这个协议中(如下代码),当正确帧到来时,帧被传递到网络层;然后接收方期待的下一个序号模2增加1(即从0到1,从1到0),任何一个到达的帧,如果包含了错误序号都将作为重复帧被拒绝接收,不过最后一个有效的确认需要重复,以便发送方确认已经被接收的帧。这样的协议称为自动重复请求或带有重传协议的肯定确认,这类协议也只在一个方向上传输数据。
/* Protocol 3 (PAR) allows unidirectional data flow over an unreliable channel. */
#define MAX_SEQ 1 /* must be 1 for protocol 3 */
typedef enum {frame_arrival, cksum_err, timeout} event_type;
#include "protocol.h"
void sender3(void)
{
seq_nr next_frame_to_send; /* seq number of next outgoing frame */
frame s; /* scratch variable */
packet buffer; /* buffer for an outbound packet */
event_type event;
next_frame_to_send = 0; /* initialize outbound sequence numbers */
from_network_layer(&buffer); /* fetch first packet */
while (true) {
s.info = buffer; /* construct a frame for transmission */
s.seq = next_frame_to_send; /* insert sequence number in frame */
to_physical_layer(&s); /* send it on its way */
start_timer(s.seq); /* if answer takes too long, time out */
wait_for_event(&event); /* frame arrival, cksum err, timeout */
if (event == frame_arrival) {
from_physical_layer(&s); /* get the acknowledgement */
if (s.ack == next_frame_to_send) {
stop_timer(s.ack); /* turn the timer off */
from_network_layer(&buffer); /* get the next one to send */
inc(next_frame_to_send); /* invert next frame to send */
}
}
}
}
void receiver3(void)
{
seq_nr frame_expected;
frame r, s;
event_type event;
frame_expected = 0;
while (true) {
wait_for_event(&event); /* possibilities: frame arrival, cksum err */
if (event == frame_arrival) { /* a valid frame has arrived */
from_physical_layer(&r); /* go get the newly arrived frame */
if (r.seq == frame_expected) { /* this is what we have been waiting for */
to_network_layer(&r.info); /* pass the data to the network layer */
inc(frame_expected); /* next time expect the other sequence nr */
}
s.ack = 1 − frame expected; /* tell which frame is being acked */
to_physical_layer(&s); /* send acknowledgement */
}
}
}
  • 当发送方和接收方的数据链路层处于等待状态时,两者都用一个变量记录下有关的值。发送方在next_frame_to_send中记录下一个帧的序号;接收方则在frame_expected中记录下一个期望收到的序号。每个协议在进入无限循环之前都有一个简短的初始化阶段。发送方在发出一帧后启动计时器,如果计时器已经在运行,则将重置,以便等待另一个完整的超时时间间隔。发送方等待下一事件的发生:确认帧完好无损返回,确认帧损坏或者计时器超时。如果到达一个有效确认帧,则发送方从它的网络层获取下一个数据包,并把它放入缓冲区覆盖掉原来的数据包,同时还要递增帧的序号;如果到达了一个受损的确认帧或者超时,则缓冲区和序号都不做任何改变,以便重传。当有效帧到达接收方时,接收方首先会检查它的序号,确定是否为重复数据包或者受损:如果不是则传递到网络层,并生成一个确认帧,否则不会传递到网络层。

猜你喜欢

转载自blog.csdn.net/ao__ao/article/details/85044108