前置
几个假设:
- 物理层、数据链路层和网络层各自是独立的处理进程
- 机器A希望向B发送的是一个可靠的、面向连接的长数据流
- 机器不会崩溃
- 从网络层拿到的数据是纯数据
数据链路层与网络层、物理层之间的数据传送接口
wait_for_event(等待事件发生函数)
等待事件发生的函数,一旦有下列事件发生,函数就会产生结果输出
几个事件如下:
- frame_arrival:帧到达的事件
- cksum_err:校验核没有通过发错的事件
- time_out:定时器超时的事件
Timer定时器:
- start_timer,stop_timer:重传定时器
- start_ack_timer,stop_ack_timer:稍带确认定时器
帧结构:
typedef struct
{
frame_kind kind; //帧类型
seq_nr seq; //数据号
seq_nr ack; //确认号
packet info; //分组信息,来源于网络层,是帧的纯数据,直接作为帧的载荷
}frame;
协议1:无限制的单工协议
- 数据单向传送
- 收发双方的网络层都处于就绪状态(随时待命)
- 处理时间忽略不计(瞬间完成)
- 可用的缓存空间无穷大(无限空间)
假设DLL之间的信道永远不会损坏或者丢失帧(完美通道)
typedef enum {frame_arrival} event_type;
#include"protocol.h"
void sender1(void) //发送方
{
frame s;
packet buffer;
while(true)
{
from_network_layer(&buffer); //从网络层获取数据
s.info = buffer;
to_physical_layer(&s); //传输到物理层
}
}
void receiver1(void)
{
frame r;
event_type event;
while(true)
{
wait_for_event(&event); //等到某个事件发生
from_physical_layer(&r); //从物理层获取比特流
to_network_layer(&r.info); //将数据传输到网络层
}
}
接收方文字描述:wait_for_event()等待某个事件的到来,在协议1里只有"帧到达"这个事件
→一旦帧到达,就立马执行函数from_physical_layer(),从物理层里面拿比特流,拿回来之后分割成帧
→再根据帧头帧尾的信息对它进行处理,最主要的就是提取出帧里面的信息(包组)……最后传递给网络层
协议2:单工的停→等协议
位避免收方被涌入的数据淹没,收方不再无脑接收所有数据
对应方法:收方回发一个哑帧,接收方收到哑帧,表明收方允许接收数据,此时再次发送下一帧数据
其实这个时候已经是双方的了,虽然收方只是传了一个很小的哑帧,所以严格上来讲该协议应该属于半双工协议
typedef enum {frame_arrival} event_type;
#include"protocol.h"
void sender1(void) //发送方
{
frame s;
packet buffer;
event_type event;
while(true)
{
from_network_layer(&buffer); //从网络层获取数据
s.info = buffer;
to_physical_layer(&s); //传输到物理层
wait_for_event(&event); //不再继续进行直到收到回复(等一个事件,等待哑帧的到来)
}
}
void receiver1(void)
{
frame r, s;
event_type event;
while(true)
{
wait_for_event(&event); //等到某个事件发生
from_physical_layer(&r); //从物理层获取比特流
to_network_layer(&r.info); //将数据传输到网络层
to_physical_layer(&s); //向对方发送一个哑帧
}
}
协议3:有错误信道的单工协议
信道不再是完美信道:有噪声就会产生差错,有差错就可能会引起以下这些问题
①有可能收到重复帧,如何解决?
- 给每个帧一个独一无二的序列号,序号也可以用来重组排序
- 增加机制: 对正确帧的确认,任何时候只有收到的帧通过校验,才向发方发送一个确认
③任何的帧(包括确认帧)都会在路上丢失,这样会导致对方一直等下去~
- 肯定确认重传(PAR)(或称自动重传请求(ARQ))技术:发送方启动一个重传定时器,超时前如果收到收方的确认,拆除定时器,超时还未收到确认,重传并重置定时器
typedef enum {frame_arrival} event_type;
#include"protocol.h"
void sender3(void)
{
seq_nr next_frame_to_send;
frame s;
packet buffer;
event_type event;
next_frame_to_send = 0;
from_network_layer(&buffer);
while(true)
{
s.info = buffer;
s.seq = next_frame_to_send;
to_physical_layer(&s);
start_timer(s.seq); //设置定时器
wait_for_event(&event);
if(event==frame_arrival) //收到了回复帧
{
from_physical_layer(&s); //从物理层里面拿数据
if(s.ack==next_frame_to_send)
{
stop_timer(s.ack); //关闭定时器
from_network_layer(&buffer); //准备传递下一个信息
inc(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);
if(event==frame_arrival)
{
from_physical_layer(&r);
if(r.seq==frame_expected)
{
to_network_layer(&r.info);
inc(frame_expected);
}
s.ack = 1−frame_expected; //对收到的帧进行确认
to_physical_layer(&s); //传回发送方
}
}
}
提高效率的方法:
- 全双工
- 捎带确认:确认帧直接和下一次要传的帧一起发送,不过也要设定定时器,不能死等
- 批量发送:停→等协议是每收到一个确认才能发送下一帧,发送端等待时间太长,网络通信效率不高,为了提高效率,可以在等待的时间发送数据帧,这样大大减少了浪费的时间