数据链路层
说明
- 为了向网络层提供服务,数据链路层必须使用物理层提供给它的服务
- 物理层的任务是接收一个原始的位流,并将它传输给模板机器
- 这个位流不保证没有错误,可能少于、等于、多于发送的位的数量
- 数据链路层负责检测和纠正位流中的错误
1.数据链路层设计要点
1.1.数据链路层特定功能
- 向网络层提供一个定义良好的服务接口
- 处理传输错误
- 调节数据量,确保慢速的接收方不会被快速的发送方淹没
1.2.为网络层提供的服务
- 无确认无连接的服务
- 源机器向目标机器发送独立的帧,目标机器并不对这些帧进行确认
- 事先并不建立逻辑连接,事后也不释放逻辑连接
- 帧丢失,数据链路层不做检测,也不做恢复
- 对于实时通信非常适合,实时通信中数据延迟比数据损坏更加严重
- 绝大多数LAN在数据链路层使用的是无确认无连接的服务
- 有确认无连接的服务
- 没有逻辑连接,所发送的每一帧都需要单独确认
- 适用于不可靠信道,比如无线系统
- 有确认有链接的服务
- 保证每一帧只被接收一次,所有的帧按照正确的顺序被接收
- 第一阶段:建立连接,双方初始化各种变量的计数器
- 第二阶段:一个或多个帧进行传输
- 第三阶段:连接释放,所有变量、缓冲区、其他用于维护该链接的资源释放;
1.3.成帧
- 字符计数法
- 利用头部的域指定该帧中的字符数
- 目标端数据链路层根据字符计数值确定后面跟着多少字符,即可确定该帧结束处
- 问题:计数值可能因为传输错误而弄乱,目标端失去同步,从而找不到下一帧起始位置;
- 在这种情况下,给源方发送一个请求重传的帧也无济于事,因为目标方不知道需要重传的起始位置
- 函字符填充的分界符法
-
让每一帧都用一些特殊的字节作为开始和结束(标志字节)
- 两个连续的标志字节代表当前帧的结束和下一帧的开始
- 接收方丢失同步时,通过搜索标志字节找到当前帧的结束位置
-
字节填充/字符填充
- 当标志字节的位模式出现在数据中时,发送方的数据链路层在标志字节前面插入一个特殊的转义字节(ESC),接收端的数据链路层在将数据发送给网络层前将转移字节删除
- 成帧的标志字节和数据中的标志字节可以通过转移字节进行区分
- 如果转义字节也出现数据中,在数据中的转义字节前面也插入一个转义字节
-
缺点
- 对8位字符模式依赖性太强,但是并不是所有字符码都使用8位字符
- UNICODE使用16位字符
- 对8位字符模式依赖性太强,但是并不是所有字符码都使用8位字符
-
- 函位填充的分界标志法
- 允许数据帧包含任意长度的位,也运行每个字符有任意长度的位
- 位填充:当发送方的数据链路层碰到数据中5个连续的位“1”时,自动在输出流中填充一个位“0”; 当接收方看到5个连续输入的位“1”,并且后面是位“0”时,自动去掉位“0”
- 通过标志模式可以明确的识别出两帧之间的边界;
- 如果接收方失去的帧同步,只需要在输入流中扫描帧边界即可;
- 物理层编码违例法
- 适用场景:物理介质上的编码方法中包含冗余信息的网络
- 说明
- 许多链路协议联合使用字符计数法和其他某一种方法,以保证额外的安全性
- 当某一帧到达后,首先用计数域确定帧结束处
- 当结束处出现了正确分解符,帧校验正确时,该帧有效
- 否则,接收方在输入流中扫描下一个分解符
1.4.错误控制
- 目的:确保所有帧最终都被提交给目标机器上的网络层,并且保证正确的顺序
- 思路:向发送方提供一些有关线路另一端状况的反馈信息
- 常见方案
- 协议要求接收方送回一些特殊的控制帧,在这些控制帧中,对于它所接收到的帧进行肯定或否定的确认;肯定意味着该帧安全到达,否定意味着传输过程中产生错误,需要重传
- 定时器和序列化
- 定时器到期前,帧被正确接受,该帧的定时器取消
- 如果原始帧或确认报文丢失,定时器触发,警告发送方有一个潜在问题存在
- 每一个发送出去的帧分配序列化,用于区分原始帧或重发帧
1.5.流控制
- 基于反馈的流控制
- 接收方给发送方送回信息,告诉发送方发送多少数据,或告诉发送方它的相关信息
- 协议中规定发送方什么时候可以发送下一帧,没有接收方许可前,禁止发送方往外发送数据
- 基于速度的流控制
- 协议中有内置机制,限制发送方传输数据的速率,不需要接收方反馈
2.错误检测和纠正
2.1.纠错码
- 概述:在每一个被发送的数据块中包含足够的冗余信息,以便接收方可以推断出接收的数据肯定有哪些内容,使用纠错码的技术通常叫做向前纠错
- 适用场景:错误发生很频繁的信道上,比如无线链路
2.2.检错码
- 概述:在每一个被发送的数据块中包含冗余信息,以便接收方推断出发生的错误,但是不能推断出那个发生了错误,然后接收方请求重发
- 适用场景:高度可靠的信道,比如光纤,当偶尔有错误发生时,错误识别后请求重发就行
3.基本数据链路协议
- 单工停-等协议
- 发送方发出一个帧,然后等待一个确认,再发出下一帧
- 有噪声信道的单工协议
- 发送方送出一帧及启动定时器。
- 如果定时器已经在运行了,则它将被重置,以便等待另一个完整的定时器间隔。
- 在选择定时器间隔的时候,应该保证它足够长,以便该帧到达接收方,并且按照最坏的情形让接收方处理该帧,再允许确认帧传回发送方。
- 如果超时间隔设置得太短的话,则发送方将会发送一些不必要的帧。虽然这些额外的帧不会影响到协议的正确性,但是会损害性能。
- 只有当这一段时间间隔已经过去之后,发送方才可以假定原先的帧或者它的确认帧已经丢失了,于是再重发原先的帧。
- 如果定时器已经在运行了,则它将被重置,以便等待另一个完整的定时器间隔。
- 接收方处理到达的有效帧
- 接收方首先检查它的序列号,看它是否是一个重复分组。
- 如果不是的话,则接受该分组,并传递给网络层,以及生成一个确认。
- 重复的帧和受损的帧都不会传递给网络层。
- 接收方首先检查它的序列号,看它是否是一个重复分组。
- 发送方处理确认帧
- 如果一个有效的确认到来,则发送方从它的网络层获取下一帧,并把它放到缓冲区中,覆盖掉原来的分组。它也会增加序列号。
- 如果一个受损的确认帧到来,或者根本没有确认帧到达,则缓冲区和序列号都不会有任何改变,所以会再次发送原来的帧。
- 发送方送出一帧及启动定时器。
4.滑动窗口协议
4.1. 1位滑动窗口协议
/*Protocol 4(sliding window)isbidirectional.*/
#define MAX_SEQ 1 /+must be 1 for protocol 4 */
typedef enum (frame_arrival,cksum_err,timeout}event_type;
#include"protocol.h
void protocol4(void)
{
seq_nr next_frame_to_send; /* 0 or 1 only */
seq_nr frane_expected; /* 0 or 1 only */
frame r,s; /* scratch variables */
packet buffer; /*current packet being sent */
event_type event;
next_frame_to_send =0; /*next frame on the outbound stream */
frame_expected =0; /*frameexpected next */
from_network_layer(&-buffer); /*fetch a packet from the network layer */
s.info =buffer; /*prepare to send the initial frame*/
s.seq =next_frame_to_send; /*insert sequence number intoframe */
s.ack =1-frame_expected; /*piggybacked ack */
to_physical_layer(&s); /*transmit the frame */
start_timer(s.seq); /* start the timer running */
while (true){
wait_for_event(&event); /*frame_arrival,cksum_err,or timeout */
if(event == frame_arival) { /*aframe has arrived undamaged,*/
from_physical_layer(&r); /*go get it */
if (r.seq ==frame_expected){ /*handle inbound frame stream.*/
to_network_layer(&r.info); /*pass pecket to network layer */
inc(frame_expected): /*invert seg number expectednext */
}
if(r.ack ==next_frame_to_send)( /*handle outboundframe stream.*/
stop_timer(r.ack); /*turn the timer off */
from_network layer(&buffer); /*fetch new pkt fromnetwork layer */
inc(next_frame_to_send); /*invert sender's sequence number */
}
}
s.info =buffer; /*construct outbound frame */
s.seq =next_frame_to_send; /*insert sequence number into it */
s.ack=1-frame_expected /*seq number of last received frame */
to_physical_layer(&s); /*transmit a frame */
start_timer(s.seg): /#start the tiner running*/
}
- 说明
- next_frame_to_send:发送者当前发送帧的序列号
- frane_expected:接受者等待接受帧的序列号
- seq:帧中的序列号
- ack:帧中的确认号
- 初始化
- next_frame_to_send = 0、frame_expected =0、seq=0、ack=1
- 发送者
- 从物理层获取帧信息
- 接收到的帧中确认号 = 发送者当前正在发送的帧的序列号(next_frame_to_send)
- 停止定时器
- 从网络层获取分组
- 修改当前发送帧的序列号
- 将从网络层获取的分组放置在帧中、设置帧序列号为当前发送帧序列化
- 将帧推动到物理层
- 启动定时器
- 接受者
- 从物理层获取帧信息
- 接收到的帧中序列号 = 接受者等待接受帧的序列号(frane_expected)
- 发送帧中分组信息到网络层
- 修改接受者等待接受帧的序列号
- 设置帧确认好为接受到的帧的序列号
- 将帧推动到物理层
- 重置定时器
- 其他
- 发送者超时未收到接受者的确认信息、或收到的确认号与当前发送的帧序列化不匹配,重复发送当前帧信息到接受者,且重置定时器;
- 接受者接受到的帧信息不是想要的帧序列号,不将接受到的帧信息推动到网络层
- 接受到的帧信息不是想要的帧或超时未接收到新的帧,重复发送确认信息