传输层服务介绍(上)

Scroll Down

传输层服务介绍(上)

传输层概述

传输层协议为运行在不通Host上的进程提供了一种逻辑通信机制,两个进程之间仿佛是之间连接的,无论中间连接过多少个网络,路由器等,是端对端的协议

端系统运行传输层协议:

  • 发送方:将应用递交的消息分成一个或多个的Segment,并向下传给网络层
  • 接收方:将接收到的segment组装成消息并向上交给应用层

传输层可以为应用层提供多种协议,例如:TCP,UDP

传输层 VS 网络层

网络层:提供主机之间的逻辑通信机制

传输层:提供应用进程之间的逻辑通信机制

  • ​ 位于网络层之上
  • 依赖于网络层服务
  • 对网络层服务进行增强

Internet传输层协议

可靠,按序的交付服务(TCP)

  • 拥塞控制
  • 流量控制
  • 连接建立和拆除

不可靠的交付服务(UDP)

  • 基于网络层的服务,没有做可靠性方面的扩展,只实现了传输层必要的服务

传输层的多路复用和多路分用

为什么要进行多路复用/分用?

如果某层的一个协议对应直接上层的多个协议/实体,则需要复用/分用

发送端进行多路复用

从多个Socket接收数据,为每块数据封装上头部信息,生成Segment,交给网络层

接收端进行多路分用

传输层一句头部信息将受到的Segment交给正确的Socket,即不同的进程

如图:P2和P1与P3的同一个Socket建立连接,返回时使用源端口号进行返回

image-20200203154410302

多路分用如何工作?

主机接收到IP数据报(datagram)

  • 每个数据报携带源IP地址,目的IP地址
  • 每个数据报携带一个传输层的段(Segment)
  • 每个段携带源端口号和目的端口号

主机接收到Segment之后,传输层协议提取IP地址和端口号信息,将Segment导向相应的地址

无连接的多路分用(面向UDP)

UDP的Socket用二元组标识(目的IP地址,目的端口号)

主机收到UDP段后,检查段中的目的端口号,将UDP段导向绑定在该端口号的Socket.

来自不同源IP地址或源端口号的IP数据包被导向同一个Socket.

image-20200203154201908

面向连接的分用(面向TCP)

TCP的Socket用四元组来标识(源IP地址,源端口号,目的IP地址,目的端口号)

接收端利用所有的四个值将Segment导向合适的Socket

服务器可能同时支持多个TCP Socket(每个Socket用自己的四元组标识)

P2与P6建立连接,P3与P5建立连接,根据四元组值的不同,可以判断是不同的TCP连接

image-20200203154218426

TCP一定是1对1的,UDP可以多对1.

一个线程可以创建多个线程,通过不同的线程也可以维护多个TCP连接,Web服务器使用多线程的服务器用于维护多个不同客户端请求的TCP连接.如图,一个P4的服务进程,可以启动多个服务线程与P1,P2,P3建立TCP连接

image-20200203154128236

UDP协议概述

基于Internet IP协议

实现了多路复用/分用,做了简单的错误校验

传输层是端对端的第一层,不能简单依赖网络层,链路层的错误校验

UDP的报文段可能会丢失,也可能会非按序到达

UDP是无连接的,UDP发送方和接收方之间不需要握手,每个UDP段的处理独立于其他段

相对于TCP的可靠性,为什么还需要UDP的存在?

  • 无需建立连接,减少延迟(所以DNS使用的是UDP协议)
  • 实现简单:无需维护连接状态
  • 头部开销少(TCP的头部是20个字节,UDP的头部是8个字节)
  • 没有拥塞控制:应用可更好的控制发送时间和速率(TCP会自己调整发送速率,对于需要掌控发送速率和时间的应用来说,UDP更合适)

UDP常用的应用

常用于流媒体应用

  • 容忍丢失
  • 速率敏感

UDP还用于

  • DNS
  • SNMP

如何在UDP上实现可靠数据传输?

  • 在应用层增加可靠性机制
  • 应用特定的错误恢复机制(开发难度大)

UDP校验传输中是否发生错误

image-20200203155703820

UDP的首部总共32位,8个字节,来源端口,目的端口,UDP的长度,checksum

如何检测UDP段在传输中是否发生错误?

发送方

  • 将段的内容视为16-bit整数
  • 校验和计算:计算所有的整数的和,如果有仅为加导和的后面,将得到的值按位取反,得到校验和
  • 发送方将校验和放入校验和字段

接收方:

  • 使用发送方的校验方法,计算所收到段的校验和
  • 将其余校验和字段进行比对,如何不相等,就检测出错误,如果相等的话意味着没有检测出错误,但有可能发生错误,所以说UDP是简单的错误校验

