数据链路层概述

 

一.定义

 

1:链路是指从一个节点到另一个节点的纯物理线路,而中间没有其他任何节点。

 

2:数据链路:在链路的基础上添加了实现通信协议的硬件和软件就是数据链路。

 

3.数据链路层以帧为单位处理和传输数据。

二.数据链路层的三个重要问题

 

1.封装成帧:

  数据链路层给从网络层下来的网络层协议数据单元添加一个帧头,添加一个帧尾,这个操作就叫做封装成帧。添加帧头帧尾的目的是为了在链路上以帧为单元传送数据。

2.差错检测:

  数据链路层通过物理层把封装好的帧发送给传输媒体,但是在传输媒体中可能出现误码,也就是0变1,1变0,所以为了让接收方知道是否误码,需要在数据帧的尾部添加一个检错码,这个检错码是发送方根据差错检测算法和待发送数据算出来的。接受方通过检错码和相应算法得知是否出现误码的过程就叫做差错检测。

3.可靠传输:

  如果接收方发现数据出现误码,就会将数据帧丢弃。因为是可靠传输,所以需要其他措施来确保接收方会重新收到被丢弃的这个帧的正确副本。换句话说,因为误码是不能完全避免的,所以如果实现了发送方发送什么,接收方就收到什么,那么我们就称之为可靠传输!

三.数据链路层的互连设备

1.网桥和交换机的工作原理

2.集线器(物理层设备)和交换机的区别

 

上面因为是概述,所以写的比较简略,下面我们开始逐一深入总结。

 

一.封装成帧

1.帧的定界符

数据链路层通过物理层将构成帧的各比特转化成电信号,然后再发送到传输媒体,但是接收方的数据链路层如何从一串比特流中提取出一个一个帧呢?它是怎么清楚一个帧的开头和结尾的呢?其实帧头帧尾的作用之一就是帧定界,在帧头帧尾中各含一字节的标志字段。

值得说明的是,并不是所有的数据链路层协议都有帧定界标志,例如在以太网v2的mac帧中就没有帧定界标志。

链路层 帧收发 python 链路层数据帧_数据链路层

 

物理层在这种帧前面添加上前导码,通过前导码来实现帧开始定界符的作用,而且规定了帧间间隔时间为96比特时间,所以帧结束定界符的作用也能实现了。

2.防止数据被当作定界符的措施

首先我们先来说一下透明传输的概念,透明传输是指网络层不受数据链路层的任何限制,可以像数据链路层传输任意数据,就好像数据链路层根本不存在一样。

透明传输是网络分层的前提,是必须要实现的特性。

可是如果我们按照上面的帧定界符来划分帧的首尾,那么一旦有数据和定界符相等,是不是就会被误认为定界符而产生错误?可能有人会想到,只要命令网络层坚决不发送和定界符相同的数据不就好了。但这样会打破透明传输,使链路层对网络层产生影响,那么这样的链路层就是毫无意义的,因此这种方案不可行。通常数据链路层会通过字节填充和零比特填充的方法来消除这种影响。

对于面向字节的物理链路应该使用字节填充的方法来实现透明传输,即如果数据中出现帧定界符或者转义字符那么就在它前面添加一个转义字符,这样接收方看见转义字符就知道后面那个字符是数据,不会产生误会。

对于面向比特流的物理链路应该使用零比特填充的方法来实现透明传输,即如果连续出现五个1那么就在其后面添加一个0,这样接收方在连续收到五个一后把后面的零去掉即可,同样可以不产生误会。

二.差错检测

1.奇偶校验:

首先双方先约定采用奇校验或偶校验,如采用奇数校验那么就在比特串后面添加一个1,使1的个数为奇数,如果接收方收到数据后发现1的个数为偶数则说明数据无误,偶校验以此类推。

这种校验方式很明显的会导致准确率不高,因为只要偶数个比特位错误,就检查不出来,所以奇偶校验很少用。

2.crc循环冗余校验。

(1)双方先约定好一个生成多项式G(X).

(2)发送方基于待发送的数据和生成多项式生成校验码(冗余码),并将其添加到后面一起传输。

(3)接收方收到数据后通过生成多项式来计算是否产生误码。

文字可能描绘不清楚,我用两幅图来描述接收双方的行为:

链路层 帧收发 python 链路层数据帧_数据链路层_02

链路层 帧收发 python 链路层数据帧_数据链路层_03

 

循环冗余校验码有很好的检错能力,漏检率很低,因此在数据链路层被广泛使用。

三.可靠传输

可靠传输有三种实现机制,要一一总结,所以篇幅会有点长,做好准备,冲冲冲!

1.停止等待协议(stop-and-wiat)

