一、运输层
1. 概述
运输层协议为运行在不同主机上的应用进程之间提供了
逻辑通信
功能,使得不同主机好像直接连在一起一样
运输层协议是在端系统中而不是在路由器中实现的
。网络路由器仅作用于网络层分组(即数据报)的网络层字段,而不检查封装在该数据报的运输层报文段的字段。
2. 运输层 VS 网络层
-
运输层为运行在不同主机上的进程间提供了逻辑通信,而网络层提供了不同主机之间的通信。
以下面的例子类比:
有A和B两个家庭,每个家庭分别有12个孩子,两家孩子之间互相写信。每个家庭由一个孩子负责收发邮件,A家庭是Ann,B家庭是Bill。A家庭由Ann将所有兄弟姐妹的信件收集好之后交给邮政服务的邮车上,并将收到的信件分给兄弟姐妹。B家庭的Bill也是一样的工作。
对比这个例子中,就有:
- 应用层报文 ==> 信封上的字符
- 进程 ==> 家庭的孩子
- 主机(端系统) ==> 家庭
- 运输层协议 ==> Ann和Bill
- 网络层协议 ==> 邮政服务
-
运输层协议只工作在端系统,负责将应用进程的报文移动到网络边缘(即网络层)
-
运输层能够提供的服务常常受限于底层网络层协议的服务模型
。例如:网络层协议无法为主机之间发送的传输层报文段提供时延或带宽保证的话,运输层协议也就无法为进程之间发送的应用层程序报文提供时延或带宽保证。
-
即使底层网络协议不能再网络层提供相应的服务,运输层协议也能提供某些服务
。
例如,即使网络层协议会是分组丢失、篡改和冗余,运输层协议也能为应用程序提供可靠的数据传输服务。
3. 因特网运输层
在介绍因特网运输层协议前,先简单了解一下因特网的网络层协议:
因特网提供的网络层协议为:网际协议(IP),网络层的分组称为
数据报
-
IP被称为
不可靠服务
,它的服务模型是
尽力而为交付服务
,即尽最大努力在通信的主机之间交付报文段,但并不做任何确保 -
可以认为
每台主机会有一个IP地址
再来讲运输层协议:
因特网提供的运输层协议有两种:TCP和UDP,运输层的分组称为
报文段
-
UDP(用户数据报协议)
:提供不可靠、无连接的服务 -
TCP(传输控制协议)
:提供可靠、面向连接的服务 - TCP和UDP的最基本责任,就是将两个端系统间IP的交付服务扩展为在端系统上的两个进程之间的交付服务
-
将主机之间的交付扩展到进程间交付被称为
运输层的多路复用和多路分解
运输层提供的服务包括:
-
进程到进程的数据交付和差错检查
(两种最低限度的运输层服务,也是UDP所能提供的所有服务) -
(TCP提供)
可靠数据传输
:保证正确、按序将数据从发送进程交付给接收进程 -
(TCP提供)
拥塞控制
:防止任何一条TCP连接用过多流量来淹没通信主机之间的链路和交换设备
4. 多路复用和多路分解
除了因特网之外,多路复用和多路分解是在所有计算机网络中都需要的。
多路分解 & 多路复用
:
-
对于运输层,数据并没有被直接交付给进程,而是被交给进程对应的套接字
-
多路分解
:将运输层报文段中的数据交付到正确的套接字 -
多路复用
:从不同套接字中收集数据块,并为每个数据块封装上首部信息从而生成报文段,再将报文段传递到网络层 -
虽然这里是以运输层协议来讨论多路分解和多路复用,但是从更一般的角度,
它们与在某层(在运输层或者别处)的单一协议何时被位于接下来的较高层的多个协议使用有关
。
运输层具体是
怎么实现多路复用和多路分解
的呢?
-
对于多路复用和多路分解,要求:
1. 套接字有唯一标识;2. 每个报文段有特殊字段来指示该报文段所交付到的套接字
。报文段中的特殊字段就是
源端口号字段和目的端口号字段
。-
以UDP为例,报文中的源与目的端口号字段示意图如下:
端口号是一个16比特的数,大小在0-65535之间。其中0-1023之间的端口称为
周知端口号
,留给周知的应用层协议来使用。
-
以UDP为例,报文中的源与目的端口号字段示意图如下:
-
对于UDP而言,它的套接字是由一个二元组全面标识的,该二元组包含一个目的IP地址和一个目的端口号
。- 如果两个UDP报文段有两个不同的源IP地址和/或源端口号,但具有相同目的IP地址和目的端口号,这两个报文段会被相同的套接字定位到相同的目的进程。
-
对于TCP而言,它的套接字是由一个四元组(源IP地址、源端口号、目的IP地址和目的端口号)全面标识的
。- 两个具有不同源IP地址或源端口号的报文段将被定位到两个不同的套接字。
二、无连接运输:UDP协议
1. UDP的优缺点
UDP和IP一样是不可靠的,它在IP的基础上,只是做了复用/分解功能以及少量的差错检测
。同时UDP是无连接的,在发送报文段之前,发送方和接收方的运输层实体之间没有握手。
由于缺乏拥塞控制,
UDP能够导致发送方和接收方之间的高丢包率,并挤垮TCP会话
,这是一个潜在的严重问题。
既然UDP有这么多缺点,
为什么还有很多应用要用UDP协议呢
?
-
关于发送什么数据以及何时发送的应用层控制更为精细
- 应用可以自己实现所需的、超过UDP的不提供不必要的报文段交付服务之外的额外功能
-
无需连接建立,因此不会引入建立连接的时延
-
无连接状态
- TCP需要在端系统中维护连接状态(接受和发送缓存、拥塞控制参数以及序号和确认号),UDP则不需要
-
分组首部开销小
- 每个TCP报文段都有20字节的首部开销,而UDP仅有8字节的开销
2. UDP报文段
以下是UDP的报文结构:
应用层数据占用UDP报文段的数据字段
UDP报文段首部有四个字段,除了前面提到的方便多路复用与多路分解的源
端口号和目的端口号字段
,还有长度和检验和字段。
-
长度字段
指示了在UDP报文段中的字节数(首部加数据) -
检验和字段
用于确认报文段从源到达目的地移动时,其中的比特是否发生了改变。
3. UDP检验和
发送方对UDP报文段中所有16比特字的和进行反码运算,求和时遇到的任何溢出都被回卷,得到的结果存放在检验和字段。
接收方将全部的16比特字(包括检验和字段)加在一起,如果分组中没有引入差错,得到的结果将是1111111111111111。如果这些比特之一是0,该分组就已经出现了差错。
虽然UDP提供差错检测,但是对差错恢复无能为力
。UDP的某种实现只是丢弃受损的报文段,其他实现是将受损的报文段交给应用程序并给出警告。
三、可靠数据传输原理
1. 讨论的一些前提
-
可靠数据传输的实现不仅在运输层出现,也会在链路层以及应用层出现
,因此要在一般场景下考虑该问题 -
可靠数据传输协议
为上层实体提供的服务抽象是:
数据可以通过一条可靠的信道传输,传输数据比特不会受到损坏或丢失,所有数据都是按照其发送顺序进行交付
。而可靠数据传输协议的下层也许是不可靠的,这里的讨论,我们
直接将较低层视为不可靠的点对点信道
。 - 这里的要讨论的原理适用于一般的计算机网络,而不只是因特网
- 后续的讨论仅考虑单向数据传输,即数据传输是从发送端到接收端。
2. 可靠数据传输协议的构建
从简单的模型开始,一步一步构建完美、可靠的数据传输协议
A. 经完全可靠信道的可靠数据传输:rdt1.0
假定:底层信道是完全可靠的。
在上述的
有限状态机(FSM)
定义中:
- 发送发和接收方的FSM每个都只有一个状态,箭头指示了协议从一个状态变迁到另一个状态
- 引起变迁的事件显示在横线上方,事件发生时所采用的动作显示在横线下方
-
横线上方或下方的符号
Λ\Lambda
Λ
表示缺少动作或事件,FSM的初始状态用虚线表示
由于底层信道完全可靠,接收端不需要提供任何反馈信息给到发送方
B. 经具有比特差错信道的可靠数据传输:rdt 2.0
假定:分组中的比特可能受损,但所有分组按发送的顺序被接受
这种假定情况下,需要引入
肯定确认
和
否定确认
,使得发送方知道哪些内容被正确接收,哪些接收有误需要重传。基于这样重传机制的可靠数据传输协议称为
自动重传请求(Automatic Repeat reQuest, ARQ)协议
。
ARQ协议需要以下三种协议功能来处理存在比特差错的情况:
-
差错检测
:需要有额外的比特从发送方发送到接收方(如UDP中的检验和字段),这些比特将被汇集在rdt2.0 数据分组的分组检验和字段中 -
接收方反馈
:为了实现肯定确认和否认确定,rdt2.0协议将从接收方向发送方发送NAK和ACK分组。理论上这些分组只需要一个比特长,如0表示NAK,1表示ACK -
重传
:接收方收到有差错的分组时,发送方将重传该分组
上图中即rdt2.0的FSM,需要注意的是:当发送方处于等待ACK或NAK的状态时,不能从上层获取更多的数据,因此rdt2.0协议也被称为
停等协议
。
rdt 2.0协议存在一个
致命的缺陷
:没有考虑到ACK和NAK分组受损的可能性。那么如何解决这个问题呢?可以考虑以下几种可能的解决方法:
-
引入一种新的发送方到接收方的分组
,接收该分组后,接收方重新发送ACK和NAK分组。- 缺点在于:这种新的分组也会出现受损的情况,这就陷入一个死局,不可取
-
增加足够的检验和比特
,发送方不仅可以检测差错,还可以恢复差错。对会产生差错但不丢失分组的信道,可以直接解决问题。- 缺点在于:不适用于后续丢失分组的情况,不具有扩展性,不可取
-
冗余分组
:当发送方接收到含糊不清的ACK或NAK分组时,直接重传当前分组。- 缺点在于:接收方不知道上次发的ACK或者NAK是否被发送方正确收到,无法事先知道接受的分组是新的还是一次重传
上述可能的解决方法中,可以采用的方案是冗余分组。而对于冗余分组的缺点,可以这样解决:
让发送方对其数据分组编号,并在数据分组中添加一个新字段存放该序号
。接收方只需要检查序号即可确认收到的分组是否是一次重传。而对于停等协议,序号仅需要区分前一个分组和现分组,因此有 0 和 1 两种序号,1比特的序号就能完全满足需求。
由于假定不丢失分组,ACK和NAK不需要指明它们要确认的分组序号,发送方知道接收到的NAK和ACK分组(无论是否含糊不清)是为了响应最近发送的数据分组。
以下是rdt2.1的FSM,是在rdt2.0的基础上引入冗余分组的改进结果:
对于rdt2.1协议还可以做进一步改进:
当收到错误的分组时,接收方不发送 NAK,而是发送 ACK + 上一个正确分组的序号。当发送方收到同一个分组的 2 个 ACK 时也可以判断出接收方没有收到被确认 2 次的分组后的所有分组,此时就需要进行重传。
这就是rdt2.2协议,下面是rdt2.2的FSM
C. 经具有比特差错的丢包信道的可靠数据传输:rdt 3.0
假定:除了比特受损外,底层信道还会丢包
此时的协议需要处理的问题有两个:
怎么检测丢包以及发生丢包之后该做什么?
对于这两个问题,这里考虑
让发送方来解决
。
当发送方发送的数据分组或者接收方对该分组发送的ACK丢失了,那么发送方长时间无法收到ACK,在逻辑上就可以认为发生了丢包,此时仅需要重传分组即可。
为了基于时间来判断是否重传,发送方需要一个
倒计数计时器
,发送方需要做到:每发送一次分组就启动一次计时器,等待一段时间没收到 ACK 就重传并重启计时器,收到 ACK 后终止计时器。
那么如果一个分组没有丢失,而是经历了特别大的时延,超过了倒计数计时器设定的时间,此时发送方也会重传该分组,这就引入了
冗余数据分组
。但是在rdt2.2协议中,对这种冗余数据分组的情况已经完全可以解决了。
以上是rdt3.0发送方的FSM,
对于接受方,rdt3.0引入的定时器对于接收方并没有任何影响,因此接收方参见rdt2.2中的接收方
。
由于分组的序号在0和1之间交替,rdt3.0协议也被称为
比特交替协议
。
3. 流水线可靠数据传输协议
rdt3.0协议已经是一个功能正确的协议,但该协议仍然是一个停等协议,它的性能让人难以满意。
为了说明rdt3.0协议的性能问题,这里定义发送方(或信道)的
利用率
为:发送方实际忙于将发送比特送进信道的那部分时间与发送时间之比。
假定:
- 两个端系统之间的传播时延RTT为30ms
- 端系统之间的信道发送速率R为1Gbps
- 分组的长度L为1000字节
在停等协议的情况下,发送方的利用率为:U
sender
=L/R / (RTT + L/R) = 0.008 / 30.008 = 0.00027。在发送速率高达1Gbps的链路下,实际的利用率却只有万分之2.7。
解决上述性能问题的方法是:不以停等协议运行,允许发送方发送多个分组而无须等待确认,这种技术称为
流水线
。
流水线对于可靠数据传输协议的影响如下:
-
必须增加序号范围
- 因为每个输送的分组(不计算重传的)必须有一个唯一的序号,而且也许有多个在输送中的未确认报文
-
协议的发送方和接收方两端也许不得不缓存多个分组
- 发送方最低限度应当能缓存那些已经发送但没有确认的分组,接收方也需要缓存已经正确接收的分组
-
所需的序号范围和缓存取决于数据传输协议如何处理丢失、损坏以及延时过大的分组。
-
解决流水线的差错恢复有两种基本方法:
回退N步
和
选择重传
。
-
解决流水线的差错恢复有两种基本方法:
4. 回退N步(GBN)协议
对于回退N步协议而言,
允许发送方发送多个分组(当有多个分组可用时)而不需要等待确认
。但协议受限
于在流水线中未确认的分组数不能超过最大允许数N
。N被称为
窗口长度
,GBN协议也因此被称为
滑动窗口协议(sliding-window protocal)。
以下是GBN协议的窗口示意图,其中:
-
基序号(base)
:最早未确认分组的序号 -
下一个序号(nextseqnum)
:最小的未使用序号
一个分组的序号承载在分组首部的一个固定长度的序号字段中。如果该序号字段的比特为k,则该序号的范围为[0, 2
k
-1]。在一个有限的序号范围内,所有涉及序号的运算必须使用模2
k
运算(即序号空间可以被看作是一个长度为2
k
的环)。
以下是一个基于ACK、无 NAK的GBN协议的发送方和接收方的扩展FSM。
对于发送方,需要响应的事件有三种:
-
上层的调用
:上层调用rdt_send()时,检查发送窗口是否已满。如果未满则产生一个分组并发送;如果已满,缓存这些数据或使用同步机制允许上层在仅当窗口不满时才调用rdt_send() -
收到一个ACK
:对序号为n的分组的确认采用累积确认,表明接收方已正确接收到序号为n的以前包括n在内的所有分组。 -
超时事件
:仅使用一个定时器,该定时器是最早的已发送但未被确认的分组所使用的定时器。如果收到一个ACK,但仍然有已发送但未被确认的分组,定时器会被重启。如果没有则停止该定时器。如果出现超时,发送方重传所有已发送但未被确认过的分组。
对于接收方:
- 如果一个序号为n的分组被正确接收,并且按序,则接收方为分组n 发送一个ACK,并将该分组的数据部分交付到上层
- 其他所有情况下,丢弃该分组,并未最近按序接收的分组重新发送ACK。
-
这样处理,优点是
接受缓存简单,接收方不需要缓存任何失序分组,唯一需要维护的信息就是下一个按序接收的分组的序号
。
以下是回退N步的运行示意图:
5. 选择重传(SR)协议
GBN协议的缺点
:
当窗口长度和带宽时延都很大时,
单个分组的差错就能引起GBN重传大量分组
,而许多的分组其实没有必要重传
选择重传(SR)协议对GBN协议进行了优化:
发送方仅需重传那些它怀疑在接收方出错(丢失或受损)的分组
,这要求接收方逐个地确认正确接受的分组。
接收方也需要引入窗口
,以此限制流水线中未完成、未被确认的分组数。
接收方和发送方虽然都有窗口,但是两者并不总是能看到相同的结果,因此两者的窗口并不总是一致的。
发送方关注的事件和动作如下:
-
从上层接收数据
:发送方检查下一个可用于该分组的序号,如果序号在窗口内,则打包发送数据,否则将数据缓存或将其返回给上层以便后面传输 -
超时
:每个分组都必须有自己的逻辑定时器,超时发生后只重传一个分组 -
收到ACK
:- ACK的分组序号在窗口内,发送方标记该分组已被接收;
- ACK的分组序号等于send_base,窗口基序号向前移动到具有最小确认号的未确认分组;窗口如果移动且有序号在窗口的未发送分组,则发送这些分组
接收方关注的事件和动作
-
序号在[rcv_base, rcv_base + N – 1]内的分组被接收
:分组序号落在接收方窗口内,发送ACK- 分组序号如果之前没收到过,缓存该分组
- 分组序号如果等于rcv_base,该分组以及之前缓存的序号(起始于rcv_base)连续分组交付给上层,接收窗口向前移动
-
序号在[rcv_base – N, rcv_base – 1]内的分组被接收
:必须产生一个ACK,即使该分组是接收方以前确认过的分组 -
其他情况
:忽略分组
以下是选择重传协议的运行示意图:
需要注意的是,窗口的长度必须比序号空间足够小,两者具体的关系是:
窗口长度必须小于等于序号空间大小的一半。
下面的例子是当窗口长度比序号空间小1时会出现的问题:
- 当发送方发送的0、1、2分组被接收方正确接收,而接收方发送的ACK丢失。发送方会重传分组,接收方接收的序号为0的分组是老分组
- 当发送方发送的0、1、2分组被接收方正确接受,接收方发送的ACK也正确被接收,但发送方发送的3分组丢失,接收方接收的序号为0的分组是新分组
-
对于接收方而言,接收到的序号0并不能判断是新分组还是老分组,因此会产生问题。
上述的讨论中,我们假定分组在发送方与接收方之间的信道中不能被重新排序,这在发送方和接收方由单段物理线路相连的情况下是合理的。实际情况下,是会发生分组重新排序的,即一个具有序号或确认好x的分组的旧副本可能会出现,即使发送方或接收方的窗口中都没有包含x。
为了确保一个序号不被重新使用,直到发送方”确信”任何先前发送的序号x都不再在网络中位置,可以假定一个分组在网络中的”存活”时间不会超过固定最大时间量。
四、面向连接的运输:TCP协议
1. TCP连接概述
- TCP协议中,一个进程向另一个进程发送数据前,这两个进程必须先进行三次握手,以建立确保数据传输的参数,从而建立TCP连接。
-
TCP连接是一条逻辑连接,其
共同状态仅保存在两个通信端系统的TCP程序中
,中间的网络元素不会维持TCP连接状态 -
TCP连接是
全双工服务
,应用层数据可以在两个进程间相互流动 -
TCP连接是
点对点
的,即在单个发送者与单个接受者之间的连接 -
TCP连接的组成包括:一台主机上的缓存、变量和与进程连接的套接字,以及另一台主机上的另一组缓存、变量和与进程连接的套接字
- 进程通过套接字传递数据,TCP将这些数据引导入发送缓存
-
TCP会不时从缓存中取出数据传递到网络层,而取出并放入报文段的数据数量受限于
最大报文段长度(Maximum Segment Size, MSS)
- MSS要保证一个TCP报文段(当封装在一个IP数据报中)加上TCP/IP首部字段将适合单个链路层帧。
- MSS是报文段中应用层数据的最大长度,而不是包括首部的TCP报文的最大长度
2. TCP报文段结构
TCP报文段由
首部字段和一个数据字段
组成。
数据字段由MSS限制
首部字段包括:
-
源端口号和目的端口号
:用于多路复用和分解 -
32比特的序号字段和32比特的确认号字段
:用于实现可靠数据传输-
TCP将数据看成一个无结构的、有序的字节流。序号是建立在传送的字节流之上的,而不是建立在传送的报文段的序列之上,
一个报文段的序号因此是该报文段首字节的字节流编号
-
一个报文的确认号是主机希望从另一个主机收到的下一个字节的序号
-
TCP提供
累积确认
,只确认流中至第一个丢失字节位置的字节。对于收到的失序报文,会保留该失序的报文。
-
TCP将数据看成一个无结构的、有序的字节流。序号是建立在传送的字节流之上的,而不是建立在传送的报文段的序列之上,
-
16比特的接受窗口字段
:用于流量控制,指示接收方愿意接受的字节数量 -
4比特的首部长度字段
:指示以32比特的字为单位的TCP首部长度 -
可选与变长的选项字段
:用于发送方和接收方协商最大报文长度(MSS),或在高速网络环境下用做窗口调节因子时使用。 -
6比特的标志字段
- ACK:指示确认字段中的值是有效的
- RST、SYN、FIN:用于连接建立和拆除
- PSH:被置位时,指示接收方应立即将数据交给上层
- URG:指示报文中存在被发送段的上层置为“紧急”的数据
-
因特网检验和字段
:用于检验分组是否损坏 -
16比特的紧急数据指针
:指出紧急数据的最后一个字节
3. 往返时间的估计与超时
TCP协议采用超时/重传机制处理报文的丢失问题,但是超时时间应该怎样设置才合理呢?很显然,超时时间必须大于该连接的往返时间(RTT),即一个报文发出到它被确认的时间。
估计往返时间
-
样本RTT(表示为SampleRTT)
:从某报文段被发出到对该报文段的确认被接收到之间的时间量- 在任意时间,TCP仅为一个已发送但目前尚未被确认的报文段测量一个SampleRTT,从而产生一个接近每个RTT的新SampleRTT值
- TCP决不为已被重传的报文段计算SampleRTT,而是仅为传输一次的报文段测量SampleRTT
-
SampleRTT均值(称为EstmatedRTT)
:由于SampleRTT是波动的,因此任何给定的SampleRTT也许都是非典型的,而用SampleRTT均值作为典型值。
* TCP根据公式:
EstimatedRTT = (1 – α) * EstimatedRTT + α * SampleRTT
来更新EstimatedRTT
* α 一般取值为0.125,因此有:
EstimatedRTT = 0.875 * EstimatedRTT + 0.125 * SampleRTT
-
RTT偏差DevRTT
:用于估算SampleRTT偏离EstimatedRTT的程度
* 计算公式:
DevRTT = (1 – β) * DevRTT + β * | SampleRTT – EstimatedRTT |
* β一般取值为0.25,因此有:
DevRTT = 0.75 * DevRTT + 0.25 * | SampleRTT – EstimatedRTT|
超时时间的设置
-
TCP的超时时间可以通过公式:
TimeoutInterval = EstimatedRTT + 4 * DevRTT
来估算 - 推荐的初始TimeoutInterval值为1秒。出现超时后,TimeoutInterval将加倍。只要收到报文段并更新EstimatedRTT,就重新计算TimeoutInterval。
4. 可靠数据传输
简化情况
先考虑简化情况,即发送方只用超时机制来恢复报文段的丢失。
TCP发送方有三个与发送和重传有关的事件:
-
从上层应用程序接收数据
:TCP将数据封装在一个报文段中,每个报文段包含一个序号,该序号是报文段第一个数据字节的字节流编号。当报文被传给IP时,启动定时器 -
超时
:TCP通过重传引起超时的报文段来响应超时事件,再重启定时器 -
收到ACK
:TCP采用累积确认,ACK的值 y 确认了 y 之前的所有字节都已经收到。TCP比较ACK的值y与SendBase(最早未被确认的字节的序号),如果y大于SendBase,更新SendBase。如果当前有仍有未被确认的报文段,重新启动定时器。
超时间隔加倍
- TCP发送方在超时事件中,TCP重传时重启定时器,都会将下一次的超时间隔TimeoutInterval设置为之前的两倍,而不是从EstimatedRTT和DevRTT推算出来的值
- 在从上层应用数据接收数据和收到ACK事件时,TCP启动定时器,TimeoutInterval由最近的EstimatedRTT和DevRTT推算出
快速重传
- 超时触发重传存在的问题之一是超时周期可能相对较长,导致发送方延迟重传丢失的分组,增加了端到端时延
-
冗余ACK
:即再次确认某个报文的ACK,而发送方之前已经收到了对该报文的确认。 - 当发送方一旦收到3个冗余ACK,TCP就执行快速重传,即在该报文段的定时器过期之前重传丢失的报文段。
回退N步 or 选择重传
-
TCP是累积确认的,
正确接受但失序的报文段是不会被接收方逐个确认的。
-
发送方仅需要维护已发送过但未被确认的字节的最小序号和下一个要发送的字节的序号
,因此TCP看起来更像GBN协议 -
与GBN协议的区别在于,
TCP接收方会缓存正确接受但是失序的报文
。当一组报文段1, 2, …, N全都按序无差错地到达接收方,而其中的n < N 分组的ACK丢失,GBN会重传n, n+1, n+2, … N分组,而TCP只会重传分组n -
对TCP的一种修改是:
选择确认
,即允许TCP接收方有选择地确认失序报文,而不是累积地确认最后一个正确接受的有序报文。当配合选择重传机制,TCP就更接近SR协议了。 - TCP是GBN和SR协议的混合体。
5. 流量控制
TCP连接的每一侧主机都为连接设置了缓存,当收到正确的、按序的字节后,TCP连接将数据放入缓存,相关联的应用从该缓存中读取数据。当应用程序读取数据相对缓慢,而发送方发送得太快、太多,缓存很容易溢出。
流量控制 VS 拥塞控制
-
流量控制
是一个速度匹配服务,用于消除发送方使接收方缓存溢出的可能性。 -
拥塞控制
是指发送方由于IP网络的拥塞而被遏制 - 虽然两者采取的动作非常相似,但它们显然是针对完全不同的原因而采取的措施。
如何做流量控制?
- 发送方维护一个接收窗口(receive window, rwnd)来实现流量控制
-
做以下定义
- 假设主机A通过TCP连接向主机B发送一个大文件
- RcvBuffer表示主机B为连接分配的接收缓存
- LastByteRead表示主机B上的应用进程从缓存中读取的数据流的最后一个字节的编号
- LastByteRcvd表示从网络中到达的并且已经放入主机B接受缓存中的数据流的最后一个字节的编号
-
由于不允许缓存溢出,因此 LastByteRcvd – LastByteRead <= RcvBuffer,定义
接收窗口 rwnd = RcvBuffer – [ LastByteRcvd – LastByteRead ]
- 主机B将当前的rwd值放入它发给主机A的报文段接收窗口中,通知主机A在该连接的缓存中还有多少可用空间
-
主机A跟踪变量LastByteSend和LastByteAcked,在整个生命周期内保证LastByteSend – LastByteAcked <= rwnd
存在的问题和解决方案
- 问题:当主机B的接受缓存已满,即rwd为0,在告知主机A之后,主机B没有任何数据发给主机A。当主机B清空缓存后,主机A并不知道主机B已经有新空间,而是继续被阻塞不能发数据。
- 解决方案:当主机B的接收窗口为0,主机A继续发生只有一个字节的报文段。主机B接受后发送确认报文,在确认报文中包含非零的rwd。
6. TCP连接管理
TCP连接的创建需要通过
三次握手:
-
客户端TCP向服务端TCP发送
SYN报文段
(报文段的首部中的SYN标志为被置为1)。客户端会随机选择一个初始序号x,并将该编号置于SYN报文段首部的序号字段中。 -
服务端分配TCP缓存和变量,并向客户TCP发送允许连接的
SYNACK报文段
。报文段首部包含三个重要信息:- SYN比特被置为1
- 确认号字段 x + 1
- 服务端选择初始序号y放置到首部的序号字段中
- 客户端分配TCP缓存和变量,发送报文对服务器的允许链接的报文段进行确认。报文中的SYN比特被置为0,负载中可以携带数据。
-
三次握手后的每个报文段,SYN比特都被置为0
TCP连接的终止需要**四次挥手:**以客户端关闭连接为例
- 客户端向服务端发送TCP报文,报文首部的FIN比特被置为1
- 服务端接收到报文段后发送确认报文段
- 服务端发送一个终止报文段,报文首部的FIN比特被置为1
-
客户端对终止报文进行确认
在一个TCP连接的生命周期中,运行在每台主机中的TCP协议在各种TCP状态之间变迁。以下分别是客户端和服务端的TCP状态变迁图。
TCP连接中,当一台主机接收到一个报文段,其端口或源IP地址与其主机上进行的套接字都不匹配时,该主机会向源发送一个重置报文,报文首部的RST标志位被置为1 。而在UDP中,该主机则会发生一个ICMP数据报。
五、拥塞控制原理
1. 拥塞原因与代价
A. 情况1:两个发送方和一台具有无穷大缓存的路由器
如图所示,做以下假设:
- 主机A和B都有一条连接,两条连接共享单条路由
-
主机A和主机B以相同的平均速率λ
in
向一段容量为R的共享式输出链路上传输数据 - 路由器带有无限大的缓存,当分组的到达速率超过输出链路的容量时可以存入缓存
以下是主机A的连接性能结果
- 当发送速率在0 – R/2时,接收方的吞吐量等于发送方的发生速率;当发生速率大于R/2时,吞吐量达到上限R/2 。
- 当发送速率接近R/2时,平均时延越来越大;当发送速率超过R/2时,平均时延变为无穷大
-
拥塞的代价之一:当分组的到达速率接近链路容量时,分组经历巨大的时延
B. 情况2:两个发送方和一台具有有限缓存的路由器
如图所示,与情况1相似,做以下假设:
- 假定路由器缓存的容量有限,当分组到达已满的缓存时会被丢弃
- 每条连接都是可靠的,当分组在路由器中被丢弃,它将被发送方重传
-
定义λ
in
表示应用程序将初始数据发送到套接字中的速率,λ
in’
表示运输层向网络中发送报文段(含有初始数据或重传数据)的速率
以下是主机A的连接性能结果
-
当主机A能以某种方式确定路由器中的缓存是否空闲,并且仅在空闲时才发送一个分组。
-
此时λ
in
和λ
in’
相等,连接的吞吐量等于λ
in’
,主机的吞吐量不超过R/2
-
此时λ
-
当发送方仅当在确认了一个分组已经丢失时才重传
-
此时λ
in’
大于λ
in
,主机的吞吐量不超过R/3 -
拥塞的代价之一:发送方必须执行重传以补偿因为缓存溢出而丢弃的分组
-
此时λ
-
发送方可能因为超时并重传在队列中已被推迟但还未丢失的分组
- 初始数据分组和重传分组都可能到达接收方,但接收方只需要一份,重传分组将被丢弃
-
此时仍有λ
in’
大于λ
in
,主机的吞吐量不超过R/4 -
拥塞的代价之一:发送方在遇到大时延所进行的不必要重传会引起路由器利用其链路带宽转发不必要的分组副本。
C. 情况3:四个发送方和具有有限缓存的多台路由器及多跳路径
如图所示,做以下假设:
- 四台主机发送分组,每台都通过交叠的两跳路径传输。
-
每台主机都采用超时/重发机制来实现可靠数据传输服务,都有相同的λ
in
值,所有路由器的链路容量都是R字节/秒
以下是主机A的连接性能结果:
-
对于较小的λ
in
,由于路由器的缓存溢出较少,λ
in
的增大会导致吞吐量增大 -
随着λ
in
的增大,到达R2的A-C流量最多是R。当λ
in
大到一定程度,A-C流量与B-D流量在路由器R2上必须为有限的缓存空间竞争,而B-D的载荷增大会导致A-C连接通过R2的流量越来越小。 -
当λ
in
趋近无穷大,R2的空闲载荷会被B-D连接的分组占满。
每当有一个分组在第二跳路由器上被丢弃,第一跳路由所做的将分组转发到第二跳路由的工作就是无用功。
因此
有拥塞的代价之一:当一个分组沿一条路径被丢弃时,每个上游路由器用于转发该分组到丢弃该分组而使用的传输容量最终被浪费掉。
2. 拥塞控制方法
根据网络层是否为运输层拥塞控制提供显示帮助,可以将拥塞控制方法分为两种:
-
端到端拥塞控制
:网络层没有为运输层拥塞控制提供显示帮助。端系统需要通过对网络行为的观察(如分组丢失与时延)来推断网络中是否存在拥塞。 -
网络辅助的拥塞控制
:路由器向发送方提供关于网络中拥塞状态的显示反馈信息。反馈发送方的方式有两种- 网络路由器向发送方发送阻塞分组
- 路由器标记或更新从发送方流向接收方的分组中的某个字段来指示拥塞的产生。接收方收到分组后向发送方通知该网络拥塞指示。
六、TCP拥塞控制
1. TCP拥塞控制概述
TCP采用的拥塞控制方法是:让每一个发生者根据所感受到的网络拥塞程度来限制其能向连接发送流量的速率。因此属于端到端拥塞控制
要实现以上方法,需要关注以下几个问题:
问题1:TCP发送方如何限制其向连接发送流量的速率?
-
TCP的发送方跟踪一个额外的变量:
拥塞窗口(congestion window)
,用cwnd表示。 - TCP限制发送方中未被确认的数据量不超过cwnd与rwnd中的最小值。即:LastByteSend – LastByteAcked <= min{cwnd, rwnd}
- TCP通过限制发送方中未被确认的数据量,从而间接限制了发送方的发送速率
问题2:TCP发送方如何感知其到目的地之间的路径上存在拥塞?
- TCP发送方定义丢包事件为:要么出现超时,要么收到接收方的3个冗余ACK。
- 当发送方出现丢包事件时,即认为路径上存在拥塞
问题3:TCP发送方怎样确定其应该发送的速率?
TCP为确定合适的发送速率,应遵循以下指导性原则:
- 一个丢失的报文段意味着拥塞,因此当丢失报文段时应当降低TCP发送方的速率
- 一个确认报文段指示该网络正在向接收方交付发送方的报文段。因此,当对先前未确认报文段的确认到达时,能够增加发送方的速率
- 带宽探测:增加速率以响应到达的ACK,除非出现丢包事件,此时才减小传输速率
2. TCP拥塞控制算法
TCP拥塞控制是通过TCP拥塞控制算法实现的,该算法分为3个部分:慢启动、拥塞避免和快速恢复。其中,慢启动和拥塞避免是TCP的强制部分,两者的差异在于对收到的ACK做出反应时增加cwnd长度的方式。
以下是TCP拥塞控制的FSM描述:
慢启动
- 慢启动阶段,cwnd的值以1个MSS开始并且每当传输的报文段首次被确认就增加一个MSS。
- 慢启动的效果是,每过一个RTT,发送速率就翻番(指数增长)
-
慢启动的结束
- 当出现一个由超时指示的丢包事件,TCP发送方将慢启动阈值ssthresh设置为cwnd/2,并将cwnd置为1个MSS,重新开始慢启动。
- 当到达ssthresh时,结束慢启动,进入拥塞避免状态
- 当检测到3个冗余ACK时,进入快速重传并进入快速恢复状态
拥塞避免
- 进入拥塞避免状态时,cwnd的值大约是上一次遇到拥塞时的值的一半。
- 每个RTT只将cwnd的值增加一个MSS。
-
拥塞避免的结束
- 当出现超时指示的丢包事件,ssthresh的值被更新为cwnd/2,并将cwnd置为1个MSS,重新开始慢启动。
- 当检测到3个冗余ACK时,TCP将ssthresh的值被更新为cwnd/2,并将cwnd的值减半,进入快速恢复期
快速恢复
- 对于引起TCP进入快速恢复状态的报文段,对收到的每个冗余ACK,cwnd的值增加1个MSS。
-
快速恢复的结束
- 当对丢失报文段的一个ACK到达时,TCP在降低cwnd后进入拥塞避免状态。
- 当出现超时事件,则与慢启动、拥塞避免的操作相同:ssthresh的值被更新为cwnd/2,并将cwnd置为1个MSS,重新开始慢启动。
TCP的平均吞吐量
-
在忽略一条连接初始的慢启动阶段,假设丢包由3个冗余的ACK而不是超时指示的。TCP的拥塞控制是:每个RTT内cwnd线性增加1MSS,出现3个冗余ACK事件时cwnd减半。因此TCP拥塞控制常常被称为
加性增、乘性减
拥塞控制方式。
-
假定TCP在丢包事件发生时,发送方的窗口长度是W字节,当前的往返事件为RTT,且连接持续期间W和RTT几乎不变,则TCP的传输速率在W / (2 * RTT)和W / RTT之间变化。可以得到
一条连接的平均吞吐量为:0.75 * W / RTT
- 在经过高带宽路径的TCP连接情况下,例如考虑一条具有1500字节报文段和100ms RTT的TCP连接,假设要在该连接上实现10Gbps的吞吐量,平均拥塞窗口将需要83333个报文段。这些报文段能以何种比例丢失才能达到10Gbps呢?
-
假定丢包率为L,往返时间为RTT,最大报文长度为MSS,可以得到一条连接的平均吞吐量为:1.22
×\times
×
MSS / RTT
L\sqrt[]{L}
L
2. 公平性
-
考虑K条TCP连接,每条都有不同的端到端路径,但都经过一条传输速率为R bps的瓶颈链路。每条连接都在传输一个大文件,而且无UDP流量通过该段瓶颈链路。如果每条连接的平均传输速率接近R/K,则认为该拥塞控制机制是
公平
的。 -
TCP趋于在竞争的多条TCP连接之间提供对一段瓶颈链路带宽的平均分享。
- 在实际情况中,具有较小RTT的链接能够在链路空闲时更快地抢到可用带宽,因而比哪些具有较大RTT的连接享用更高的吞吐量。
- 当有UDP流量通过时,由于UDP没有内置的拥塞控制,因此UDP源有可能压制TCP流量
- 即使迫使UDP流量具有公平的行为,也没有办法阻止基于TCP的应用使用多个并行连接,使得应用占用的带宽更高。
3. 明确拥塞通知:网络辅助拥塞控制
-
明确拥塞通告(Explict Congestion Notification, ECN)
允许网络明确向TCP发送方和接收方发出拥塞信号 - 在网络层,IP数据报首部的服务类型字段中的两个比特被用于ECN。
- 路由器所使用的一种ECN比特设置指示该路由器正在经历拥塞,该拥塞指示由被标记的IP数据报所携带,送给目的主机,再由目的主机通知发送主机
- 接收主机中的TCP通过一个接收到的数据报收到一个ECN拥塞指示,在给发送发的ACK报文段中设置ECN比特。发送法接收到后通过减半拥塞窗口做出反应,并在下一个发送的TCP报文段首部中对CWR(拥塞窗口减半)比特进行设置。