运输层概述
计算机网络体系结构中的物理层、数据链路层以及网络层共同解决了将主机通过异构网络互联起来所面临的问题,实现了主机到主机的通信
但实际上在计算机网络中进行通信的真正实体是位于通信两端主机中的进程
AP1和AP2是主机A中的与网络通信相关的两个应用进程
AP3和AP4是主机B中的与网络通信相关的两个应用进程
如何为运行在不同主机上的应用进程提供直接的通信服务是运输层的任务,运输层协议又称为端到端协议
从计算机网络体系结构的角度看运输层
假设主机A中的应用进程AP1与主机B中的应用进程AP4进行基于网络的通信、主机A中的AP2与主机B中的AP3进行基于网络的通信
主机A中的运输层使用不同的端口对应不同的应用进程,然后通过网络层及其下层传输应用层报文
主机B中的运输层通过不同的端口将收到的应用层报文交付给应用层中相应的应用进程
注意:这里的“端口”并不是看得见摸得着的物理端口,而是用来区分不同应用进程的标识符
运输层向应用层实体屏蔽了下面网络核心的细节(如网络拓扑、所采用的路由选择协议等),它使应用进程看见的就好像是在两个运输层实体之间有一条端到端的逻辑通信信道
根据应用需求的不同,因特网的运输层为应用层提供了两种不同的运输协议,即面向连接的TCP和无连接的UDP
TCP/TP体系结构运输层中的两个重要协议
TCP/TP体系结构运输层中的两个重要协议:TCP和UDP
TCP/IP体系结构的网络接口层并没有规定什么具体内容,这是为了可以使用各种不同的网络接口
TCP/IP体系结构网际层中的协议IP是网际层中的核心协议,它可以互连不同的网络接口,也就是互连各种异构网络,并为其上层提供无连接不可靠的数据传输服务
TCP/IP体系结构的应用层中包含有大量的应用层协议,其中有些应用层协议需要使用可靠传输服务,例如浏览网页、文件传输、电子邮件以及电子银行等应用所涉及的应用层协议都需要使用可靠传输服务,否则传输错误可能会造成灾难性的后果,而有些应用层协议使用不可靠传输服务,例如音频、视频等多媒体应用所涉及的应用层协议,实时性使它们的首要需求,而少量传输错误对播放质量产生的影响较小,可以满足应用需求
TCP/IP体系结构运输层的首要任务就是使用端口号为运行在不同主机上的应用进程提供逻辑通信服务
除此之外,由于TCP/IP体系结构网际层中的IP协议向其上层提供的是无连接不可靠的数据报传输服务,因此在TCP/IP体系结构中的运输层中使用TCP协议为应用层中那些需要使用可靠传输服务的协议提供可靠传输服务,使用UDP协议为应用层中那些使用不可靠传输服务的协议提供不可靠传输服务
不管在运输层使用的是UDP协议提供的服务还是TCP协议提供的服务,在网际层都需要使用IP协议提供的服务
TCP协议的要点
-
传输控制协议(Transmission Control Protocol,TCP)为其上层提供的是面向连接的可靠数据传输服务
-
使用TCP通信的双方,在传送数据之前必须首先建立TCP连接(逻辑连接,非物理连接)
数据传输结束后必须要释放TCP连接
-
TCP为了实现可靠传输,就必须使用很多措施,例如TCP连接管理、确认机制、超时重传、流量控制以及拥塞控制等
-
TCP的实现复杂,TCP报文段的首部比较大,占用处理机资源比较多
UDP协议的要点
- 用户数据报协议(User Datagram Protocol,UDP)为其上层提供的是无连接的不可靠数据传输服务
- 使用UDP通信的双方,在传送数据之前不需要建立连接
- UDP不需要实现可靠传输,因此不需要使用实现可靠传输的各种机制
- UDP的实现简单,UDP用户数据报的首部比较小
因特网中的一些典型应用所使用的TCP/IP应用层协议和相应的运输层协议
| 因特网应用 | TCP/IP应用层协议 | TCP/IP运输层协议 |
|---|---|---|
| 域名解析 | 域名系统DNS | UDP |
| 文件传送 | 简单文件传送协议TFTP | UDP |
| 路由选择 | 路由信息协议RIP | UDP |
| 网络参数配置 | 动态主机配置协议DHCP | UDP |
| 网络管理 | 简单网络管理协议SNMP | UDP |
| 远程文件服务器 | 网络文件系统NFS | UDP |
| IP电话 | 专用协议 | UDP |
| 流媒体通信 | 专用协议 | UDP |
| IP多播 | 网际组管理协议IGMP | UDP |
| 电子邮件 | 简单邮件传送协议SMTP | TCP |
| 远程终端接入 | 电传机网络TELNET | TCP |
| 万维网 | 超文本传送协议HTTP | TCP |
| 文件传送 | 文件传送协议FTP | TCP |
运输层端口号、复用与分用的概念
运行在计算机上的进程使用进程标识符PID(Process Identification)来标识
但因特网上的计算机并不是使用统一的操作系统,不同的操作系统使用的进程标识符的格式也不相同
为了使运行不同操作系统的计算机的应用进程之间能够进行网络通信,就必须使用统一的方法对TCP/IP体系结构中的计算机中的应用进程进行标识
TCP/IP体系的运输层使用端口号来区分应用层的不同应用进程
端口号使用16个比特表示,取值范围:0~65535
端口号分为两种:
-
服务器端使用的端口号
熟知端口号(又称全球通用端口号):0~1023,IANA把这些端口号指派给了TCP/IP体系中最重要的一些应用协议。常用的一些服务器端的应用层协议的熟知端口号如下表所示
FTP SMTP DNS DHCP HTTP HTTPS BGP RIP 21/20 25 53 67/68 80 443 179 520 登记端口号(又称注册端口号):1024~49151,为没有熟知端口号的应用程序使用,使用这类端口号必须在IANA按照规定的手续登记,以防止重复。例如:Mircosoft RDP微软远程桌面使用的端口是3389
-
客户端使用的端口号
短暂端口号(又称临时端口号):49152~65534,仅在客户端使用,由客户进程在运行时动态选择,当服务器进程收到来自客户进程的报文时,就知道客户进程所使用的临时端口号,因而就可以把响应报文发送给客户进程。通信结束后已使用的临时端口号会被系统收回,以便给其他客户进程使用
端口号只具有本地意义,即端口号只是为了标识本计算机网络协议栈应用层中的各应用进程
在因特网中,不同计算机中的相同端口号是没有联系的,即相互独立
另外,TCP和UDP端口号之间也是没有关系的
发送方的复用和接收方的分用
发送方的某些应用进程所发送的不同应用报文,在运输层使用UDP协议进行封装,这称为UDP复用
而另一些应用进程所发送的不同应用报文,在运输层使用TCP协议进行封装,这称为TCP复用
运输层使用端口号来区分不同的应用进程,不管是运输层的UDP协议封装的UDP用户数据报还是TCP协议封装的TCP报文段,在网络层都需要使用IP协议封装成IP数据报,这称为IP复用
IP数据报中协议字段的值,用来表明IP数据报的数据载荷封装的是何种协议数据单元,取值为6,表示封装的是TCP报文段;取值为17,表示封装的是UDP用户数据报
接收方的网络层接收到IP数据报后进行IP分用,即若IP数据报首部协议字段的值为17,则把IP数据报的数据载荷部分所封装的UDP用户数据报,上交运输层的UDP;若字段值为6,则把IP数据报的数据载荷部分所封装的TCP报文段,上交运输层的TCP
运输层对UDP用户数据报进行UDP分用,对TCP报文段进行TCP分用,也就是根据端口号,将它们交付给上层相应的应用进程
TCP/TP体系的应用层常用协议所使用的运输层熟知端口号
注意:OSPF并不使用运输层的UDP或TCP进行封装,而是直接使用网际层的IP协议进行封装,封装其的IP数据报首部协议字段的值为89
| TCP/TP体系的应用层常用协议 | 端口号 | 协议在运输层使用的协议 |
|---|---|---|
| RIP | 520 | UDP |
| DNS | 53 | UDP |
| TFTP | 69 | UDP |
| SNMP | 161 | UDP |
| DHCP | 67/68 | UDP |
| SMTP | 25 | TCP |
| FTP | 21/20 | TCP |
| BGP | 179 | TCP |
| HTTP | 80 | TCP |
| HTTPS | 443 | TCP |
运输层端口号应用实例
用户PC、DNS服务器以及Web服务器通过以太交换机进行互连,它们处于同一个以太网中
Web服务器的域名为www.porttest.net
在DNS服务器中记录有该域名所对应的IP地址
在用户PC中使用网页浏览器访问Web服务器的内容
一般情况下,用户只知道Web服务器的域名,而不知道相应的IP地址,这需要用户PC首先通过Web服务器的域名获取其相应的IP地址,然后才能通过Web服务器的IP地址访问Web服务器
在用户PC中的网页浏览器的地址栏中输入Web服务器的域名,之后用户PC中的DNS客户进程会发送一个DNS查询请求报文,其内容为“域名www.posttest.net对应的IP地址是什么”
DNS查询请求报文需要使用运输层的UDP协议封装成UDP用户数据报,其首部中的源端口字段的值由系统在短暂端口号49152~65535中挑选一个未被占用的,用来表示DNS客户进程,本例使用49152作为UDP用户数据报首部的源端口号;目的端口字段的值设置为53,这是DNS服务器进程所使用的熟知端口号
用户PC将UDP用户数据报封装在IP数据报中,通过以太网发送给DNS服务器
DNS服务器收到该IP数据报后,从中解封出UDP用户数据报,UDP用户数据报首部中的目的端口号为53,这表明应将该UDP用户数据报的数据载荷,也就是DNS查询请求报文交付给本服务器中的DNS服务器进程
DNS服务器进程解析DNS查询请求报文的内容,然后按其要求查询对应的IP地址,之后会给用户PC发送DNS响应报文
DNS响应报文的内容为“域名www.posttest.net对应的IP地址是192.168.0.3”
DNS响应报文需要使用运输层的UDP协议封装成UDP用户数据报,其首部中的源端口号字段的值设置为熟知端口号53,表明这是DNS服务器进程所发送的UDP用户数据报;目的端口字段的值设置为49152,这是之前用户PC中发送DNS查询请求报文的DNS客户进程所使用的短暂端口号
DNS服务器将UDP用户数据报封装在IP数据报中,通过以太网发送给用户PC,用户PC收到该IP数据报后从中解封出UDP用户数据报
UDP用户数据报首部中的目的端口号为49152,这表明应将该UDP用户数据报的数据载荷也就是DNS响应报文交付给PC中的DNS客户进程
DNS客户进程解析DNS响应报文的内容,就可知道自己之前所请求的Web服务器的域名所对应的IP地址为192.168.0.3
DNS客户进程将所使用的短暂端口号49152归还给系统,以便其他进程可以选择使用
现在,用户PC中的HTTP客户进程可以向Web服务器发送HTTP请求报文了
HTTP请求报文的内容是“首页内容是什么”
HTTP请求报文需要使用运输层的TCP协议封装成TCP报文段,其首部中的源端口字段的值由系统在短暂端口号49152~65535中挑选一个未被占用的,用来表示HTTP客户进程,例如仍然使用之前使用过的49152;目的端口字段的值设置为80,这是HTTP服务器进程所使用的熟知端口号
用户PC将TCP报文段封装在IP数据报中通过以太网发送给Web服务器
Web服务器收到该IP数据报后从中解封出TCP报文段,TCP报文段首部中的目的端口号为80,这表明应将该TCP报文段的数据载荷,也就是HTTP请求报文交付给HTTP服务器中的HTTP服务器进程
HTTP服务器进程解析HTTP请求报文的内容,然后按其要求查找首页内容,之后会给用户PC发送HTTP响应报文
HTTP响应报文的内容就是HTTP客户端所请求的首页内容
HTTP响应报文需要使用运输层的TCP协议封装成TCP报文段,其首部中源端口字段的值设置为熟知端口号80,表明这是HTTP服务器进程所发送的TCP报文段;目的端口字段的值设置为49152,这是之前用户PC中发送的HTTP请求报文的HTTP客户进程所使用的短暂端口号
Web服务器将TCP报文段封装在IP数据报中通过以太网发送给用户PC,用户PC收到该IP数据报后,从中解封出TCP报文段
TCP报文段首部中的目的端口号为49152,这表明应将该TCP报文段的数据载荷也就是HTTP响应报文交付给用户PC中的HTTP客户进程
HTTP客户进程解析HTTP响应报文的内容并在网页浏览器解析渲染显示
这样,用户就可以在网页浏览器中看到Web服务器所提供的首页内容了
HTTP客户进程将所使用的短暂端口号49152归还给系统,以便其他进程可以选择使用
UDP和TCP的对比
TCP/IP体系结构中的运输层为其应用层提供了两个不同的运输层协议,即UDP和TCP
这两个协议的使用频率仅次于网际层的IP协议
其中UDP向应用层提供的是无连接的不可靠的数据传输服务;TCP向应用层提供的是面向连接的可靠的数据传输服务
由于UDP仅提供不可靠的数据传输服务,因此UDP非常简单;而TCP提供的是可靠数据传输服务,因此TCP非常复杂
UDP(User Datagram Protocol,用户数据报协议)和TCP(Transmission Control Protocol,传输控制协议)是TCP/IP体系结构运输层中两个重要协议
UDP是无连接的,TCP是面向连接的
如图,左边是使用UDP的通信双方,纵坐标为时间;右边是使用TCP的通信双方,纵坐标为时间
由于UDP是无连接的,因此在传送数据之前不需要建立连接,可以随时发送数据
由于TCP是面向连接的,因此在传送数据之前必须使用三报文握手来建立TCP连接
TCP连接建立成功后才能基于建立好的TCP连接进行数据传输
数据传输结束后必须使用四报文挥手来释放TCP连接
注意:这里所谓的连接是指逻辑连接关系,而不是物理连接
左边的某个局域网上有四台使用UDP进行通信的主机,其中任何一台主机都可向其他三台主机中的任何一个发送单播,也可以向其他三台主机发送广播,还可以向某个多播组发送多播
终上所述,UDP支持单播、多播和广播,也就是UDP支持一对一、一对多以及一对全的通信
使用TCP协议的通信双方,在数据传输之前必须使用三报文握手建立TCP连接
TCP连接建立成功后,通信双方之间就好像有一条可靠的通信信道,通信双方使用这条基于TCP连接的可靠信道进行通信
显然,TCP仅支持单播,也就是一对一的通信
UDP对应用层报文的处理情况:发送方的应用进程将应用层报文向下交付给运输层的UDP,UDP直接给应用层报文添加一个UDP首部使之成为UDP用户数据报,然后进行发送
为了简单起见,我们忽略运输层下面的各层处理
接收方的UDP收到UDP用户数据报后去掉UDP首部,将应用层报文向上交付给应用进程
终上所述,UDP对应用进程交付下来的报文既不合并也不拆分,而是保留这些报文的边界,也就是UDP是面向应用报文的
TCP对应用层报文的处理情况:发送方的TCP把应用进程交付下来的应用报文仅仅看作一连串的无结构的字节流,TCP并不知道这些待传输的字节流的含义,仅将它们编号并存储在自己的发送缓存中
TCP根据发送策略,从发送缓存中提取一定数量的字节构建TCP报文段并进行发送
接收方的TCP一方面从所接收到的TCP报文段中取出数据载荷并存储在接收缓存中,另一方面,将接收缓存中的一些字节向上交付给应用进程
TCP不保证接收方应用进程所接收到的数据块与发送方应用进程所发出的应用层报文之间具有对应大小关系
例如发送方应用进程交给发送方的TCP共10个应用层报文,但接收方的TCP可能只用了4个数据块就把收到的字节流交付给上层的应用进程,但接收方应用进程收到的字节流必须和发送方应用进程发出的字节流完全一样
当然,接收方的应用进程必须有能力识别收到的字节流,把他还原成有意义的应用层报文
终上所述,TCP是面向字节流的,这正是TCP实现可靠传输、流量控制以及拥塞控制的基础
需要说明的是,为了突出示意图要点,只画出了一个方向的数据流,在实际的网络中,基于TCP连接的两端可以同时进行TCP报文段的发送和接收,也就是全双工通信
另外,图中TCP报文段的数据载荷只包含几个字节,在实际中,一个TCP报文段常常包含上千个字节
UDP对数据传输可靠性的支持情况:TCP/IP体系结构的网际层向其上层提供的是无连接不可靠的数据传输服务,当运输层使用UDP时,UDP向其上层提供的也是无连接不可靠的数据传输服务
发送方给接收方发送UDP用户数据报,若传输过程中,UDP用户数据报受到干扰而产生误码,接收方UDP可以通过该UDP用户数据报首部中的检验和字段的值检查出产生误码的情况,但仅仅丢弃该UDP用户数据报,其他什么也不做
如果发送方给接收方发送的UDP用户数据报被因特网中的某个路由器丢弃了,这可能是由于路由器太忙或路由器检查出封装该UDP用户数据报的IP数据报的首部出现了误码,发送方UDP也不做任何处理,因为UDP向其上层提供的也是无连接不可靠的是数据传输服务
基于UDP的这些特点,UDP适用于实时应用,例如IP电话和视频会议等
TCP对数据传输可靠性的支持情况:尽管TCP/IP体系结构中的网际层的IP协议向其上层提供的是无连接不可靠的数据传输服务,也就是IP数据报可能在传输过程中出现丢失或误码,但只要运输层使用TCP协议,收发双方就可基于TCP连接的可靠信道进行数据传输,不会出现误码、丢失、失序和重复等传输差错
终上所述,TCP向上层提供面向连接的可靠传输服务
基于TCP的这个特点,TCP适用于要求可靠传输且对实时性要求不高的应用,例如文件传输和电子邮件等
左边是UDP用户数据报的结构,它由首部和数据载荷两部分构成,UDP首部格式如图所示,仅有四个字段,每个字段长度为2个字节,由于UDP不提供可靠传输服务,它仅仅在网际层的基础上添加了用于区分应用进程的端口,因此它的首部非常简单,仅有8个字节
TCP报文段也由首部和数据载荷两部分构成,TCP首部格式如右边图所示。它比UDP首部复杂得多,其最小长度为20字节,最大长度60字节,TCP要实现可靠传输、流量控制、拥塞控制等服务,其首部自然会比较复杂
UDP(User Datagram Protocol,用户数据报协议):
-
UDP是无连接的
-
UDP支持单播、多播以及广播
-
UDP是面向应用报文的
-
UDP向上层提供无连接不可靠的传输服务(适用于IP电话、视频会议等实时应用)
-
一个UDP用户数据报由首部和数据载荷两部分构成,其首部仅有四个字段(源端口、目的端口、长度、检验和),每个字段均为2个字节,(首部)共8字节
UDP用户数据报首部的检验和字段是对整个UDP用户数据报进行差错检验,校验和是可选择使用的字段
UDP首部中的长度字段记录的是整个UDP数据报的长度
TCP(Transmission Control Protocol,传输控制协议):
- TCP是面向连接的
- TCP仅支持单播(因为TCP是基于已建立的连接进行通信的,而TCP连接是一对一的连接)
- TCP是面向字节的
- TCP向上层提供面向连接的可靠传输服务(适用于要求可靠传输的应用,例如文件传输、电子邮件)
- 一个TCP报文段由首部和数据载荷两部分构成,其首部最小长度为20字节,最大长度为60字节,包含的字段也非常多
TCP报文段的首部格式
为了实现可靠传输,TCP采用了面向字节流的方式,但TCP在发送数据时,是从发送缓存中取出一部分或全部字节并给其添加一个首部使之成为TCP报文段后进行发送
一个TCP报文段由首部和数据载荷两部分构成
TCP的全部功能都体现在它首部中各字段
TCP报文段的首部格式
-
源端口
占16比特,写入源端口号,用来标识发送该TCP报文段的应用进程
-
目的端口
占16比特,写入目的端口号,用来标识接收该TCP报文段的应用进程
-
序号seq
占32比特,取值范围0 ~ 2^32^ - 1
当序号增加到最后一个时,下一个序号就又回到0
用来指出本TCP报文段数据载荷的第一个字节的序号
-
确认号ack
占32比特,取值范围0 ~ 2^32^ - 1
当序号增加到最后一个时,下一个序号就又回到0
用来指出期望收到对方下一个TCP报文段的数据载荷的第一个字节的序号,同时也是对之前收到的所有数据的确认
若确认号 = n,则表明到序号n - 1为止的所有数据都已正确接收,期望接收序号为n的数据
侧面表明接收的TCP报文段首部中的确认号是对已正确接收且按序到达到的最后一个TCP报文段的确认
-
确认标志位ACK
只有ACK取值为1时确认号字段ack才有效
ACK取值为0时确认号字段ack无效
TCP规定,在连接建立后所有传送的TCP报文段都必须把ACK置1
序号、确认号以及确认标志位的作用:
假设TCP客户与TCP服务器已通过三报文握手建立了TCP连接,纵坐标为时间
假设TCP客户进程给TCP服务器进程发送了一个TCP报文段,在TCP报文段首部中序号字段的取值为201,表示该TCP报文段数据载荷的第一个字节的序号为201
该报文段的数据载荷长100字节,因此,数据载荷的最后一个字节的序号为300
TCP报文段首部中确认号字段的取值为800,表示TCP客户进程之前已正确接收了TCP服务器进程发来的序号到799为止的全部数据,现在期望收到序号从800开始的数据
为了使确认号字段有效,首部中的确认标志位ACK的值必须设置为1
TCP服务器进程收到该报文段后给TCP客户进程发送一个TCP报文段,该报文段首部中序号字段的取值为800,表示该TCP报文段数据载荷的第一个字节的序号为800,这正好与TCP客户进程的确认相匹配
TCP报文段首部中确认号字段的取值为301,表示TCP服务器进程正确接收了TCP客户进程发来的序号到300为止的全部数据,现在期望收到序号从301开始的数据,为了使确认号字段有效,首部中的确认标志位ACK的值必须设置为1
-
数据偏移
占4比特,该字段的取值以4字节为单位
用来指出TCP报文段的数据载荷部分的起始处距离TCP报文段的起始处有多远,实际上就是指出了TCP报文段的首部长度
首部最小长度为20字节,因此数据偏移字段的最小值为(0101)
2首部最大长度为60字节,因此数据偏移字段的最大值位(1111)
2
-
保留
占6比特,保留为今后使用,目前应置为0
-
窗口
占16比特,该字段的取值以字节为单位
指出发送本报文段的一方的接收窗口大小,即接收缓存的可用空间大小,这用来表征接收方的接收能力
窗口值作为接收方让发送方设置其发送窗口的依据
这是以接收方的接收能力来控制发送方的发送能力,称为流量控制
注意:发送方的发送窗口的大小还取决于拥塞窗口的大小,即
TCP发送方的发送窗口的大小 = Min[TCP接收方的接收窗口大小, 自身的拥塞窗口大小]如果题目未给出TCP接收方的接收窗口大小,则取拥塞窗口大小为自己的发送窗口尺寸
-
校验和
占16比特,用来检查整个TCP报文段在传输过程中是否出现了误码
在计算校验和时,要在TCP报文段的前面加上12字节的伪首部(UDP计算检验和时也是如此)
伪首部的格式与UDP用户数据报的伪首部一样
左起第三个字段是保留字节,应置为0
第四个字段(协议字段)的值为6,这表明伪首部后面跟着的是TCP报文段(而17表示UDP用户数据报)
第五字段为TCP长度,表明了TCP报文段的长度(不包括伪首部)
接收方接收到此报文段后,仍要加上这个伪首部来计算检验和
发送方检验和计算方法:
将TCP首部中检验和字段的值置为0
将伪首部、TCP首部以及数据载荷这三部分划分成若干个2字节的字
若伪首部、TCP首部以及数据载荷这三部分的总长度不是偶数个字节,则在最后添加一个“全0”字节
对划分出的全部2字节的字进行反码算数运算求和,并将求和结果取反码
将步骤3得到的结果写入TCP首部中的检验和字段
这与IPv4数据报首部检验和的计算方法类似,区别仅在于前者只对首部进行检验
接收方通过检验和字段的值判断TCP报文段在传输过程中是否误码的方法:
- 给接收到的TCP报文段前面添加一个12字节的伪首部
- 将伪首部、TCP首部以及数据载荷这三部分划分成若干个2字节的字
- 对划分的全部2字节的字进行反码算数运算求和,并将求和结果取反码
- 若步骤3得到的结果为0,则表示TCP报文段在传输过程中没有出现误码,否则表示出现了误码
-
同步标志位SYN
用于在TCP连接建立时同步序号
当SYN = 1且ACK = 0时,表明这是一个TCP连接请求报文段
对方若同意建立连接,则应在响应的TCP报文段的首部中使SYN = 1且ACK = 1
SYN为1的TCP报文段要么是TCP连接请求报文段,要么是TCP连接响应(确认)报文段,这两个报文段不能携带数据,但要消耗掉一个序号
-
终止标志位FIN
用来释放TCP连接
当FIN = 1时,表明此TCP报文段的发送方已经将全部是数据发送完毕,现在要求释放TCP连接
FIN为1的TCP报文段是TCP连接释放报文段,该报文段不能携带数据,但要消耗掉一个序号
-
复位标志位RST
用来复位TCP连接
当RST = 1时,表明TCP连接中出现了异常,必须释放连接,然后再重新建立连接
RST置1还可以用来拒绝一个非法的报文段或拒绝打开一个TCP连接
-
推送标志位PSH
出于效率的考虑,TCP的发送方可能会延迟发送数据,而TCP的接收方可能会延迟向应用进程交付数据,这样可以一次处理更多的数据
但是当两个应用进程进行交互式通信时,有时在一端的应用进程希望在键入一个命令后立即就能收到对方的响应,在这种情况下,应用进程可以通知TCP使用推送(PUSH)操作
发送方TCP把PSH置为1,并立即创建一个TCP报文段发送出去,而不需要积累到足够多的数据再发送
接收方TCP收到PSH为1的报文段,就会尽快上交给应用进程,而不再等到接收到足够多的数据后才向上交付
-
紧急标志位URG
取值为1时紧急指针字段有效;取值为0时紧急指针字段无效
-
紧急指针
占16比特,以字节为单位,用来指明紧急数据的长度
当发送方有紧急数据时,可将紧急数据插队到发送缓存的最前面,并立刻封装到一个TCP报文段中进行发送
紧急指针会指出本报文段数据载荷部分包含了多长的紧急数据,紧急数据之后是普通数据
接收方收到紧急标志位为1的报文段,会按照紧急指针字段的值从报文段的数据载荷部分取出紧急数据,并直接上交应用进程,而不必在接收缓存中排队
-
选项
该字段可以增加TCP报文段的功能,目前有以下功能:
最大报文段长度MSS选项:指出TCP报文段数据载荷部分的最大长度
窗口扩大选项:为了扩大窗口(提高吞吐率)
时间戳选项:用来计算往返时间TTL;用于处理序号超范围的情况,又成为防止序号绕回PAWS
选择确认选项:用来实现选择确认功能
-
填充
若选项字段的长度加上20字节的固定首部的长度不能被4字节整除时,需要填充相应数量的比特0,以确保首部长度能被4字节整除(因为数据偏移字段,即首部长度字段是以4字节为单位的)
TCP的运输连接管理 — TCP的连接建立
TCP是面向连接的协议,它基于运输连接来传送TCP报文段
TCP运输连接的建立和释放是每一次面向连接的通信中必不可少的过程
TCP运输连接有以下三个阶段:
- 通过“三报文握手”来建立TCP连接
- 基于已建立的TCP连接进行可靠的数据传输
- 在数据传输结束后,通过“四报文挥手”来释放TCP连接
TCP的运输连接管理就是使运输连接的建立和释放都能正常地进行
TCP的连接建立的目的在于解决以下三个问题:
- 使TCP双方能够确知对方的存在
- 使TCP双方能够协商一些参数(如最大窗口值、是否使用窗口扩大选项和时间戳选项以及服务质量等)
- 使TCP双方能够对运输实体资源(如缓存大小、连接表中的项目等)进行分配和初始化
TCP使用“三报文握手”建立连接的具体过程
假设存在两台要基于TCP进行通信的主机,其中一台主机中的某个应用进程主动发起TCP连接建立,称为TCP客户
另一台主机中被动等待TCP连接建立的应用进程,称为TCP服务器
可以将TCP建立连接的过程比喻为“握手”
“握手”需要在TCP客户和服务器之间交换三个TCP报文段
最初,两端的TCP进程都处于关闭状态
一开始,TCP服务器进程首先创建传输控制块,用来存储TCP连接中的一些重要信息,例如:TCP连接表、指向发送和接收缓存的指针、指向重传队列的指针、当前的发送和接收序号等
之后,TCP服务器进程就进入监听状态,等待TCP客户进程的连接请求
TCP服务器进程被动等待来自TCP客户进程的连接请求,而不是主动发起,因此称为被动打开连接
TCP客户进程也是首先创建传输控制块
然后,在打算建立TCP连接时,向TCP服务器进程发送TCP连接请求报文段,并进入同步已发送状态
TCP连接请求报文段首部中的同步位SYN被设置为1,表明这是一个TCP连接请求报文段
序号字段seq被设置了一个初始值x,作为TCP客户进程所选择的初始序号
由于TCP连接建立是由TCP客户主动发起的,因此称为主动打开连接
TCP服务器进程收到TCP连接请求报文段后,如果同意建立连接,则向TCP客户进程发送TCP连接请求确认报文段,并进入同步已接收状态
该确认报文段首部中的SYN和确认位ACK都设置为1,表明这是一个TCP连接请求确认报文段
序号字段seq被设置了一个初始值y,作为TCP服务器进程所选择的初始序号
确认号字段ack的值被设置为x+1,这是对TCP客户进程所选择的初始序号的确认
TCP客户进程收到TCP连接请求确认报文段后,还要向TCP服务器进程发送一个普通的TCP确认报文段,并进入连接已建立状态
该报文段首部中的确认标志位ACK被设置为1,表明这是一个普通的TCP确认报文段,序号字段seq被设置为x+1,这是因为TCP客户进程之前发送的TCP连接请求报文段的序号为x,该报文段虽然不能携带数据但要消耗一个序号,因此TCP客户端进行发送的第二个报文段的序号为x+1
确认号字段ack被设置为y+1,这是对TCP服务器进程所选择的初始序号的确认
TCP服务器进程收到该确认报文段后也进入连接已建立状态
现在,TCP双方都进入了连接已建立状态,可以基于已建立的TCP连接进行可靠的数据传输了
注意:
- TCP规定:SYN被设置为1的报文段不能携带数据(如上面的TCP连接请求报文段和TCP连接请求确认报文段),但要消耗一个序号
- TCP规定:普通的TCP确认报文段可以携带数据,但如果不携带数据,则不消耗序号
思考
为什么在TCP建立的过程中,TCP客户进程最后还要发送一个普通的 TCP确认报文段,这是否是多余的?(换句话说,“三报文握手”是否可以替换为“两报文握手”?)
答:发送针对TCP连接请求的确认的确认是必要的,因为这是为了防止已失效的连接请求报文段突然又传送到了TCP服务器,因而导致错误
“两报文握手”建立连接举例:假设TCP客户进程发出一个TCP连接请求报文段,但该报文段在某些网络节点长时间滞留了,这必然会造成该报文段的超时重传
假设重传的连接请求报文段被TCP服务器进程正常接收,TCP服务器进程给TCP客户进程发送一个TCP连接请求确认报文段,并进入连接已建立状态
注意,由于现在改为两报文握手,因此TCP服务器进程发送完TCP连接请求确认报文段后进入的是连接已建立状态,而不像三报文握手那样进入同步已接收状态,同时也不会等待TCP客户进程发来针对TCP连接请求确认报文段的普通确认报文段
TCP客户进程收到TCP连接请求确认报文段后,进入TCP连接已建立状态,但不会给TCP服务器进程针对该报文段的普通确认报文段
现在,TCP双方都处于连接已建立状态,它们可以相互传输数据了
之后可以通过四报文挥手释放连接TCP双方都进入了关闭状态
一段时间后,之前滞留在网络中的那个失效的TCP连接请求报文段到达了TCP服务器进程,TCP服务器进程会误认为这是TCP客户进程又发起了一个新的TCP连接请求,于是给TCP客户进程发送TCP连接请求确认报文段,并进入连接已建立状态
TCP客户进程收到TCP连接请求确认报文段后,由于TCP客户进程并没有发起新的TCP连接请求,并且处于关闭状态,因此不会理会该TCP连接请求确认报文段
然而,TCP服务器进程已进入连接已建立状态,它认为新的TCP连接已建立好了,并一直等待TCP客户进程发来数据,这将白白浪费TCP服务器进程所在主机的很多资源
采用“三报文握手”而不是“两报文握手”来建立TCP连接,是为了防止已失效的TCP连接请求报文段突然又传送到了TCP服务器进程,因而导致错误
TCP的运输连接管理 — TCP的连接释放
TCP通过“四报文挥手”释放连接
TCP使用“四报文挥手”释放连接的具体过程
数据传输结束后,TCP通信双方都可以主动释放连接
现在TCP客户进程和TCP服务器进程都处于连接已建立状态,假设使用TCP客户进程的应用进程通知其主动关闭TCP连接,TCP客户进程会发送TCP连接释放报文段,并进入终止等待1状态
该报文段首部中的终止位FIN和确认标志位ACK的值都被设置为1,表明这是一个TCP连接释放报文段,同时也对之前收到的TCP报文段进行确认
序号seq字段的值设置为u,它等于TCP客户进程之前已传送过的、数据的最后一个字节的序号加1
确认号ack字段的值设置为v,它等于TCP客户进程之前已收到的、数据的最后一个字节的序号加1
TCP服务器进程收到TCP连接释放报文段后,会发送一个普通的TCP确认报文段并进入关闭等待状态,该报文段首部中的确认标志位ACK的值被设置为1,表明这是一个普通的TCP确认报文段
序号seq字段的值设置为v,它等于TCP服务器进程之前已传送过的数据的最后一个字节的序号加1,这也与之前收到的TCP连接释放报文段中的确认号匹配
确认号ack字段的值设置为u+1,这是对TCP连接释放报文段的确认
TCP服务器进程这时应通知高层应用进程:TCP客户进程要断开与自己的TCP连接
此时,从TCP客户进程到TCP服务器进程这个方向的连接就释放了
这时的TCP连接属于半关闭状态,也就是TCP客户进程已经没有数据要发送了,但TCP服务器进程如果还有数据要发送,TCP客户进程仍要接收,也就是说,从TCP服务器进程到TCP客户进程这个方向的连接并未关闭,这个状态(半关闭状态)可能会持续一段时间
TCP客户进程收到该普通的TCP确认报文段后就进入终止等待2状态,并等待TCP服务器进程发出的TCP连接释放报文段
若使用TCP服务器进程的应用进程已经没有数据要发送了,应用进程就通知其TCP服务器进程释放连接
由于TCP连接释放是由TCP客户进程主动发起的,因此TCP服务器进程对TCP连接的释放称为被动关闭连接
TCP服务器进程发送TCP连接释放报文段并进入最后确认状态,该报文段首部中的终止位FIN和确认标志位ACK的值都被设置为1,表明这是一个TCP连接释放报文段,同时也对之前收到的报文段进行确认
现在假定序号seq字段的值为w,这是因为在半关闭状态下,TCP服务器进程可能又发送了一些数据
确认号ack字段的值为u+1,这是对之前收到的来自TCP客户进程的TCP连接释放报文段的重复确认
TCP客户进程收到TCP连接释放报文段后,必须针对该报文段发送普通的TCP确认报文段,之后进入时间等待状态;该(确认)报文段首部中的确认标志位ACK的值被设置为1,表明这是一个普通的TCP确认报文段
序号seq字段的值设置为u+1,这是因为TCP客户进程之前发送的TCP连接释放报文段虽然不携带数据,但要消耗掉一个序号
确认号ack的值设置为w+1,这是对所收到的TCP连接释放报文段的确认
TCP服务器进程收到该报文段后就进入关闭状态,服务器进程撤销相应的传输控制块
而TCP客户进程还要经过2MSL后才能进入关闭状态,也就是说TCP客户进程进入时间等待状态后,还需要经过4分钟才能进入关闭状态
经过2MSL时间后,客户进程撤销相应的传输控制块,就结束了这次的TCP连接
MSL(Maximum Segment Lifetime)意思是最长报文段寿命,RFC793建议为2分钟
对于现代网络,MSL为2分钟可能太长了,因此TCP允许不同的实现可根据具体情况使用更小的MSL值
注意:TCP规定终止位FIN等于1的报文段即使不携带数据,也要消耗一个序号
思考
问:TCP客户进程在发送完最后一个确认报文段后,为什么还要等待2MSL后才能进入关闭状态?
不等待2MSL时间就直接进入关闭状态举例:TCP服务器进程发送TCP连接释放报文段后,进入最后确认状态
TCP客户进程收到该报文段后发送普通的TCP确认报文段,并进入关闭状态而不是时间等待状态
然而该TCP确认报文段丢失了,这必然会造成TCP服务器进程对之前所发送的TCP连接释放报文段的超时重传,并仍处于最后确认状态
重传的TCP连接释放报文段到达TCP客户进程,由于TCP客户进程处于关闭状态,因此不会理会该报文段,这必然会造成TCP服务器进程反复重传TCP连接释放报文段,并一直处于最后确认状态而无法进入关闭状态
处于时间等待状态后要经过2MSL时长,可以确保TCP服务器进程可以收到最后一个TCP确认报文段而进入关闭状态
另外,TCP客户进程在发送完最后一个TCP确认报文段后,再经过2MSL的时长,就可以使本次连接持续时间内所产生的所有报文段都从网络中消失,这样就可以使下一次新的连接中不会出现旧连接中报文段
TCP中保活计时器的作用
假设TCP双方已经建立了连接,后来TCP客户进程所在的主机突然出现了故障,因此TCP客户不可能再发送数据给TCP服务器
那么TCP服务器进程如何才能知道TCP客户出现了故障而无法发送数据呢?
实际上,TCP服务器进程使用保活计时器来解决上述问题
TCP服务器进程每收到一次TCP客户进程的数据,就重新设置并启动保活计时器(通常为2小时)
假设TCP客户进程所在主机突然出现了故障,也就不可能再发送数据给TCP服务器了
当TCP服务器中的保活计时器到时后,TCP服务器进程就向TCP客户进程发送一个探测报文段,以后则每隔75秒钟发送一次
若一连发送10个探测报文后仍无TCP客户进程的响应,TCP服务器进程就认为TCP客户进程所在主机出现了故障,接着就关闭这个连接
TCP的流量控制
流量控制的基本概念
如图是TCP客户和TCP服务器
TCP客户既可以作为发送方,也可以作为接收方
TCP服务器既可以作为接收方,也可以作为发送方
假设TCP客户作为发送方,TCP服务器作为接收方,也就是TCP客户给TCP服务器发送TCP数据报文段,TCP服务器正确接收后,给TCP客户发送相应的TCP确认报文段
TCP客户与TCP服务器首先要通过三报文握手建立TCP连接
成功建立TCP连接后,TCP客户进程将上层交付下来的报文看作是字节流,存入发送缓存,并根据发送策略,从发送缓存中取出一定数量的字节,将其封装成TCP报文段后发送出去
TCP服务器正确接收TCP报文段后,将其数据载荷暂存到接收缓存中,相应的应用程序会从该接收缓存中读取数据
然而,应用程序并不一定能够立即将接收缓存中的数据取走,这可能因为应用程序正忙于其他任务,需要经过较长的时间后才能从接收缓存中读取数据
如果应用程序从接收缓存中取走数据比较慢,而发送方持续快速发送大量数据,则很容易造成接收方的接收缓存溢出,也就是造成数据的丢失
TCP为应用程序提供了流量控制(Flow Control)机制,以解决因发送方发送数据太快而导致接收方来不及接收,造成接收方的接收缓存溢出的问题
流量控制的基本方法:接收方根据自己的接收能力(接收缓存的可用空间大小)控制发送方的发送速率,具体为接收方给发送方发送自己的接收窗口大小,发送方可以依此改变自己的发送窗口大小
如图,主机A和主机B已成功建立TCP连接,A给B发送数据,而B对A进行流量控制,纵坐标为时间
左上角是主机A待发送数据的字节序号,每个小格子表示100字节数据的序号
假设主机A发送的每个TCP数据报文段都携带100字节的数据;网络不会拥塞(不考虑TCP的拥塞控制);在A和B建立TCP连接时,B告诉A“我的接收窗口rwnd = 400”,因此A将自己的发送窗口swnd也设置为400,这意味着主机A在未收到主机B发来的确认时可将序号落入发送窗口中的全部数据发送出去
主机A将发送窗口内序号为1~100的数据封装成一个TCP报文段发送出去,此时发送窗口内还有300字节可以发送
seq是TCP报文段首部中的序号字段,其取值为1,表示该TCP报文段数据载荷的第一个字节的序号为1
由于假设每个TCP报文段都携带100字节的数据,因此该TCP报文段数据载荷的最后一个字节的序号为100
DATA表示这是TCP数据报文段
主机A将发送窗口内序号为101~200的数据封装成一个TCP报文段发送出去,(此时)发送窗口内还有200字节可以发送
主机A将发送窗口内序号为201~300的数据封装成一个TCP报文段发送出去,但该报文段在传输过程中丢失了,这可能是由于误码被路由器丢弃或路由器繁忙而丢弃
主机A还可发送100字节的数据,即序号落在发送窗口内的301~400号数据
此时主机B给主机A发送了累计确认报文段,对主机A之前所发送的201号以前的数据进行累计确认,该累计确认报文段首部中的确认标志位ACK被设置为1,表示这是一个TCP确认报文段,确认号ack字段的取值为201,表示序号201之前的数据已全部正确接收,现在期望收到序号201及其后序数据,窗口字段(也就是主机B的接收窗口rwnd的值)被设置为300,可简单认为现在主机B的接收缓存的可用空间为300字节,而不是之前的400字节,也就是对主机A进行了流量控制
主机A收到主机B发来的累计确认报文段后,将发送窗口向前滑动,使已发送并收到确认的数据的序号移出发送窗口,这些数据可从发送缓存中删除了
由于主机B在该累积确认报文段中将自己的接收窗口调整为了300,因此主机A相应地将自己的发送窗口调整为300,这样主机A的发送窗口内的序号为201~500,其中201~300号是已发送但还未收到确认的数据的序号,因此不能将这些数据从发送缓存中删除,因为有可能之后会超时重传这些数据
301~400号字节数据以及401~500号字节数据还未被发送,可被分别封装在一个TCP报文段中发送。到目前为止,主机B对主机A进行了一次流量控制
主机A将发送窗口内序号301~400号的数据封装成一个TCP报文段发送出去,发送窗口内还有401~500号共100字节可以发送
主机A将发送窗口内序号401~500号的数据封装成一个TCP报文段发送出去
至此,序号落在发送窗口内的是数据已经全部发送出去了,不能再发送新的数据了
一段时间后,主机A的发送窗口内序号201~300这100字节数据的重传计时器超时了,主机A将它们重新封装成一个TCP报文段发送出去
主机B收到该重传的TCP报文段后给主机A发送累计确认报文段,对主机A之前所发送的501号以前的数据进行累计确认
另外,主机B在该累计确认报文段中将自己的接收窗口调整为100,这是主机B对主机A进行的第二次流量控制
主机A收到主机B发来的累积确认报文段后,根据其首部中的确认号ack字段的值以及rwnd窗口字段的值将自己的发送窗口向前滑动相应的位置并缩小到相应的尺寸,这样就使已发送并收到确认的数据的序号移出发送窗口,这些数据可从发送缓存中删除了,而新序号501~600落入了发送窗口
到目前为止,主机B对主机A进行了两次流量控制
主机A将发送窗口内序号为501~600号的数据封装成一个TCP报文段发送出去,至此,序号落入发送窗口内的数据已经1全部发送出去了,不能再发送新的数据了
主机B给主机A发送累积确认报文段,对主机A之前所发送的601号以前的数据进行累积确认
另外,主机B在该累积确认报文段中将自己的接收窗口调整为0,这是主机B对主机A进行的第三次流量控制
主机A收到主机B发来的累积确认报文段后,根据其首部中的确认号ack字段的值以及窗口rwnd字段的值,将自己的发送窗口向前滑动相应的位置并缩小其尺寸到0,这样就使已发送并收到确认的数据的序号移出发送窗口,这些数据可从发送缓存中删除了
由于主机B在该累积确认报文段中将自己的接收窗口调整为了0,因此主机A相应地将自己的发送窗口调整为了0,所以主机A不能再发送普通的TCP报文段了
至此,主机B对主机A进行了三次流量控制
假设主机B向主机A发送了零窗口的报文段后不久,主机B的接收缓存又有了一些可用空间,于是主机B向主机A发送了接收窗口等于300的报文段
然而,这个报文段在传输过程中丢失了,主机A会一直等待主机B发送的非零窗口的通知,而主机B会一直等待主机A发送的数据
如果不采取措施,这种互相等待而形成的死锁局面将会一直持续下去
为了打破由于非零窗口通知报文段丢失而引起的双方相互等待的死锁局面,TCP为每一个连接都设有一个持续计时器
只要TCP连接的一方收到对方的零窗口通知,就启动持续计时器
当持续计时器超时时,就发送一个零窗口探测报文段,仅携带1字节的数据
对方在确认这个零窗口探测报文段时,给出自己现在的接收窗口值
如果接收窗口值仍为0,那么收到这个报文段的一方就重新启动持续计时器
如果接收窗口值不为0,那么死锁的局面就可以被打破了
假设这是主机B对主机A进行的第三次流量控制,主机A收到零窗口通知时,就将自己的发送窗口的尺寸调整为0,因此不能再发送数据报文段了,主机A会启动一个持续计时器
假设主机B向主机A发生了零窗口的报文段后不久,主机B的接收缓存又有了一些可用空间,于是主机B给主机A发送了接收窗口等于300的报文段,然而这个报文段在传输过程中丢失了
当主机A的持续计时器超时时,主机A立刻发送一个仅携带1字节数据的零窗口探测报文段
假设主机B此时的接收窗口值又为0了,主机B就在确认这个零窗口探测报文段时给出自己现在的接收窗口值为0
主机A再次收到零窗口通知,就再次启动一个持续计时器
当持续计时器超时时,主机A立刻发送一个零窗口探测报文段
假设主机B此时的接收缓存又有了一些可用空间,于是将自己的接收窗口调整为300,主机B就在确认这个零窗口探测报文段时给出自己现在的接收窗口值为300,这样就打破了死锁的局面
思考:A发送的零窗口探测报文段到达B时,如果B此时的接收窗口值仍然为0,那么B根本就无法接受该报文段,又怎么针对该报文段给A发送确认呢
注意:
- TCP规定,即使接收窗口值为0,也必须接受零窗口探测报文段、确认报文段以及携带有紧急数据的报文段
- 零窗口探测报文段有其相应的超时重传计时器,以防止零窗口探测报文段丢失造成无法打破死锁的情况
利用滑动窗口机制可以很方便地在TCP连接上实现对发送方的流量控制
TCP的拥塞控制以及与网际层拥塞控制的关系
在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络性能就要变坏,这种情况就叫做拥塞(congestion)
在计算机网络中的链路容量(即带宽)、交换结点中的缓存和处理机等,都是网络的资源
若出现拥塞而不进行控制,整个网络的吞吐量将随输入负荷的增大而下降
如图,横坐标是输入负载,代表单位时间内输入给网络的分组数量,纵坐标是吞吐量,代表单位时间内从网络中输出的分组数量
具有理想拥塞控制的网络,在吞吐量达到饱和之前,网络吞吐量应等于输入负载
但当输入负载超过某一限度时,由于网络资源受限,吞吐量就不再增长而保持水平线,也就是吞吐量达到饱和,也就表明输入的负载中有一部分损失掉了,例如,输入到网络中的某些分组被某个结点丢弃了
虽然如此,在这种理想的拥塞控制作用下,网络的吞吐量仍然维持在其所能达到的最大值
(在没有干预的情况下)实际网络情况是随着输入负载的增大,网络吞吐量的增长率逐渐减小,也就是在网络吞吐量还未达到饱和时,就已经有一部分输入分组被丢弃了,当网络的吞吐量明显地小于理想的吞吐量时,网络就进入了轻度拥塞的状态
当输入负载到达某一数值时,网络的吞吐量反而随着输入负载增大而减小,这时网络就进入了拥塞状态,当输入负载继续增大到某一数值时,网络吞吐量就减小到0,此时网络就无法工作了,这就是所谓的死锁。因此进行拥塞控制是非常有必要的
实际的拥塞控制曲线应该尽量接近理想的拥塞控制曲线
流量控制与拥塞控制的区别
流量控制的任务是确保发送方不会持续地以超过接收方接收能力的速率发送数据,以防止接收方来不及从接收缓存中取走数据而导致接收缓存溢出进而数据丢失
拥塞控制的任务是防止过多的数据注入到网络中,使网络能够承受现有的网络负荷
从表面上看,流量控制与拥塞控制非常相似,因为它们都需要控制源点的发送速率,但实际上这两者有很大的区别
流量控制以接收方的接收能力控制发送方,即源点的发送速率,只与特定的点对点通信的发送方和接收方之间的流量有关
而拥塞控制是源点根据各方面因素,按拥塞控制算法自行控制发送速率,这是一个全局性问题,涉及各反面的行为,包括网络中的所有主机、所有路由器、路由器内部的存储转发处理过程还有与降低网络传输性能有关的所有因素等
从控制论的角度看,拥塞控制可以分为开环控制和闭环控制两大类
-
开环控制
试图用良好的设计来解决问题
从一开始就保证问题不会发生
一旦系统启动并运行起来,就不需要中途修改
-
闭环控制
基于反馈的控制方法,包括以下三个部分:
- 监测网络拥塞在何时何地发生
- 把拥塞发生的相关信息传送到可以采取行动的地方
- 调整网络的运行以解决拥塞问题
当网络的流量特征可以准确规定且性能要求可以事先获得时,适合使用开环控制
当网络的流量特征不能准确描述或者当网络不提供预留时,适合使用闭环控制
由于因特网不提供资源预留机制,而且流量的特征不能准确描述,所以在因特网中拥塞控制主要采用的就是闭环控制方法
根据拥塞信息的反馈形式,可将闭环拥塞控制算法分为:
-
显式反馈算法
从拥塞节点(即路由器)向源点提供关于网络中拥塞状态的显式反馈信息
例如,当有大量IP数据报涌入因特网中的某个路由器时,该路由器可能会丢弃一些IP数据报,同时可以使用ICMP源站抑制报文通知源站,源站收到后应该降低发送速率
但这些额外注入到网络中的ICMP源点抑制报文有时反而会造成网络更加拥堵,更好的显式反馈算法是在路由器转发的分组中保留一个字段,用该字段的值表示网络的拥塞状态,而不是专门发送一个通知分组
-
隐式反馈算法
源点自身通过对网络行为的观察(例如超时重传或往返时间RTT)来推断网络是否发生了拥塞,而无须拥塞结点提供显式反馈信息
TCP采用的就是隐式反馈算法
拥塞控制并不仅仅是运输层要考虑的问题,显式反馈算法就必须涉及网络层
虽然一些网络体系结构(如ATM网络)主要在网络层实现拥塞控制,但因特网主要利用隐式反馈在运输层实现拥塞控制
进行拥塞控制是需要付出代价的:
- 可能需要在节点之间交换信息和各种命令,以便选择拥塞控制的策略并实施控制,这样会产生额外开销
- 可能需要预留一些资源用于特殊用户或特殊情况,这样就降低了网络资源的共享程度
为了确保网络性能的稳定,不会因为输入负载的增大而导致网络性能的恶化甚至出现崩溃,使用拥塞控制而付出一定的代价是值得的
衡量网络拥塞的指标
- 由于缓存溢出而丢弃的分组的百分比
- 路由器的平均队列长度
- 超时重传的分组数量
- 平均分组时延和分组时延的标准差
- ...
上述指标的上升都标志着拥塞程度的增大
TCP的四种拥塞控制算法
-
慢开始(slow-start)
-
拥塞避免(congestion avoidance)
-
快重传(fast retransmit)
-
快恢复(fast recovery)
慢开始和拥塞避免
为了集中精力讨论拥塞控制算法的基本原理,假定如下条件:
- 数据是单方向传送的,而另一个方向只传送确认
- 接收方总是有足够大的接收缓存空间,因而发送方的发送窗口的大小仅由网络的拥塞程度来决定,也就是不考虑接收方对发送方的流量控制
- 以TCP最大报文段MSS(即TCP报文段的数据载荷部分)的个数作为讨论问题的单位,而不是以字节为单位(尽管TCP是面向字节的)
如图,这是TCP的收发双方,它们基于TCP连接进行通信
TCP的发送方给接收方发送TCP数据报文段,接收方收到数据报文段后给发送方发送确认报文段
发送方要维护一个叫做发送窗口swnd的状态变量,还要维护一个叫做拥塞窗口cwnd的状态变量
接收方要维护一个叫做接收窗口rwnd的状态变量
发送窗口swnd的取值应从拥塞窗口cwnd的取值和接收窗口rwnd的取值中取小者
发送方可将序号落入发送窗口swnd的数据连续发送出去
接收窗口rwnd的取值取决于接收方可用的接收缓存的大小
而拥塞窗口cwnd取决于网络的拥塞程度和所采用的TCP拥塞控制算法
在不考虑拥塞控制的情况下,发送窗口swnd就等于接收窗口rwnd
在不考虑流量控制的情况下,发送窗口swnd就等于拥塞窗口cwnd(本节所假设的情况)
拥塞窗口cwnd的维护原则:只要网络没有出现拥塞,拥塞窗口就再增大一些;但只要网络出现拥塞,拥塞窗口就减少一些
判断出现拥塞的依据:没有按时收到应当到达的TCP确认报文段而产生了超时重传
- 由于现在的通信线路的传输质量一般都较好,因传输误码而被路由器丢弃分组的概率远小于1%,因此当发送方出现超时重传时,很可能是因为网络中的某个路由器繁忙而丢弃了一些分组,这是网络出现拥塞的征兆
除拥塞窗口cwnd和发送窗口swnd,发送方还需要维护一个慢开始门限ssthresh状态变量(也称为拥塞控制阈值)
- 当cwnd < ssthresh时,使用慢开始算法
- 当cwnd > ssthresh时,停止使用慢开始算法而改用拥塞避免算法
- 当cwnd = ssthresh时,即可使用慢开始算法,又可使用拥塞避免算法
ssthresh的值为人工配置
如图,在TCP建立连接时,拥塞窗口cwnd的初始值被设置为1,这是因为主机刚开始发送数据时完全不知道网络的拥塞情况(这不是本例假设,而是固定做法),如果立即把大量的数据都注入网络中就可能引起网络拥塞
经验证明,较好的方法是由小到大逐渐增大发送方的拥塞窗口cwnd的数值,直到发生拥塞
另外还需要设置慢开始门限ssthresh的初始值,本例采用16
在不考虑TCP流量控制的情况下,发送方的发送窗口swnd的取值就取为拥塞窗口cwnd的数值
发送方给接收方发送0号数据报文段,接收方收到后给发送方发回对0号数据报文段的确认报文段
此时第一个传输轮次结束
传输轮次是指发送方将发送窗口中所有数据报文段发送出去,并成功收到来自接收方对这些报文段的确认,一个传输轮次所经历的时间就是发送窗口内所有数据的往返时间RTT
RTT并非恒定的数值
发送方收到该确认报文段后,将拥塞窗口cwnd的值加1增大到2,这意味着发送方在下一个传输轮次中可以发送1~2号共2个数据报文段
发送方给接收方发送1~2号共2个数据报文段,接收方收到后给发送方发回对1~2号数据报文段的确认报文段
此时第二个传输轮次结束
发送方收到确认号文段后,将拥塞窗口cwnd的值加2增大到4,这意味着发送方在下一个传输轮次中可以发送3~6号共4个数据报文段
发送方给接收方发送3~6号共4个数据报文段,接收方收到后给发送方发回对3~6号数据报文段的确认报文段
此时第三个传输轮次结束
发送方收到确认号文段后,将拥塞窗口cwnd的值加4增大到8,这意味着发送方在下一个传输轮次中可以发送7~14号共8个数据报文段
发送方给接收方发送7~14号共8个数据报文段,接收方收到后给发送方发回对7~14号数据报文段的确认报文段
此时第四个传输轮次结束
发送方收到确认号文段后,将拥塞窗口cwnd的值加8增大到16,至此发送方当前拥塞窗口cwnd的值已经增大到了慢开始门限ssthresh的值
因此之后要采用拥塞避免算法,也就是每个传输轮次结束后,cwnd的值只能线性加1,而不像慢开始算法那样每次传输轮次结束后cwnd的值按指数规律增长
接下来使用拥塞避免算法
发送方给接收方发送15~30号共16个数据报文段,接收方收到后给发送方发回对15~30号数据报文段的确认报文段
此时第五个传输轮次结束
发送方收到确认报文段后将拥塞窗口cwnd的值加1增大到17,这意味着发送方在下一个传输轮次中可以发送31~47号共17个数据报文段
发送方给接收方发送31~47号共17个数据报文段,接收方收到后给发送方发回对31~47号数据报文段的确认报文段
此时第六个传输轮次结束
发送方收到确认报文段后将拥塞窗口cwnd的值加1增大到18
随着传输轮次的增加,拥塞窗口cwnd的值每轮次都线性加1,例如又经过几次传输轮次后,cwnd的值线性加1增大到了24,发送方给接收方发送171~194共24个数据报文段
假设这24个数据报文段在传输过程中丢失了几个,这必然会造成发送方对这些丢失的报文段的超时重传,发送方以此判断网络可能出现了拥塞,需要调整自己的拥塞窗口cwnd的值和慢开始门限ssthresh的值
将慢开始门限ssthresh的值调整为发生拥塞时拥塞窗口cwnd的值的一半,对于本例为12
将拥塞窗口cwnd的值减小为1并重新开始执行慢开始算法,让拥塞窗口cwnd的值继续按指数规律增大
当拥塞窗口cwnd的值增大到新的慢开始门限值时就停止使用慢开始算法,转而执行拥塞避免算法
为了更清楚地看出拥塞控制过程,可以绘制出本例的拥塞窗口cwnd的值随传输轮次RTT的变化关系图
注意:
-
“慢开始”是指一开始网络注入的报文段少,而不是指拥塞窗口值cwnd增长速度慢
-
“拥塞避免”并非指完全能够避免拥塞,而是指拥塞避免阶段将拥塞窗口控制为按线性规律增长,使网络比较不容易出现拥塞
快重传和快恢复
(在上述的两个拥塞控制算法的配合下)有时,个别报文段会在网络中丢失(如IP数据报出现误码从而被路由器丢弃),但实际上网络并未发生拥塞,这也会导致发送方超时重传,并误认为网络发生了拥塞,于是发送方把拥塞窗口cwnd又设置为最小值1,并错误地启动了慢开始算法,因而降低了传输效率
采用快重传算法可以让发送方尽早知道发生了个别报文段的丢失
所谓快重传,就是使发送方尽快进行重传,而不是等超时计时器超时再重传
这就要求接收方不要等待自己发送数据时才进行捎带确认,而是要立即发送确认,即使收到了失序的报文段也要立即发出对已收到的报文段的重复确认
发送方一旦收到3个连续的重复确认,就将相应的报文段立即重传,而不是等该报文段的超时计时器超时再重传
如图,这是已建立TCP连接的收发双方
发送方发送1号TCP数据报文段,接收方收到后给发送方发回对1号数据报文段的确认
在该确认报文段到达发送方之前,发送方还可以将发送窗口内的2号数据报文段发送出去
接收方收到2号数据报文段后,给发送方发回对2号数据报文段的确认
在该确认报文段到达发送方之前,发送方还可以将发送窗口内的3号数据报文段发送出去,但该报文段丢失了,接收方自然不会给发送方发回针对该报文段的确认
发送方还可以将发送窗口内的4号数据报文段发送出去
接收方收到4号数据报文段后发现这不是按序到达的报文段,因此给发送方发回针对2号数据报文段的重复确认,表明我现在期望收到的是3号数据报文段但是我没有收到3号数据报文段而是收到了未按序到达的4号数据报文段
发送方还可以将发送窗口内的5号数据报文段发送出去,接收方收到5号数据报文段后发现这不是按序到达的数据报文段,因此给发送方发回针对2号数据报文段的重复确认
发送方还可以将发送窗口内的6号数据报文段发送出去,接收方收到6号数据报文段后发现这不是按序到达的数据报文段,因此给发送方发回针对2号数据报文段的重复确认
至此发送方会收到3个连续的对2号数据报文段的重复确认,于是就立即重传3号数据报文段
接收方收到3号数据报文段后给发送方发回针对6号数据报文段的确认,表明序号到6为止的数据报文段都正确接收了,这样就不会造成对3号数据报文段的超时重传,而是提早进行了重传
对于个别丢失的报文段,发送方不会出现超时重传,也就不会误认为出现了拥塞而错误地把拥塞窗口cwnd的值减为1
实践证明,使用快重传可以使整个网络的吞吐量提高约20%
与快重传算法配合使用的是快恢复算法,发送方一旦收到3个重复确认,就知道现在只是丢失了个别的报文段,于是不启动慢开始算法,而执行快恢复算法
快恢复算法:发送方将慢开始门限ssthresh值和拥塞窗口cwnd值都调整为当前拥塞窗口cwnd的一半,并开始执行拥塞避免算法
也有的快恢复实现是把快恢复开始时的拥塞窗口cwnd值再增大一些,即等于新的ssthresh+3
这么做的理由是,既然发送方收到了3个重复的确认,就表明有3个数据报文段已经离开了网络,这3个报文段不再消耗网络资源而是停留在接收方的接收缓存中,可见现在网络中不是堆积了报文段而是减少了3个报文段,因此可以适当把新的拥塞窗口扩大些
TCP四种拥塞控制方法举例
慢开始门限ssthresh的初始值被设置为16,TCP发送方一开始使用慢开始算法,让拥塞窗口cwnd的值从1开始按指数规律增大
当拥塞窗口cwnd的值增大到慢开始门限值时,停止使用慢开始算法,转而执行拥塞避免算法,让拥塞窗口cwnd的值按线性加1的规律增大
当发生超时重传时,就判断网络可能出现了拥塞,要采取相应的措施
一方面将慢开始门限的值调整为发生拥塞时拥塞窗口的一半(即24 / 2 = 12),另一方面,将拥塞窗口cwnd的值减小为1,并重新执行慢开始算法
拥塞窗口cwnd的值又从1开始按指数规律增大,当增大到新的慢开始门限值时停止使用慢开始算法,转而执行拥塞避免算法,让拥塞窗口cwnd的值按线性加1的规律增大
当发送方收到3个重复确认时,就进行快重传和快恢复
也就是立刻进行相应数据报文段的重传,并将慢开始门限sthresh的值和拥塞窗口cwnd的值都调整为当前拥塞窗口cwnd的值的一半(即16 / 2 = 8),转而执行拥塞避免算法,让拥塞窗口cwnd的值按线性加1的规律增大
注意:超时重传发生在发送端发送24个数据报文段的过程中(即在传输轮次12期间),而不是将拥塞窗口值设置为24的一开始
TCP拥塞控制流程图
TCP拥塞控制与网际层拥塞控制的关系
网际层的策略对TCP拥塞控制影响最大的就是IP路由器的IP数据报丢弃策略
路由器的输入缓存(可以看作缓存队列,以下简称为队列)通常都按照“先进先出FIFO”的规则来处理到达的IP数据报
由于队列长度总是有限的,因此当队列已满时,之后再到达的所有IP数据报都将被丢弃,这就叫做尾部丢弃策略
当网络中有大量封装了TCP报文段的IP数据报涌入某个或某些路由器并造成路由器进行尾部丢弃时,这些TCP报文段的多个发送方就会出现超时重传,这将导致它们将拥塞窗口cwnd的值陡降为1,因此发送窗口swnd的值也降低为1,并且进入TCP拥塞控制的慢开始阶段,这称为全局同步
全局同步会使得全网的通信量骤降,而在网络恢复正常后,其通信量又突然增大很多
为了避免网络中出现全局同步问题,在1998年提出了主动队列管理(Active Queue Management, AQM)
- 所谓“主动”,就是在路由器的队列长度达到某个阈值但还未满时就主动丢弃IP数据报,而不是要等到路由器的队列已满时才不得不丢弃后面到达的IP数据报,这样就太被动了
- 应当在路由器队列长度达到某个值得警惕的数值时,也就是网络出现了某些拥塞征兆时,就主动丢弃到达的IP数据报来造成发送方的超时重传,进而降低发送方的发送速率,因而有可能减轻网络的拥塞程度,甚至不出现网络拥塞
主动队列管理AQM可以有不同的实现方法,其中曾流行多年的就是随机早期检测(RandomEarly Detection, RED),也称为随机早期丢弃(Random Early Drop, RED或Random Early Discard, RED)
路由器需要维护两个参数来实现RED:队列长度最小门限和最大门限
当每一个IP数据报到达路由器时,RED就按照规定的算法计算出当前的平均队列长度
- 若平均队列长度小于最小门限,则把新到达的IP数据报存入队列进行排队
- 若平均队列长度大于最大门限,则把新到达的IP数据报丢弃
- 若平均队列长度在最小门限和最大门限之间,则按照某一丢弃概率p把新到达的IP数据报丢弃(这体现了丢弃IP数据报的随机性)
TCP可靠传输的实现
TCP基于以字节为单位的滑动窗口来实现可靠传输
- 发送方在未收到接收方的确认时,可将发送窗口内还未发送的数据全部发送出去
- 凡是已经发送过的数据,在未收到确认之前都必须暂时保留,以便在超时重传时使用
- 接收方只接收序号落入发送窗口内的数据
因特网上的两台主机基于已建立的TCP连接进行通信
为了简单起见,假设数据传输只在一个方向进行,也就是发送方给接收方发送TCP数据报文段,接收方给发送方发送相应的TCP确认报文段,这样的好处是使讨论仅限于发送方的发送窗口swnd和接收方的接收窗口rwnd
假设发送方收到了一个来自接收方的确认报文段,报文段首部中的确认号ack字段的值为31,这表明接收方期望收到下一个数据的序号是31,而序号到30为止的数据已经全部正确接收了
- ack[n]在选择重传协议与TCP协议中并不完全相同
- 在选择重传协议中,ack[n]表明序号到n为止的数据已正确接收,现在期望收到序号为n+1的数据
- 在TCP协议中,ack[n]表明序号到n-1为止的数据已正确接收,现在期望收到序号为n的数据
该报文段首部中窗口字段的值为20,表明接收方自己的接收窗口rwnd的尺寸为20字节
为了简单起见,假定网络不存在拥塞问题,也就是发送方在构造自己的发送窗口swnd时,仅考虑接收方的接收窗口rwnd,而不考虑发送方自己的拥塞窗口cwnd
对于本例,接收方告诉发送方自己的接收窗口rwnd的值为20,因此发送方将自己的发送窗口swnd的值也设置为20
终上所述,发送方收到接收方发来的确认报文段后,根据其首部中确认号ack字段的值和窗口字段的值构造出自己的发送窗口
发送方在没有收到接收方确认的情况下,可以把序号落入发送窗口内的数据依次全部发送出去
凡是发送过的数据,在未收到确认之前都必须暂时保留,以便在超时重传时使用
发送窗口具有前沿和后沿,发送窗口后沿的后面部分是已发送并已收到确认的数据字节的序号,这些数据字节不需要再保存在发送缓存中了,可以将它们删除
发送窗口前沿的前面部分是当前不允许发送的数据字节的序号
发送窗口后沿的移动情况有两种可能:
- 不动(没有收到新的确认)
- 前移(收到了新的确认)
发送窗口的前沿的移动情况有三种可能:
前移(通常情况)
不动
没有收到新的确认,接收方通知的窗口大小也没有改变
收到了新的确认,但接收方通知的窗口缩小了,使发送窗口的前沿正好不动
后缩(接收方通知的窗口变小了)
TCP标准不建议让前沿后缩,因为很可能在发送方收到这个通知之前,就已经发送了窗口中的许多数据,现在又要收缩窗口,不让发送这些数据,显然就会产生错误
假设发送方将发送窗口内的31~41号数据封装在几个不同的TCP数据报文段中发送出去
此时发送窗口的位置并没有改变,发送窗口内序号31~41的数据已经发送但未收到确认,而序号42~50的数据是允许发送但还未发送的
如果要编程实现滑动窗口机制,可以使用三指针P1、P2和P3分别指向相应的字节序号,来描述当前发送窗口的状态
P1指向发送窗口内已发送但还未收到确认的第一个数据的序号
P2指向发送窗口内还未发送的第一个数据的序号
P3指向发送窗口前沿外的第一个数据的序号
用P1、P2和P3三个指针描述发送窗口的相关信息:
- 小于P1的就是已发送并已收到确认的部分
- 大于等于P3的就是不允许发送的部分
- P3 - P1 = 发送窗口尺寸
- P2 - P1 = 已发送但还未收到确认的字节数量
- P3 - P2 = 允许发送但当前还未发送的字节数量(可用窗口或有效窗口)
假设接收方的接收窗口尺寸为20,在接收窗口外面,序号到30为止的数据是已经发送过相应确认并已交付给应用进程的数据,因此无须再保留这些数据,可将它们从接收缓存中删除了
接收窗口内31~50号数据是允许接收的数据
接收窗口外51号及其后续数据目前不允许接收
假设发送方之前发送的封装有32和33号数据的报文段到达了接收方,由于数据序号落在接收窗口内,所以接收方接受它们,并将它们存入接收缓存,但它们是未按序到达的数据,因为31号数据还没有到达,这有可能是丢了,也有可能是滞留在网络中的某处
接收方只能对按序收到的数据中的最高序号给出确认,因此接收方发出的确认报文段中的确认号ack仍然是31,也就是期望收到31号数据
窗口字段的值仍为20,表明接收方没有改变自己接收窗口rwnd的大小
发送方收到该确认报文段后发现这是一个针对31号数据的重复确认,就知道接收方收到了未按序到达的数据
由于这是针对31号数据的第一个重复确认,因此这不会引起发送方针对该数据的快重传
另外,接收方通知的窗口尺寸仍是20,因此,发送方仍保持自己的发送窗口尺寸为20
现在假设封装有31号数据的报文段到达了接收方,接收方接受该报文段,将其封装的31号数据存入接收缓存
接收方现在可将接收缓存中的31~33号数据一起交付给应用进程,然后将接收窗口向前滑动3个序号并给发送方发送确认报文段
该确认报文段中确认号ack字段的值为34,表明接收方已经收到序号到33为止的全部数据,现在期望收到序号为34的数据
窗口字段的值仍为20,表明接收方没有改变自己接收窗口rwnd的大小
现在假设又有几个数据报文段到达了接收方,它们封装有37、38以及40号数据,这些数据的序号虽然落在接收窗口内,但它们都是未按序到达的数据,只能先暂存在接收缓存中
假设接收方先前发送的确认报文段到达了发送方,发送方接收后,将发送窗口向前滑动3个序号,发送窗口的尺寸保持不变,这样就有新序号51~53落入发送窗口内,而序号31~33被移出了发送窗口,现在可将31~33号数据从发送缓存中删除了,因为已经收到了接收方针对它们的确认
发送方继续将发送窗口内序号42~53号数据封装在几个不同的报文段中发送出去,现在发送窗口内的序号已经用完了,发送方在未收到接收方发来的确认的情况下,不能再发送新的数据了,而序号落在发送窗口内的已发送数据,如果迟迟收不到接收方的确认,则会产生超时重传
补充
虽然发送方的发送窗口是根据接收方的接收窗口设置的,但在同一时刻,发送方的发送窗口并不总是和接收方的接收窗口一样大,原因如下:
- 网络传送窗口值需要经历一定的时间滞后,并且这个时间还是不确定的
- 发送方还可能根据网络当时的拥塞情况适当减小自己的发送窗口尺寸
对于不按序到达的数据应如何处理,TCP并无明确规定
- 如果接收方把不按序到达的数据一律丢弃,那么接收窗口的管理将会比较简单,但这样做对网络资源的利用不利,因为发送方会重复传送较多的数据
- TCP通常对不按序到达的数据是先临时存放在接收窗口中,等到字节流中所缺少的字节收到后,再按序交付上层的应用进程
TCP要求接收方必须有累积确认和捎带确认机制,这样可以减小传输开销
接收方可以在合适的时候发送确认,也可以在自己有数据要发送时把确认信息顺便捎带上
-
接收方不应过分推迟发送确认,否则会导致发送方不必要的超时重传,这反而浪费了网络资源
TCP标准规定,确认推迟的时间不应超过0.5秒
若收到一连串具有最大长度的报文段,则必须每隔一个报文段就发送一个确认
-
捎带确认实际上并不经常发生,因为大多数应用程序很少同时在两个方向上发送数据
TCP的通信是全双工通信,通信中的每一方都在发送和接收数据报文段,因此每一方都有自己的发送窗口和接收窗口
TCP超时重传时间的选择
假设因特网上的两台主机,主机A与主机B之间已经建立了TCP连接,主机A给主机B发送TCP数据报文段0,并记录下当前的时间
主机B收到后,给主机A发送确认报文段0
主机A收到确认报文段0后,记录下当前的时间
此时,主机A记录下的这两个时间,它们的差值就是报文段0的往返时间RTT0
超时重传时间RTO如果比RTT0小,会导致报文段不必要的重传,使网络负荷增大
RTO如果被设置得远大于RTT0,则会导致重传推迟的时间太长,使网络的空闲时间增大,降低了传输效率
所以超时重传RTO的值,应该略大于往返时间RTT0
TCP下层是复杂的因特网环境:
- 主机A发送的报文段可能只经过一个高速率的局域网
- 也可能经过多个低速率的网络
- 并且每个IP数据报的转发路由还可能不同
假设主机A给主机B发送TCP数据报文段1,主机B收到后给主机A发送相应的确认报文段1,主机A这次测得的报文段往返时间RTT1远大于上次测得的RTT0
如果重传时间RTO还是之前所确定的略大于RTT0,这对于数据报文段1就是不合适的,会造成该报文段的不必要的重传
所以超时重传时间的选择并非那么简单
由于每个报文段的转发路由不是统一的,也就导致每个报文段对应的往返时间不同
所以,不能单单使用某次测量得到的RTT样本来确定超时重传时间RTO,但可以利用每次测量得到的RTT样本,计算加权平均往返时间RTT[s](又称为平滑的往返时间),进而得出超时重传时间RTO
当测量到第一个RTT样本时(记为RTT[1]),RTT[s]的值直接取第一个RTT样本的值,即RTT[s1] = RTT[1]
之后每测量到一个RTT样本时,都按照公式计算新的RTT[s]的值,即:
-
RTT[sn]= (1 - α) × RTT[sn1] + α × RTT[n]
其中:0 ≤ α < 1
若α很接近于0,则新RTT样本对RTT[s]的影响不大
若α很接近于1,则新RTT样本对RTT[s]的影响较大
RFC [6298]建议α的取值为1/8,即0.125
显然,超时重传时间RTO应略大于加权平均往返时间RTT[s],而不是某个RTT样本的值
RFC [6298]建议使用的计算超时重传时间RTO的计算公式:RTO[n] = RTT[sn] + 4 × RTT[Dn]
-
RTT[s]为加权平均往返时间
-
RTT[D]为RTT偏差的加权平均,其计算方法如下:
第一次测量时:RTT[D1] = RTT[1] ÷ 2
之后的每一次:RTT[Dn] = (1 - β) × RTT[Dn-1] + β × |RTT[sn-1] - RTT[n]|
其中,0 ≤ β < 1,RFC [6298]建议β的取值为1/4,即0.25
从RTT[s]和RTT[D]的计算公式可以看出,它们都是基于所测量到的RTT样本进行计算的
如果所测量到的RTT样本不正确,那么计算出的RTT[s]和RTT[D]自然就不正确,进而所计算出的超时重传时间RTO也就不正确
然而,往返时间RTT的测量确实是比较复杂
当发送方出现超时重传后,收到确认报文段时,是无法判断出该确认报文段是对原报文段的确认还是对重传报文段的确认,也就无法正确计算RTT,进而不能正确计算RTO
情况1:主机A给主机B发送TCP数据报文段,但该报文段在传输过程中丢失了
当超时重传记时器超时后主机A就重传该报文段
主机B收到重传的报文段后,给主机A发送确认报文段
主机A收到该确认报文段后无法判断出该确认报文段是对原数据报文段的确认还是重传数据报文段的确认(实际上是对重传确认报文段的确认)
如果主机A误将该确认报文段当作对原数据报文段的确认,则所计算出的RTT
s和RTO就会偏大,降低了传输效率情况2:主机A给主机B发送TCP数据报文段,主机B收到后给主机A发送确认报文段,由于某种原因,该确认报文段没有在正常时间内到达主机A,这必然会导致主机A对之前所发送的数据报文段的超时重传
主机A收到迟到的确认报文段后,无法判断该报文段是对原数据报文段的确认还是对重传数据报文段的确认(实际上是对原数据报文段的确认)
如果主机A误将该确认报文段当作对重传数据报文段的确认,则所计算出的RTTs和RTO就会偏小,这会导致后续报文段发生没有必要的重传,增大网络负荷
针对出现超时重传时无法测准往返时间RTT的问题,Karn提出了一个算法:在计算加权平均往返时间RTT[s]时,只要报文段重传了,就不采用其往返时间RTT样本
也就是出现重传时,不重新计算RTT[s],进而超时重传时间RTO也不会重新计算
这又引起了新的问题
设想出现这样的情况:报文段的时延突然增大了很多,并且之后很长一段时间都会保持这种时延
因此在原来得出的重传时间内,不会收到确认报文段,·于是就重传报文段
但根据Karn算法,不考虑重传的报文段的往返时间样本
这样,超时重传时间就无法更新,这会导致报文段反复被重传
因此,要对Karn算法进行修正
方法是:报文段每重传一次,就把超时重传时间RTO增大一些
典型的做法是将新RTO的值取为旧RTO值的2倍
-
若出现超时重传,则直接设置新RTO的值取为旧RTO值的2倍,而不必再根据RTT[s]和RTT[D]得出新的RTO
即发生超时重传时,本次的RTO[n] = 2 × RTO[n-1],而RTTsn = RTTsn-1,RTT[Dn] = RTT[Dn-1]
TCP的选择确认
在之前介绍的TCP快重传和可靠传输中,TCP接收方只能对按序收到的数据中的最高序号给出确认
当发送方超时重传时,接收方之前已收到的未按序到达的数据也会被重传
TCP使用选择确认(Selective ACK,SACK),可以让发送方只传送缺少的数据而不重传已经正确到达,只是未按序到达的数据
假设这是TCP接收方收到的对方发送过来的序号不连续的字节流,序号1~1000的连续字节流后缺少序号1001~1500的字节流
序号1501~3000的连续字节流由于前面缺少了部分字节流,为了后续描述方便,称其为第一个字节块。在第一个字节块后面又缺少了序号3001~3500的字节流
序号3501~4500的连续字节流由于前面也缺少了部分字节流,称其为第二个字节块
对于本例,接收方收到了和前面的字节流不连续的两个字节块
假设这些字节的序号都在接收窗口内,那么接收方就要先收下这些数据,但要把这些信息准确地告诉发送方,使发送方不再重复发送这些数据,可以使用两个指针来分别指向字节块的左边界和右边界
左边界指出字节块的第一个字节的序号,右边界指出字节块后面的第一个字节的序号
L1和R1是分别指向第一个字节块左边界和右边界的指针,L2和R2是分别指向第二个字节块左边界和右边界的指针
TCP报文段的固定首部中没有专门为选择确认机制提供专属的字段
因此,如果要使用选择确认机制,需要利用TCP的扩展首部中的选项字段进行实现
-
允许SACK选项(1字节)
是否使用选择确认选项
-
SACK选项长度(1字节)
选择确认选项需要占用多少个字节
-
指明一个边界需要4字节(因为每一个字节的序号的大小为4字节)
一个字节块需要两个边界,即8字节
因此最大长度为40字节的选项字段,最多指明4个字节块的信息
SACK相关文档并没有指明发送方应当怎样响应SACK,因此大多数的TCP实现还会重传所有未被确认的数据块