校验和计算示例:
image-20200203160244975

最高位需要进位的时候需要加到最后一位,则得到

sum = 1011101110111100

按位取反得到

checksum = 0100010001000011

可靠数据传输原理

什么是可靠?

​ 不错,不丢,不乱

​ 数据不会错误,分组数据不能丢失,分组数据不能混乱

所以需要设计可靠的数据传输协议

信道的不可靠特性决定了可靠数据传输协议(rdt)的复杂性

image-20200203230259723

应用层将数据交给rdt交给传出层,然后rdt调用ip协议(udt_send)不可靠信道向接收方传输数据,然后数据到到达接收方,接收方传输层处理完可靠的数据后,传输给应用层,传输层和信道之间是双向的流动

Rdt1.0 可靠信道上的可靠数据传输

假定底层信道完全可靠,不会发生错误,不会丢弃分组.

发送方和接收方不需要进行控制信息交换,因为信道是可靠的.

image-20200203231221068

发送方只需要将分组数据通过udt_send发送给接收方,接收方直接将数据传递给应用层,因为是可靠的信道,所以不需要进行控制信息检查.

Rdt 2.0:产生位错误的信道

假定Rdt2.0的信道会产生位错误,有些会将0翻转为1,也会将1翻转为0,分组不会丢失,顺序不会错乱,该如何设计?

首先需要检查分组中的位是否会有错误,可以使用UDP的检测手段,利用校验和检测位错误

如何从错误中恢复?

  • 确认机制(Acknowledgements,ACK):接收方显示的告知发送方分组已正确接收
  • NAK(Negative Acknowledgment ):接收方显式的告知发送方分组有错误
  • 发送方收到NAK后,重传分组

基于这种重传的rdt协议为**ARQ(Automatic Repeat reQuest)**协议

Rdt2.0中引入了新机制

  • 差错检测
  • 接收方显示反馈控制消息:ACK/NAK
  • 重传

sender方:首先等待上层调用,进行分组并加上校验和之后发送,发送完之后需要等待接收方的ACK or NAK标志,称为停-等协议,如果收到对方返回的消息是NAK的话,需要进行重发,重发完之后依然需要进行等待接收方返回的 ACK or NAK 标志,只有接收方返回的是ACK标志,我们才能继续等待上层调用

receiver方:接收方收到一个packet,首先判断分组内容是否有误,假如有错误,我们需要使用udt_send将NAK反馈给发送方,等待发送方重传,如果没错的话,反馈ACK给发送方,并将其中数据提取给应用层

image-20200203232550880

Rdt2.1和Rdt2.2

Rdt2.0有什么缺陷?

如果ACK/NAK的消息发生错误/被破坏会怎么样?

  • 为ACK/NAK增加校验和,检错并纠错(付出额外的代价)
  • 发送方收到被破坏的ACK/NAK时不知道接收方发生了什么,添加额外的控制消息(额外的控制消息也可能会坏掉)
  • 如果ACK/NAK坏掉,发送方重传(最简单的方式,现在使用的方式)
  • 不能简单的重传:会产生重复分组

如何解决重复分组的问题?

  • 增加序列号:发送方给每个分组增加序列号
  • 接收方丢弃重复的分组

Rdt2.1

sender方的变化:

image-20200203235222339

给每个分组增加上了0,和1的两个分组序列号,发送方制作分组的时候,增加序列号0或者1的分组,当我们发送的分组序列号为0的时候,遇到NAK时继续重发,然后当收到接收方为ACK的消息的时候,将下一个序列号改为1

receiver方:

image-20200203235530270

接收方也增加了状态,希望接收0或者接收1的分组,如果收到分组,分组坏掉了,就发送NAK,如果收到的分组,分组没坏掉,但是接收方想要的是序列号不匹配的分组,那接收方需要发送ACK反馈给发送方

发送方

  • 为每个分组增加了序列号
  • 两个序列号就够用,因为使用的是停-等协议,每次只会一个状态的变化
  • 需要校验ACK/NAK消息是否发生错误
  • 状态数量翻倍,状态需要记住当前分组的序列号

接收方

  • 需要判断分组是否是重复,需要记录当前需要的是序列号为0或者1的分组,以免重复

rdt2.2 无NAK消息协议

如何只是用ACK,而不使用NAK

  • 接收方通过ACK告知最后一个被正确接收的分组
  • 在ACK消息中显示的加入被确认分组的序列号
  • 发送方收到重复的ACK之后,采取和收到NAK消息相同的动作的动作(重传当前分组),比如发送方最后发送的分组序号是1,接收方反馈的最后分组是0,那就需要重传序号为1的分组

