在这个视频中,我们要探讨数据链路层组帧的原理。
我们说数据链路层在网络层的下方,它为网络层提供服务,网络层会把 IP 数据报,也就是分组,交给数据链路层,请求数据链路层,然后传送给下一个相邻的节点,下一个相邻节点的数据链路层,需要从收到的帧当中恢复 IP 数据报的原始数据,然后把IP 数据包交给第三层的实体进行下一步处理。数据链路层把网络层交给他的这些数据打包成帧的过程就是所谓的组帧,也就是封装成帧。考研大纲里边使用的是组帧这个说法。
要把数据封装成帧,主要要解决两个问题。第一个问题就是帧定界,也就是我们应该想办法让数据的接收方能够确定帧的界限。因为我们知道物理层在进行数据传输的时候,是以比特为单位来传输的,如何从一系列二进制串当中区分出帧和帧的边界呢?这就是帧定界要解决的问题。为了完成帧定界,数据的发送方会把网络层的这些数据进行一系列的处理,比如说增加首部,增加尾部,同时,还有可能对这个 IP 数据报里边的一些比特进行一些变换处理。因此,接收数据的这个节点,它的数据链路层。除了区分帧和帧之间的边界之外,它还需要从一个帧内部还原出IP数据报的原始信息,并且递交给第三层网络层进行处理,这就意味着网络层和网络层实体之间感知不到数据链路
层对 IP 数据报做的这些处理和逆处理,也就是说组帧的这个动作对于网络层而言是透明的,看不见的。所以实现帧定界的同时,我们也要实现透明传输,用大白话讲,就是说数据的接收方要能够去除帧定界的附加信息。把帧的数据部分,也就是 IP 数据报给恢复原貌,这就是透明传输。接下来我们会介绍字符计数法、字节填充法、零比特填充法和违规编码法这四种组帧的方法。
首先来看第一种组帧的方法,字符计数法,这种方法的思想很简单。假设灰色的这些部分是帧的数据部分。那么,我们会在一个针的首部用一个固定长度的计数字段,去表示帧的总长度,比如我们可以用八个比特,也就是一个字节去表示整个帧的长度。那么,这八个比特翻译成十进制,就应该等于七,也就意味着一整个帧的大小是七个字节。需要注意的是,帧的数据部分本身只有六个字节,我们记录的总长度是包含首部这一个字节的总长。所以需要把这个计数字段的长度给加进去,显然这个计数字段的比特数会影响整个帧的最大长度。比如八个比特可以表示的无符号数范围就应该是零到 255,如果我们只用一个字节去表示帧的总长度。那么整个帧的最大长度就不能超过 255。另一点值得一提的是,采用这种字符计数法,有一个最大的缺点,任何一个计数字段出错都会导致后续所有的帧都无法定界。
比如在刚才这个例子中,原本第一个字节的值等于七,现在假设中间的这个比特发生了跳变,从1变成了0。那么,第一个字节的值就从七变成了三,在这种情况下,数据的接收方会误以为第一个针的总长度是三个字节,同时会把第四个字节,视为第二个帧的长度。
这串二进制数对应十进制的 27,这也就意味着数据的接收方会认为从这个字节开始,往后的27个字节属于第二个帧,可以看到由于第一个帧的长度信息,发生了一个比特的错误,就使得后面所有帧的边界都被打乱,就像多米诺骨牌一样,第一块骨牌倒了,后面的骨牌也全部会被推倒。所以这种字符计数法的健壮性不太好。这是第一种组帧的方法,字符计数法。
接下来我们看第二种组帧的方法,字节填充法。这种方法也很简单,我们会约定两个特殊的字符,用SOH和EOT这两个字符分别去标记一个帧的起始和结束的位置。这两个字符都属于ASCII编码的控制字符。SOH的意思是 Start of header,也就是首部的起始这个意思。它对应的ASCII编码就是七个0+1一个1。如果用 16 进制表示,那就是01H。相应的EOT的全称叫做End of Transmission,表示传输结束。它在ASCII编码当中对应的二进制就是00000100,如果用 16 进制表示就应该是04H。总之SOH和EOT属于ASCII编码里边的控制字符。
现在新的问题发生了,当我们使用这两个特殊的字符作为帧的开始和结束标记的时候。如果一个帧的数据部分里边刚好包含SOH和EOT这两个特殊字符。那么,是不是就会导致帧定界出现错乱?比如绿色的这个是EOT字符,那么数据的接收方是不是会误认为前三个字节属于第一个帧。但事实上不是这样的,EOT字符,它属于帧的数据部分。为了解除这种误解,我们可以引入转义字符,就是ESC这个字符。ESC的全称是Escape character,就是转义字符的意思。
在ASCII编码当中ESC字符对应00011011,如果用 16 进制表示应该是对应1BH。在帧的数据部分绿色的是EOT字符,蓝色的这个是SOH字符。只要数据部分包含这两个特殊的字符数据的发送方,就会在他们之前插入一个转义字符ESC,这样的话,当数据的接收方检索到一个ESC转义字符的时候,他就会知道这个转义字符后面跟着的并不是控制字符,而是一串普通的数据。于是,数据的接收方可以把这个转义字符给剔除。同时也不会把后面这个字符误以为是真的开始或者结束。所以插入转义字符可以解决对EOT和SOH的误解。现在问题看似解决了,不过当我们引入转义字符之后,又有可能出现数据部分包含转义字符的这种情况。
比如说中间的这一个比特,它刚好就是转义字符ESC。那么当数据的接收方检测到这个转义字符的时候,他是不是也会把这个转移字符删掉呢?如果是这么处理的话,显然也会有问题。这个问题的解决也不难,可以采取同样的思路,如果在一个帧的内部包含这种转义字符,我们可以在转义字符的前面再插入一个转义符。对于数据的接收方来说,它可以从第一个字符开始往后检索,如果发现 SOH,就意味着这是一个帧的开始,继续往后检索。当发现一个转义字符的时候,就可以知道这个转义字符的后面,它属于普通的数据,而不是一个控制字符。所以把这个转义字符去掉,把后面的这一个字节原模原样的接受下来,紧接着继续往后检索,如果又发现一个转义字符,那么同样的数据的接收方会认为,在这个转义字符的后面,这一个字节是属于数据部分,因此,接收方的数据链路层可以把前面这个转移字符删掉,并且原模原样的保留后一个字节。同样的道理,继续往后检索,每当发现一个转移字符的时候,就认为这个转移字符的后面属于数据部分,需要把它原模原样的接收,同时把前面这个转移字符给去除。这就是字节填充法。
如果帧的数据部分包含特殊字符,也就是包含开始、结束和转义这几个字符的话。那么,发送方的数据链路层会在这些特殊字符的前面填充转义字符ESC,然后再把这些比特发给数据的接收方,那数据的接收方需要做的就是逆处理,处理的方法刚才已经详细的说过。这就是第二种组帧方法,字节填充法。
接下来介绍零比特填充法,很简单。我们约定以一串特殊的二进制串作为帧的开始和结束。比如,在实际应用当中,通常会用01111110这样的八个比特,作为一个帧的开始和结束标记。显然这种方案会面临和上一种方案类似的问题。如果在这一个帧的内部刚好有一个0六个1,再加一个0的这种比特串,就会让数据的接收方误以为这是一个帧的开始或者结束。但我们知道,这些比特属于帧的数据部分。为了解决这个问题,发送方是这么做的:它会扫描帧的数据部分,每当遇到连续的五个1,就会填充一个0。
比如说从左往右扫,扫到这个位置的时候,出现了五个1。每当发现连续的五个1,就在第五个1的后面填充一个比特0。同样的道理。继续往后扫描,后面也会遇到五个1。那么,在这五个1的后面,又需要添加一个比特0。那么用这种方案填充了0比特之后,就可以确保一个帧的数据部分不可能和开始结束的比特串一样。因为开始和结束的比特串,它会有连续的六个1,而我们刚才对帧的数据部分处理的规则是从左往右扫描,只要扫描到连续的五个1,我们就强行填入一个比特0,也就是说把这些数据处理完之后,我们可以确保这个数据部分最多只会出现连续的五个1,而不可能出现连续的六个1。这样就可以保证数据部分不会出现开始和结束的这种特殊比特串。对于数据的接收方来说,它的处理也很简单。当数据的接收方检测到一个0加6 个1,再加一个0的时候,就知道这是一个帧的开始,当这一串二进制数再一次出现的时候,它就知道这个帧已经结束了。对于帧的内部数据,接收方的处理方法是从左往右扫描,每当遇到连续的五个1,就会把后面紧跟的这个0给删除,每当遇到连续的五个1,就会把后面紧跟的这个0删除,也就是做了发送方的逆处理。这样
就可以实现透明传输,我们可以确保数据的接收方能够把这个帧的数据部分恢复原样。这就是零比特填充法,这种方法在现代计算机网络当中非常常用,因为这个处理过程很容易用硬件快速、高效的去实现,有助于提升数据链路层的处理效率。数据链路层有两个协议的名字,大家需要记一下一个叫HDLC协议,另一个叫PPP协议,这两个协议的组帧使用的就是零比特填充法。关于这两个协议的具体原理,现在先不展开。现在我们只需要知道这两个协议使用的是零比特填充法来组帧就可以了。
接下来介绍最后一种组帧方法,叫做违规编码法。假设在物理层,我们采用曼彻斯特编码去传输二进制比特串。根据爱脆普一标准的曼彻斯特编码,我们需要观察每一个时钟周期的中间点,如果中间点是上跳,表示这是二进制0,下跳表示1。比如说这个帧的前四个比特是0100,观察周期,它是上跳表示0,第二个周期下跳表示1,第三个周期上跳表示0,上跳表示0,这是前四个比特,再来看整个帧的后三个比特是三个1,那么最后三个周期下跳表示1,下跳表示1,下跳表示1。中间的比特串都是同样的道理。基于曼彻斯特编码的规则,每一个时钟周期的中间点一定会发生信号的跳变,要么上跳,要么下跳,而如果中间点没有发生信号的跳变,就意味着不符合曼彻斯特编码的规定,也就是一个违规的信号。我们可以利用这一点,比如,在传送帧的第一个比特之前,先用一个时钟的违规信号去表示这是一个帧的开始,当一个帧结束的时候,我们也可以插入一个违规信号去表示这是一个帧的结束。显然,这种方法需要物理层的配合,发送方的数据,链路层会把整个帧的数据原模原样的交给物理层,物理层会在这些帧的前面和后面分别插入一个时钟周期的违规信号,这样的话,数据的接收方的物理层也可以根据这个违规信号去分辨出一个帧的边界在哪?这就是违规编码法,这种方法在现代计算机网络当中也非常常用。
这个视频中介绍了组帧的原理。我们说数据链路层组帧主要解决两个问题,第一就是要让数据的接收方能够区分出帧的边界在哪。第二就是要能够让数据的接收方去除帧定界的附加信息,把帧的数
据部分恢复原貌。我们介绍四种方法,分别是字符计数法,字节填充法,零比特填充法和违规编码法。
其中字符计数法,会在每一个帧的开头,用一个定长的计数字段去表示整个帧的总长,需要强调的是帧的总长度是包含计数字段的长度,再加上帧的数据部分的长度。这点做题的时候容易出错,大家需要注意。
第二种字节填充法在这个方法中,我们引入了三个特殊的字符,分别是标记帧开头的SOH字符,标记帧结尾的EOT字符,以及转义字符ESC。如果帧的数据部分包含这三个特殊字符,就需要在每一个特殊字符前面填充一个转义字符。这是发送方的数据链路层需要做的事情,接收方的数据链路层要做的就是逆处理,也就是要把转义字符给去掉。
第三种方法零比特填充法,我们会用一个特殊的比特串一个0加6 个1,再加一个0作为帧的开始和结束的标记。数据的发送方会检索帧的数据部分,每当遇到五个连续的1就填充一个0。这样就可以确保帧的数据部分不可能出现这种开始和结尾的特殊标记。数据的接收方要做的就是逆处理,每当遇到连续的五个1,就删掉后面紧跟的一个0。同时,我们要再次强调HDLC协议和PPP协议采用的就是零比特填充法。
最后我们还介绍了违规编码法,这种方法需要物理层的配合,物理层会在每个帧的开头和结尾插入一个不符合编码规则的违规信号,接收方的物理层就可以根据违规信号去判断每个帧的边界在什么位置。这就是四种常见的组帧方法,以上就是这一小节全部内容。