计算机网络学习笔记(三)- 传输层(上)
传输层服务概述
- 传输层协议为运行在不同host上的进程提供了一种逻辑通信机制
(注:网络层是提供host之间的逻辑通信机制)
• 位于网络层之上
• 依赖于网络层服务
• 对网络层服务进行(可能的)增强 - 端系统运行传输层协议
• 发送方:将应用递交的消息分成一个或多个的segment,并向下传给网络层。
• 接收方:将接收到的segment组装成消息,并向上交给应用层。 - 传输层可以为应用提供多种协议
• TCP
• UDP
多路复用与分用
-
接收端进行多路分用
传输层依据头部信息将收到的Segment交给正确的Socket,即不同的进程 -
发送端进行多路复用
从多个Socket接收数据,为每块数据封装上头部信息,生成Segment,交给网络层 -
无连接分用
• 利用端口号创建Socket
• UDP的Socket用二元组标识(目的IP地址,目的端口号)
• 主机收到UDP段后
• 检查段中的目的端口号
• 将UDP段导向绑定在该端口号的Socket
注:UDP的Socket使用二元组标识仅能标识唯一的Socket,因此来自不同源IP地址和源端口号的IP数据包将被导向同一个Socket(下图中SP仅提供返回地址,并未起到标识作用) -
面向连接分用
• TCP的Socket用四元组标识
源IP地址
源端口号
目的IP地址
目的端口号
• 接收端利用所有的四个值将Segment导向合适的Socket
• 服务器可能同时支持多个TCPSocket
每个Socket用自己的四元组标识
• Web服务器为每个客户端开不同的Socket
无连接传输协议
UDP: User Datagram Protocol [RFC 768]
UDP概述
- 基于Internet IP协议
• 复用/分用
• 简单的错误校验 - “Best effort”服务,UDP段可能丢失
- 无连接
• UDP发送方和接收方之间不需要握手
• 每个UDP段的处理独立于其他段
UDP存在原因
- 无需建立连接(减少延迟)
- 实现简单:无需维护连接状态
- 头部开销少
- 没有拥塞控制: 应用可更好地控制发送时间和速率
UDP校验和(checksum)
- 目的:检测UDP段在传输中是否发生错误(如位翻转)
- 发送方
• 将段的内容视为16-bit整数
• 校验和计算:计算所有整数的和,进位加在和的后面,将得到的值按位求反,得到校验和
• 发送方将校验和放入校验和字段 - 接收方
• 计算所收到段的校验和
• 将其与校验和字段进行对比
• 不相等:检测出错误
• 相等:没有检测出错误(但可能有错误)
注:最高位进位必须被加进去
可靠数据传输原理
可靠数据传输协议
- 可靠数据传输对应用层、传输层、链路层都很重要
- 信道的不可靠特性决定了可靠数据传输协议(rdt)的复杂性
下图为可靠数据传输协议结构: - rdt_send():被上层应用调用,将数据交给rdt以发送给对方
- udt_send(): 被rdt调用,在不可靠信道上向接收方传输数据
- rdt_rcv(): 当数据包到达接收方信道时被调用
- deliver_data(): 被rdt调用,向上层应用交付数据
RDT1.0:可靠信道上的可靠数据传输
- 假设底层信道完全可靠
• 不会发生错误
• 不会丢弃分组 - 采用状态机刻画传输协议,如下所示:
![](https://img-blog.csdnimg.cn/20200403203507598.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdsaXpoaTM3MzM=,size_16,color_FFFFFF,t_70)
- 伪代码如下:
// 发送端
loop(forever)
if(call from above) // 应用层调用,传送数据给网络层
rdt_send(data);
packet = make_pkt(data); //报文分段
udt_send(packet);
end
end
// 接收端
loop(forever)
if(call from below) // 网络层调用,传送数据给应用层
rdt_rev(packet);
packet = extract(packet,data);
deliver_data(data);
end
end
RDT2.0:产生位错误的信道
- 辨别位错误
利用校验和检测位错误 - 从错误中恢复
• 确认机制(Ackonwledgements,ACK):接收方显式地告知发送方分组已正确接收
• NAK:接收方显式地告知发送方分组有错误
• 发送方收到NAK后,重传分组 - Rdt 2.0中引入的新机制
• 差错检测
• 接收方反馈控制信息:ACK/NAK
• 重传 - 采用状态机刻画传输协议,如下所示:
![](https://img-blog.csdnimg.cn/20200403205146445.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdsaXpoaTM3MzM=,size_16,color_FFFFFF,t_70)
注:这里与rdt1.0不同,这里发送方有两个状态:等待上层调用和等待ACK/NAK控制消息
- 发送方:当处在Wait for call from above状态时,有上层调用了,产生一个package,这时候要加上校验和生成SNK package,然后发出去。然后等待ACK或NAK的状态,重传回来的是ACK的话就等待下次上层调用,如果是NAK的话就要重传数据。
- 接收方:下层有数据过来,收到一个package,判断这个分组是否有错误,如果有错误,反馈NAK给发送方,如果没有错误,从中提取数据向上层交付。
- 伪代码如下
// 发送端
loop(forever)
if(call from above) // 应用层调用,传送数据给网络层
rdt_send(data);
packet = make_pkt(data,checksum); // 报文分段,并加入校验和
udt_send(sndpkt);
loop(forever)
if(rdt_rcv(rcvpkt) && isNAK(sndpkt)) // 收到了接收端回应为NAK
udt_send(sndpkt);
else if(rdt_rcv(rcvpkt) && isACK(sndpkt))
continue;
end
end
end
// 接收端
loop(forever)
if(call from below) // 网络层调用,传送数据给应用层
if(rdt_rcv(rcvpkt) && corrupt(rcvpkt))
udt_send(NAK);
else if((rdt_rcv(rcvpkt) && notcorrupt(rcvpkt))
tract(rcvpkt,data)
deliver_data(data)
udt_send(ACK)
end
end
RDT2.1
-
Rdt2.0中如果ACK/NAK消息发生错误或者被破坏了,这样的话导致Rdt2.0的机制就无法成功,有以下几种选
• 为ACK/NAK增加校验和,检错并纠错
• 发送方收到被破坏ACK/NAK时不知道接收方发生了什么,添加额外的控制消息,但是额外的消息也可能坏掉,所以不可行
• 如果ACK/NAK坏掉,发送方重传,如果简单重传,会产生重复分组
• 解决重复分组问题:序列号(Sequence number):发送方给每个分组增加序列号,根据序列号可以判断是新的还是重复的,重复的话就直接接收方丢弃重复分组发送方 -
采用状态机刻画传输协议,如下所示:
![](https://img-blog.csdnimg.cn/20200403212219651.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdsaXpoaTM3MzM=,size_16,color_FFFFFF,t_70)
![](https://img-blog.csdnimg.cn/20200403213150622.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdsaXpoaTM3MzM=,size_16,color_FFFFFF,t_70)
注:与2.0区别
- 发送方:
• 为每个分组增加了序列号
• 两个序列号(0, 1)就够用
• 需校验ACK/NAK消息是否发生错误
• 状态数量翻倍
• 状态必须“记住”“当前”的分组序列号 - 接收方
• 需判断分组是否是重复
• 当前所处状态提供了期望收到分组的序列号,如果等待0却收到了1的包,则丢弃
• 注意:接收方无法知道ACK/NAK是否被发送方正确收到
- 伪代码如下
// 发送端
loop(forever)
if(call 0 from above) // 应用层调用,传送数据给网络层
rdt_send(data);
sndpkt = make_pkt(0,data,checksum); // 报文分段,并加入校验和,序列号
udt_send(sndpkt);
loop(forever)
if(rdt_rcv(rcvpkt) && (corrupt(rcvpkt) || isNAK(rcvpkt))
udt_send(sndpkt)
else if(rdt_rcv(rcvpkt) && (corrupt(rcvpkt) && isACK(rcvpkt))
break;
end
end
if(call 1 from above) // 应用层调用,传送数据给网络层
rdt_send(data);
sndpkt = make_pkt(1,data,checksum); // 报文分段,并加入校验和,序列号
udt_send(sndpkt);
loop(forever)
if(rdt_rcv(rcvpkt) && (corrupt(rcvpkt) || isNAK(rcvpkt))
udt_send(sndpkt)
else if(rdt_rcv(rcvpkt) && (corrupt(rcvpkt) && isACK(rcvpkt))
break;
end
end
end
RDT2.2:无NAK消息协议
-
与2.1区别
• 去掉NAK消息协议,只使用ACK
• 接收方通过ACK告知最后一个被正确接收的分组
• 在ACK消息中显式地加入被确认分组的序列号
• 发送方收到重复ACK之后,采取与收到NAK消息相同的动作,重传当前分组 -
采用状态机刻画传输协议,如下所示:
![](https://img-blog.csdnimg.cn/20200403214528674.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdsaXpoaTM3MzM=,size_16,color_FFFFFF,t_70)
- 伪代码如下
// 发送端
loop(forever)
if(call 0 from above) // 应用层调用,传送数据给网络层
rdt_send(data);
sndpkt = make_pkt(0,data,checksum); // 报文分段,并加入校验和,序列号
udt_send(sndpkt);
loop(forever)
if(rdt_rcv(rcvpkt) && (corrupt(rcvpkt) || isACK(rcvpkt,1))
udt_send(sndpkt)
else if(rdt_rcv(rcvpkt) && (corrupt(rcvpkt) && isACK(rcvpkt,0))
break;
end
end
end
RDT3.0
- 前面几种协议的缺陷是如果接收方发回的ACK丢失了,那么发送方就会一直等待下去,这样协议就不能工作了,使用为了解决这个问题,rdt3.0使用增加定时器的方法
- 方法:发送方等待“合理”时间
• 如果没收到ACK,重传
• 如果分组或ACK只是延迟而不是丢了:重传会产生重复,序列号机制能够处理,接收方需在ACK中显式告知所确认的分组
• 需要定时器 - 采用状态机刻画传输协议,如下所示:
![](https://img-blog.csdnimg.cn/20200403215555639.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdsaXpoaTM3MzM=,size_16,color_FFFFFF,t_70)
![](https://img-blog.csdnimg.cn/20200403215924836.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdsaXpoaTM3MzM=,size_16,color_FFFFFF,t_70)
![](https://img-blog.csdnimg.cn/20200403220040653.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdsaXpoaTM3MzM=,size_16,color_FFFFFF,t_70)
流水线机制与滑动窗口协议
流水线机制
- 在停-等机制中,这中间只进行了一个任务,而如果多个任务的话那要一个一个来,这导致了性能下降
- 使用流水线机制,在停-等的过程中可以同时进行下一次发送,允许多个分组发出去,这样的话就可以提高性能
- 流水线协议
• 允许发送方在收到ACK之前连续发送多个分组
• 更大的序列号范围
• 发送方和/或接收方需要更大的存储空间以缓存分组
滑动窗口协议概述
- 滑动窗口协议: Sliding-window protocol
- 窗口
• 允许使用的序列号范围
• 窗口尺寸为N:最多有N个等待确认的消息 - 滑动窗口
• 随着协议的运行,窗口在序列号空间内向前滑动 - 滑动窗口协议:GBN, SR
Go-Back-N协议
- 采用累积确认的机制,如果收到ACK(n)的话表示确认到序列号n(包括n)的分组均已经被正确接收了
• 可能收到重复的ACK
• 计时器
• 超时事件:重传序列号大于等于n,还未收到ACK的所有分组
• 乱序到达的分组:直接丢弃 - 采用状态机刻画传输协议,如下所示:
- 流程图如下:
![](https://img-blog.csdnimg.cn/20200403221920919.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdsaXpoaTM3MzM=,size_16,color_FFFFFF,t_70)
- 示例:
• 数据链路层采用后退N帧(GBN)协议,发送方已经发送了编号为0~7的帧。当计时器超时时,若发送方只收到0、2、3号帧的确认,则发送方需要重发的帧数是多少?分别是那几个帧?
• 解:根据GBN协议工作原理,GBN协议的确认是累积确认,所以此时发送端需要重发的帧数是4个,依次分别是4、5、6、7号帧
Selective Repeat协议
- 概述
• 接收方对每个分组单独进行确认,设置缓存机制,缓存乱序到达的分组
• 发送方只重传那些没收到ACK的分组,为每个分组设置定时器
• 发送方窗口:N个连续的序列号,限制已发送且未确认的分组
• 序列号空间大小与窗口尺寸需满足关系:Ns+Nr<=2k,s,r分别为发送端接收端窗口尺寸,k为序列号空间大小 - 伪代码如下:
- 流程图如下:
![](https://img-blog.csdnimg.cn/20200403223242821.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdsaXpoaTM3MzM=,size_16,color_FFFFFF,t_70)