image-20200204000417274

receiver方:增加返回ACK的序列号的分组

Rdt3.0

在Rdt2.0的时代,信道只会产生位错误,现在Rdt3.0的版本,信道既可能发生错误,也可能丢失分组

校验号+序列号+ACK+重传够用吗?

假如发送方发送的一个分组中途丢失了,此时发送方会一直等待,接收方因为没有收到数据,不会有任何操作.

假如接收方返回的ACK消息丢失了,那发送方也会一直等待,不会有任何操作

方法:发送方等待合理的时间

  • 如果没收到ACK,使用重传
  • 如何分组或ACK只是延迟而不是丢了,会导致重复,重传重复通过序列号机制进行处理

需要增加一个定时器功能

sender方:

image-20200204001307412

在发送分组的时候,启动一个定时器(start_timer),如果超时(timeout)或者受到的ACK分组序列号不对,则进行重传该分组并重新启动定时器,如果收到ACK序列号分组正确,则将状态从0置为1,并停止定时器

Rdt3.0工作示例

image-20200204001537377

但是这种停等协议下,资源的利用率实在太低了,每次一个分组都必须要等待ACK之后才能进行下一个分组发送,极大的浪费了资源

滑动窗口协议

首先采用流水线机制,一次性发送多个分组,并接受多个不同的ACK,显著的提高资源的利用率,但是使用流水线机制,原有协议也需要进行改变

  • 需要更大的序列号范围
  • 发送方和接收方需要更大的存储空间以缓存分组

image-20200204134354843

这时候如果需要使用流水线机制,引入滑动窗口协议:Sliding-window protocol

窗口:允许使用的序列号范围,窗口尺寸为N:最多有N个等待确认的消息

滑动窗口:随着协议的运行,窗口在序列号空间内向前滑动
image-20200204134633867

绿色的是已经发送并收到ACK的,黄色的是正在等待ACK的分组,蓝色的是本次还可以继续使用的序列号

Go-Back-N(GBN)协议

分组头部包含K-bit个序列号,窗口尺寸为N,最多允许N个分组未确认
image-20200204134847693

当收到一个ACK(n)的消息,意思为1-n的分组已被正确接收.

当分组发送超时时,重传序列号大于等于n还未收到ACK的所有分组

sender方:

image-20200204135620577

首先base=1,开始发送消息,当分组序列号<base+N的时候,继续创建分组并发送,直至等于最大窗口数,此时启动计时器(start_timer),如果超时的话,就将之前的分组全部重新发送一遍,如果收到了分组确认的消息,则将base设置为getAckNum()+1,当收到了本次所有的分组,则停止计时器,此时base的值进行了变化,也就有了窗口向前滑动

receiver方:

image-20200204140003084

ACK机制:发送拥有最高序列号的,已被正确接收的分组的ACK,接收方只需要记住唯一的expectedseqnum++

假如出现了乱序,乱序到达的分组,接收方直接丢弃该数据,然后重新确认序列号最大的,按序到达的分组,比如接收方期望收到序列号为5的分组,发送方发送了序列号为7的分组,接收方直接抛弃该分组,并重新发送确认序列号为4的分组

GBN示例

image-20200204140304052

该图表示,当发送pkt2的时候丢失了,此时发送方继续发送pkt3的的数据,此时接收方依旧返回ACK1,发送方继续发送pkt4,pkt5,收到ACK0,ACK1,此时等待pkt2的计时器超时了,发送方将>ACK1的数据都全部发送一遍

GBN的缺陷

当某个分组丢失的时候,GBN会重传很多分组,导致网络会有很多无效传递

SR(Selective Repeat协议)

接收方对每个分组单独进行确认

设置缓存机制,缓存乱序到达的分组

发送方只需要重传那些没收到ACK的分组

为每个分组设置单独的定时器

SR协议相对于GBN协议来说,多了一个接收方窗口

image-20200204142811914

灰色的是希望收到但是还未收到的,红色的是乱序到达的分组,我们先将他缓存起来,并且已经回复了ACK,蓝颜色的是可以接收的序列号范围

发送方窗口和接收方窗口不对应,彼此不知道窗口对应何处

sender方:如果有可供发送的序列号,则发送分组,否则拒绝发送

如果某个分组已经超时,则重新发送该分组并重启定时器

如果收到ACK(n),将该分组标记为已收到,如果n是最小值,则可以把窗口向前滑动

receiver方:收到的数据发送ACK,如果是乱序的就先将他缓存起来,如果是按序来的,就将后面连续的已缓存好的就一起发送给上层进行解析