本文引用代码及图片均来自 高军: 计算机网络
数据链路层主要解决主机编址以及数据分组的格式问题,使得数据可以在一个网络内流动
链路层涉及以下三个重要概念:
- 封装成帧
- 差错检测
- 可靠传输
本文介绍一和二部分
封装成帧
链路层以帧为单位向下层提供数据,它在网络层提供的数据首尾分别添加帧头和帧尾以便将数据封装成帧,帧头和帧尾中包含控制信息
(如目的地址、差错检验码等)
不同协议帧头和帧尾不同,比如在以太网V2版本的MAC(Media Access Control)帧
中,前三个字段是帧头,在PPP(Point to Point Protocol)帧
中前四个字段是帧头。帧头和帧尾中间包着的就是网络层交付下来的数据,称为载荷
帧边界
物理层将帧所代表的比特流转换成电信号发送出去,接收端接收到信号后再将其转换成比特流,那么接收端如何从比特流中区分出一个个的帧呢?
实际上帧头和帧尾的其中一个作用就是帧定界
,如PPP帧
中首尾字节都是标志字节
,接收端检测到相应的连续比特后就能确定帧的边界
在MAC帧
中没有标志信息,物理层会为MAC帧
添加前导码
,前导码前七个
字节用于同步时钟,最后一个
字节用作起始
标志。另外,由于MAC帧
被规定有96比特
的帧间间隔
,所以它不需要
结束标志。当然,帧间间隔并不是
专门用于省掉这个结束标志的,这个间隔时间可以让接收端有时间执行清除
之前的帧缓存之类的操作
转义字符
如果帧载荷里有和起止标志(flag)
一样的数据怎么办?比如某个帧以0001
为起止标志,如果载荷里也有0001
,那么接收端在遇到第二个0001
时会误认为
数据到达了帧的结束边界从而提前结束接收数据导致帧错误
链路层不能要求上层交付的数据中不包含和起止标志相同的信息,因为要发送什么数据是上层的自由,那么,这个问题只能在链路层解决
链路层检查载荷,如果出现和标志一样的数据就在其前面添加转义字符 ESC(Escape character)
,接收端在遇到转义字符的时候就知道接下来的标志信息是假
的,此时不会提前结束,同时会将转义字符丢弃
。如果载荷里有和转义字符一样的数据呢?也同样在前面添加转义字符就行了
上述字符填充
方案是面向字节
的链路层协议使用的,对于面向比特
的协议,它们使用的是比特填充
,比如起止位组合
为01111110
时,扫描载荷,每出现连续的5个1就填充0
,接收端则将载荷中的每五个1后面的0丢弃
通过填充数据,链路层就解决了帧边界的识别问题
其他
像这样不对上层交付的数据做约束
的行为其实还涉及到透明传输
的概念
透明传输是指数据链路层对上层交付的传输数据没有任何限制,就好像链路层不存在一样
在传输时为了提高效率
一般都要求帧的载荷部分尽可能大
,载荷越大帧的首尾部占的比重就越小。当然了,这个大也是有限度的,每种协议中规定了载荷部分的长度上限,即最大传送单元 MTU (Maximum Transfer Unit)
差错检测
通信链路由于本身的问题或外界的干扰在传输数据的过程中有可能出现差错
,比如0
比特在传输过程中变成了1
,这称为比特差错
,一段时间内传输错误的比特数占总传输比特数的比率称为误码率BER(Bit Error Rate)
链路层怎么知道接收到的数据是否有误
呢?其实它可以通过帧尾中的 FCS(Frame Check Sequence 帧校验序列)
和差错检测算法
来判断数据是否有误。FCS
是根据帧头和数据载荷
算出来的,接收方根据接收到的数据再算一遍然后对比即可
下面简单介绍两种差错检测方法:
- 奇偶校验
PC(Parity Check)
- 循环冗余校验
CRC(Cyclic Redundancy Check)
奇偶校验
奇偶校验
就是在待发送数据后面添加一位奇偶校验位
,使整个数据(包括所添加的校验位)中比特1
的个数为奇数(奇校验)
或偶数(偶校验)
奇校验位由待发送数据逐比特异或
后的值再和1(奇校验)
或0(偶校验)
异或得到,例:
待发送数据:101101
奇校验位 = 1⊕0⊕1⊕1⊕0⊕1⊕1 = 1
偶校验位 = 1⊕0⊕1⊕1⊕0⊕1⊕0 = 0
复制代码
接收方只需对数据整体(包括校验位)进行逐位异或看结果是否等于1(奇校验)
或0(偶校验)
即可
奇偶校验实现起来很简单,但是,当出现多位
错误时可能无法检出错误,漏检率
很高,因此实际上很少使用
循环冗余检测
循环冗余检测需要先选定一个多项式
,然后根据多项式和数据计算出冗余码
,最后将差错检验码拼接到数据尾部
发送方 | 接收方 |
---|---|
构造被除数:待发送数据后面添加生成多项式最高次数个0 | 构造被除数:接收到的数据就是被除数 |
构造除数:生成多项式各项系数构成的比特串 | 构造除数:生成多项式各项系数构成的比特串 |
做除法:异或 | 做除法:异或 |
检查余数:余数位数应于生成多项式做高次数相同,不够则补0 | 检查余数:余数为0则传输无误,否则有误码 |
下面直接以实例说明如何计算冗余码
,假设待发送数据为101001
,生成多项式为
- 构造被除数:多项式最高次为
3
,直接在待发送数据后面补3个0
共同构成被除数101001000
- 构造除数:多项式各项系数分别为:1 1 0 1,构成除数
1101
- 做除法:二进制除法就是
异或
- 检查余数:余数为
1
,多项式最高次为3
,1
前面补2个0
构成余数(冗余码)
下面的例子多项式不变,接收方接收到的数据为101101001
,该例算出来余数不为0,表明检测出错误
循环冗余检测的漏检率
很低,虽然计算复杂,但很容易用硬件实现,因此被广泛用于链路层
链路层检测到帧有错误就直接丢弃该帧,如果链路层提供的是不可靠服务,那么帧丢弃后不会有其他动作。但如果链路层需要提供可靠服务,那么就需要通过其他手段通知发送方重新发送有误的帧
尽管误码不可避免,但若能实现发送方发送什么,接收方就能收到什么,就称为可靠传输