链路层 帧收发 python 链路层数据帧_数据链路层_04

  如上图所示,发送方向接收方发送数据,如果接收方成功收到并且验证无误后,会发送ACK给发送方确认收到,这是发送方才可以接着往下发送,如果有误码,那么就会发送NAK,发送方收到后就会重传数据,知道发送方收到ACK才会接着向下进行。

这么看上去是不是好像很简单?然而真的只有这样吗,如果DATA没有产生误码而是直接丢失掉那该怎么处理呢?就像下图所示:

链路层 帧收发 python 链路层数据帧_链路层 帧收发 python_05

 

 

  别怕,我们还有超时重传机制:发送方发送完一个分组后会启动一个超时计时器,如果到了所限制的时间,发送方仍然没有收到来自接收方的的ACK或者NAK那么就会自动重传。一般计时器设置时间为从发送方到接收方的平均往返时间。

  那么加上超时重传机制就完美了吗?不,还差点东西,DATA数据都可以丢失了,那么ACK和NAK确认当然也可以丢失了,如下图所示:

链路层 帧收发 python 链路层数据帧_重传_06

  所以如果ACK或NAK丢失,那么一定会出发超时重传机制,这就会导致分组重复发送,这不是我们希望看到的。因此,我们通过给每个分组带上一个序号来避免重复发送。对于停止等待协议由于每次发送后都会等待一段时间,所以只要保证每次发送的分组与上一次的不同即可,因此,只需一个比特位来用作序号,即序号只有0和1就够了。当收到连续的序号时,接收方将会丢弃该分组,并向发送方返回ACK确认,从而使发送方接着往下发送。

  既然DATA都要加序号确认了,那么ACK和NAK需不需要呢,我们看下图:

链路层 帧收发 python 链路层数据帧_数据链路层_07

  如果ACK在发送时迟到了,那么发送方就会重传DATA0,这时会有两个ACK返回,第一个ACK告诉发送方可以发送DATA1了,这是正确的,但是第二个ACK其实是多余的,但发送方会把它当成对DATA1的确认,从而产生BUG,所以ACK和NAK也要加上一个比特的序号来解决确认迟到和重复确认的问题!

因为数据链路层的点对点信号往返时间很固定,确认序号不会迟到,所以如果只是在数据链路层实现停止等待协议就不需要给ACK加序号防止重复了。

  我们再来看一下停止等待协议的信道利用率,U=TD/(TD+RTT+TA),其中TD为发送数据的时间,RTT为往返时间,TA为发送确认的时间,由于TA过小,所以一般忽略。可以看出,RTT一定是远远大于TD的所以信道利用率十分低,如果在发生重传什么的,就雪上加霜了,所以为了缓解这个问题,又出现了后退N帧协议(GBN),和选择重传协议(SR).

2.后退N帧协议

  停止等待协议信道利用率低最最主要的原因是他等待的时间太长了,如果采用流水线式发送,也就是可以有多个分组连续发送,如下图:

链路层 帧收发 python 链路层数据帧_重传_08

 

   那么信道利用率自然就提升了。而我们要说的后退N帧协议,正是在流水线发送的基础上演变的,它规定了一个发送窗口,和k位序号来防止重复,发送窗口的大小是(1~2^k-1),如果超过了这个大小那么接收方将会无法辨析旧分组的数据。而接收方的窗口大小只能为1.

  发送方可以在未收到接收方确认的基础上把发送窗口内的数据全部发送出去,接收方每收到一个当前窗口内的并且无误码的数据就将窗口往后挪一个位置,同时为了减少开销,接收方不一定每收到一个分组就发一个确认,可以发送一个累计确认,就代表在这个分组序号前的所有分组都已接到,这样做不仅减少了发送确认的开销,还减少了分组重传的概率。另外,如果收到了未按序到达的分组后,除了要将其丢弃,还要把最近按序收到的分组进行确认。

  发送方收到确认后会把窗口向后移动,并且如果收到多个重复确认时可以直接开始重传而不需要等待超时计时器,发送窗口某个分组发生超时重传后在其后面并且也在这个窗口的分组也必须要进行重传,这就是后退N帧协议(GO-BACK-N)

3.选择重传协议

  由于后退N帧协议的接收方窗口只能为1,所以只能按序接受数据分组,一个分组的误码或者丢失,会导致后面多个分组不能被接受而被丢弃,这显然是对计算机资源的浪费,所以能不能把接收窗口变大一大呢?

  通过加大接收窗口就可以先收下那些按需到达而且无误码的分组,等到缺失的分组补齐后在向上提交,这样就可以只重传丢失或者误码的分组,又进一步的提高了资源利用率,这就是选择重传协议的概念。需要注意的是,采用了选择重传后为了能只重传出错的分组,就不能使用累计确认了,必须要对收到的分组逐一确认。

  选择重传协议两种窗口的大小范围:1<发送窗口<=2^(k-1),1<接收窗口<=发送窗口,如果发送窗口大于指定范围那么可能造成重复接收相同分组。