1 传输层概述
(1) 作用
传输层为它上面的应用层提供通信服务。在OSI七层参考模型中,传输层是面向通信的最高层,也是用户功能的最底层。
(2) 传输层两大重要的功能
复用:在发送端,多个应用进程公用一个传输层;
分用:在接收端,传输层会根据端口号将数据分派给不同的应用进程。
(3) 传输层和网络层的区别
网络层为不同主机提供通信服务,而传输层为不同主机的不同应用提供通信服务。
网络层只对报文头部进行差错检测,而传输层对整个报文进行差错检测。
(4) 传输层协议
因特网为应用层提供了两种截然不同的传输层协议,一种是UDP(用户数据报协议),它为调用它的应用程序提供了一种不可靠、无连接的服务。另一种是TCP(传输控制协议),它为调用它的应用程序提供了一种可靠的、面向连接的服务。
UDP和TCP最基本的责任是,将两个端系统间IP的交付服务扩展为运行在端系统上的两个进程之间的交付服务;UDP和TCP还在其报文段首部中包含了差错检查字段而提供完整性检查;另外,UDP是一种无连接的、不可靠的服务,而TCP提供了可靠的数据传输,它通过使用流量控制、序号、确认和定时器,确保正确地、按序地将数据从发送进程交付给接收进程。除此之外,TCP还提供了拥塞控制。
2 UDP(用户数据报协议)详解
2.1 UDP的特点
(1) UDP只在IP数据报服务的基础上增加了少量的功能:复用、分用和对整个报文的差错检测。
(2) UDP是无连接的:通信前不需要建立连接,通信结束也无需释放连接。
(3) UDP是不可靠的:它是尽力而为交付,不能确保每一个数据报都送达。
(4) UDP是面向报文的:所谓面向报文就是指,UDP数据传输的单位是报文,且不会对数据作任何拆分和拼接 操作。
(a) 在发送端,应用程序给传输层的UDP什么样的数据,UDP不会对数据进行切分,只增加一个UDP头并交给网络层。
(b) 在接收端,UDP收到网络层的数据报后,去除IP数据报头部后遍交给应用层,不会作任何拼接操作。
(5) UDP没有拥塞控制:UDP始终以恒定的速率发送数据,并不会根据网络拥塞情况对发送速率作调整。这种方式有利有弊。
(a) 弊端:网络拥塞时有些报文可能会丢失,因此UDP不可靠。
(b) 优点:有些使用场景允许报文丢失,如:直播、语音通话,但对实时性要求很高,此时UDP还是很有用武之地的。
(6) UDP支持一对一、一对多、多对多、多对一通信。而TCP只支持一对一通信。
(7) UDP首部开销小,只有8字节。而TCP头部至少由20字节,相比于TCP要高效很多。
2.2 UDP报文头
头部结构中各部分的作用:
(1) 16位源端口号
记录源端口号,在需要对方回信时选用。不需要时可用全0。
(2) 16位目的端口号
记录目标端口号。这在终点交付报文时必须要使用到。
(3) 长度
UDP数据报的长度(包括数据和首部),其最小值为8B(即仅有首部没有数据的情况)。
(4) 校验和
检测UDP数据报在传输中是否有错,有错就丢弃。该字段时可选的,当源主机不想计算校验和,则直接令该字段为全0。当传输层从IP层收到UDP数据报时,就根据首部中的目的端口,把UDP数据报通过相应的端口,上交给进程。如果接收方UDP发现收到的报文中目的端口号不正确(即不存在对应端口号的应用进程),就丢弃该报文,并由ICMP发送“端口不可达”差错报文交给发送方。
3 TCP(传输控制协议)详解
3.1 TCP特点
(1) TCP是面向连接的
通信前需要建立连接,通信结束需要释放连接。
(2) TCP提供可靠交付服务
所谓可靠指的是:TCP发送的数据无重复、无丢失、无错误、与发送端顺序一致。
(3) TCP是面向字节流的
所谓面向字节流指的是:TCP以字节为单位。虽然传输的过程中数据被划分成一个个数据报,但这只是为了方便传输,接收端最终接受到的数据将与发送端的数据一模一样。
(4) TCP提供全双工通信
所谓全双工通信指的是:TCP的两端既可以作为发送端,也可以作为接收端。TCP允许通信双方的应用进程在任何时候都能发送数据。TCP连接的两端都设有发送缓存和接收缓存,用来临时存放双向通信的数据。在发送时,应用程序在把数据传送给TCP缓存后,就可以做自己的事情,而TCP在合适的时候把数据发送出去。在接收时,TCP把收到的数据放入缓存,上层的应用进程在合适的时候读取缓存中的数据。
(5) 一条TCP连接的两端只能有两个端点
TCP只能提供点到点的通信(只支持单播,不提供多播和组播服务),而UDP可以任意方式的通信。
3.2 TCP连接与套接字
(1) TCP连接是一种抽象的概念,表示一条可以通信的链路。
每条TCP连接有且仅有两个端点,表示通信的双方。且双方在任意时刻都可以作为发送者和接收者。
(2) 一条TCP连接的两端就是两个套接字。
套接字 = IP地址:端口号。
因此,TCP连接=(套接字1,套接字2)=(IP1:端口号1,IP2:端口号2)
3.3 TCP头部
TCP头部长度有20字节的固定部分,选项部分长度不定,但最多40字节,因此TCP头部在20-60字节之间。
(1) 源端口 和 目的端口
各占2个字节,传输层和网络层一大重要区别就是传输层指定了数据报发往的应用进程,因此需要端口号标识。
(2) 序号
占4个字节,当前TCP数据报数据部分的第一个字节的序号。我们知道,TCP是面向字节的,它会对发送的每一个字节进行编号,而且不同数据报之间是连续编号的。
由于本字段4字节,可以给[0,2^32-1]个字节进行编号(大约4G),而且序号循环使用,当发送完2^32-1个字节后,序号又从0开始。一般来说,当2^32-1个字节被发送的时候,前面的字节早就发送成功了,因此序号可以循环使用。
(3) 确认号
占4字节,表示当前主机作为接收端时,期望接收的下一个字节的编号是多少。也表示,当前主机已经正确接收的最后一个字节序号+1。
(4) 数据偏移(报文长度)
占4位,它表明了数据报头部的长度。
(5) 保留字段
占6位,保留为今后使用,目前置为0。
(6) 标识符
TCP有6种标识符,用于表示TCP报文的性质。它们只能为0或1。
(a) URG=1
当URG字段被置1,表示本数据报的数据部分包含紧急信息,此时紧急指针有效。
紧急数据一定位于当前数据包数据部分的最前面,紧急指针标明了紧急数据的尾部。
如control+c:这个命令要求操作系统立即停止当前进程。此时,这条命令就会存放在数据包数据部分的开头,并由紧急指针标识命令的位置,并URG字段被置1。
(b) ACK=1
ACK被置1后确认号字段才有效。
此外,TCP规定,在连接建立后传送的所有报文段都必须把ACK置1。
(c) PSH=1
当接收方收到PSH=1的报文后,会立即将数据交付给应用程序,而不会等到缓冲区满后再提交。
一些交互式应用需要这样的功能,降低命令的响应时间。
(d) RST=1
当该值为1时,表示当前TCP连接出现严重问题,必须要释放重连。
(e) SYN=1
SYN在建立连接时使用。
当SYN=1,ACK=0时,表示当前报文段是一个连接请求报文。
当SYN=1,ACK=1时,表示当前报文段是一个同意建立连接的应答报文。
(f) FIN=1
FIN=1表示此报文段是一个释放连接的请求报文。
(7) 接收窗口大小
占2个字节,该字段用于实现TCP的流量控制。
它表示当前接收方的接收窗口的剩余容量,发送方收到该值后会将发送窗口调整成该值的大小。发送窗口的大小又决定了发送速率,所以接收方通过设置该值就可以控制发送放的发送速率。
发送方每收到一个数据报都要调整当前的发送窗口。
(8) 检验和
占2字节,用于接收端检验整个数据包在传输过程中是否出错。
(9) 紧急指针
占2个字节,用于标识紧急数据的尾部。
(10) 选项字段
上述字段都是每个TCP头部必须要有的,而选项字段是可选的,且长度可变,最长40字节。
最常用的选项字段为MMS:最大报文长度。
4 TCP传输连接管理
TCP在传输之前会进行三次沟通,一般称为“三次握手”,传完数据断开的时候要进行四次沟通,一般称为“四次挥手”。
4.1 TCP三次握手(连接建立)
起初,服务器和客户端都为CLOSED状态。在通信开始前,双方都得创建各自的传输控制(TCB)。 服务器创建完TCB后遍进入LISTEN状态,此时准备接收客户端发来的连接请求。
(1) 第一次握手
客户端向服务端发送连接请求报文段。该报文段的头部中SYN=1,ACK=0,seq=x。请求发送后,客户端便进入SYN-SENT状态。
注:
SYN=1,ACK=0 表示该报文段为连接请求报文。
x为本次TCP通信的字节流的初始序号。
TCP规定:SYN=1的报文段不能有数据部分,但要消耗掉一个序号。
(2) 第二次握手
服务端收到连接请求报文段后,如果同意连接,则会发送一个应答:SYN=1,ACK=1,seq=y,ack=x+1。
该应答发送完成后服务器便进入SYN-RCVD状态。
注:
SYN=1,ACK=1表示该报文段为连接同意的应答报文。
seq=y表示服务端作为发送者时,发送字节流的初始序号。
ack=x+1表示服务端希望下一个数据报发送序号从x+1开始的字节。
(3) 第三次握手
当客户端收到连接同意的应答后,还要向服务端发送一个确认报文段,表示:服务端发来的连接同意应答已经成功收到。
该报文段的头部为:ACK=1,seq=x+1,ack=y+1。TCP的标准规定,ACK报文段可以携带数据。但如果不携带数据则不消耗序号,在这种情况下,下一个数据报文段序号仍是 seq=x+1。
客户端发完这个报文段后便进入ESTABLISHED状态,服务端收到这个应答后也进入ESTABLISHED状态,此时连接的建立完成!
4.2 TCP四次挥手(连接释放)
TCP连接的释放一共需要四步,因此称为四次挥手。
我们知道,TCP连接是双向的,因此在四次挥手中,前两次挥手用于断开一个方向的连接,后两次挥手用于断开另一方向的连接。
(1) 第一次挥手
若A认为数据发送完成,则它需要向B发送连接释放请求。该请求只有报文头,头中携带的主要参数为:
FIN=1,seq=u。此时,A将进入FIN-WAIT-1状态。
注:
FIN=1表示该报文段是一个连接释放请求。
seq=u,u-1是A向B发送的最后一个字节的序号。
(2) 第二次挥手
B收到连接释放请求后,会通知相应的应用程序,告诉它A向B这个方向的连接已经释放。此时B进入CLOSE-WAIT状态,并向A发送连接释放的应答,其报文头包含:
ACK=1,seq=v,ack=u+1。
注:
ACK=1:除TCP连接请求报文段以外,TCP通信过程中所有数据报的ACK都为1,表示应答。
seq=v,v-1是B向A发送的最后一个字节的序号。
ack=u+1表示希望收到从第u+1个字节开始的报文段,并且已经成功接收了前u个字节。
A收到该应答,进入FIN-WAIT-2状态,等待B发送连接释放请求。
第二次挥手完成后,A到B方向的连接已经释放,B不会再接收数据,A也不会再发送数据。但B到A方向的连接仍然存在,B可以继续向A发送数据。
(3) 第三次挥手
当B向A发完所有数据后,向A发送连接释放请求,请求头:FIN=1,ACK=1,seq=w,ack=u+1。B便进入LAST-ACK状态。
(4) 第四次挥手
A收到释放请求后,向B发送确认应答,此时A进入TIME-WAIT状态。该状态会持续2MSL时间,若该时间段内没有B的重发请求的话,就进入CLOSED状态,撤销TCB。当B收到确认应答后,也便进入CLOSED状态,撤销TCB。我们注意到B结束TCP连接的时间要比A早一些。
注:
B只要收到了A发出的确认,就进入CLOSED状态。同样,B在撤销相应的传输控制块TCB后,就结束了这次连接。
4.3 原理详解
(1) 为什么连接建立需要三次握手,而不是两次握手的原因?
防止失效的连接请求报文段被服务端接收,从而产生错误。
所谓“已失效的连接请求报文段”是这样产生的。考虑一种正常情况。A发出连接请求,但因连接请求丢失而未收到确认。于是A再次重传一次连接请求。后来收到了确认建立了连接。数据传输完毕后,就释放了连接。A共发送了两个连接请求的报文段,其中第一个丢失,第二个到达了B。没有“已失效的连接请求报文段”。
现假定出现一种异常情况,即A发出的第一个连接请求报文段并没有丢失,而是在某些网络节点长时间滞留了,以致延误到连接释放以后的某个时间才到B。本来这是一个已失效的报文段。但是B收到此失效的连接请求报文段后,就误认为是A又发出一次新的连接请求。于是就向A发出确认报文段,同意建立连接。假定不采用三次握手,那么只要B发出确认,新的连接就建立了。
由于现在A并没有发出建立连接的请求,因此不会理睬B的确认,也不会向B发送数据。但B却以为新的运输连接已经建立了,并一直等待A发来数据。B的许多资源就这样白白浪费了。
采用三次握手的办法可以防止上述现象的发生。例如在刚才的情况下,A不会向B的确认发出确认。B由于收不到确认,就知道A并没有要求建立连接。
(2) 为什么A要先进入TIME-WAIT状态,等待2MSL时间后才进入CLOSED状态?
(a) 为了保证客户端发送的最后一个ACK报文段能够达到服务器。 这个ACK报文段可能丢失,因而使处在LAST-ACK状态的服务器收不到确认。服务器会超时重传FIN+ACK报文段,客户端就能在2MSL时间内收到这个重传的FIN+ACK报文段,接着客户端重传一次确认,重启计时器。最后,客户端和服务器都正常进入到CLOSED状态。如果客户端在TIME-WAIT状态不等待一段时间,而是再发送完ACK报文后立即释放连接,那么就无法收到服务器重传的FIN+ACK报文段,因而也不会再发送一次确认报文。这样,服务器就无法按照正常步骤进入CLOSED状态。
(b) 防止已失效的连接请求报文段出现在本连接中。客户端在发送完最后一个ACK确认报文段后,再经过时间2MSL,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。这样就可以使下一个新的连接中不会出现这种旧的连接请求报文段。
(3) 为什么连接的时候是三次握手,关闭的时候却是四次握手?
因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,"你发的FIN报文我收到了"。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。
(4) 在TIME_WAIT状态中,如果TCP client端最后一次发送的ACK丢失了,它将重新发送TIME_WAIT状态中所需要的时间是依赖于实现方法的。典型的值为30秒、1分钟和2分钟。等待之后连接正式关闭,并且所有的资源(包括端口号)都被释放。
5 TCP可靠传输的原理
理想的传输条件有以下两个特点:
(1) 传输信道不产生差错。
(2) 不管发送方以多快的速度发送数据,接收方总是来得及处理收到的数据。
在这样的理想传输条件下,不需要采取任何措施就能够实现可靠传输。
然而实际的网络都不具备以上两个理想条件。但我们可以使用一些可靠传输协议,当出现差错时让发送方重传出现错误的数据,同时在接收方来不及处理收到的数据时,及时告诉发送方适当降低发送数据的速度。这样一来,本来是不可靠的传输信道就能够实现可靠传输了。
TCP的可靠性表现在:它向应用层提供的数据是无差错的、有序的、无丢失的。简单的说就是,TCP最终递交给应用层的数据和发送者发送的数据是一模一样的。
TCP采用了流量控制、拥塞控制、连续ARQ等技术来保证它的可靠性。
网络层传输的数据单元为数据报,传输层的数据单元为报文段,但为了方便起见,可以统称为分组。
5.1 停止等待协议(ARQ协议)
TCP保证其可靠性采用的是更为复杂的滑动窗口协议,但停止等待协议是它的简化版,为了方便理解,这里先介绍停止等待协议。
(1) AQR协议
ARQ(Automatic Repeat reQuest)自动重传请求。
顾名思义,当请求失败时它会自动重传,直到请求被正确接收为止。这种机制保证了每个分组都能被正确接收。停止等待协议是一种ARQ协议。
(2) 停止等待协议的原理
(a) 无差错的情况
A向B每发送一个分组,都要停止发送,等待B的确认应答;A只有收到了B的确认应答后才能发送下一个分组。
(b) 分组丢失和出现差错的情况
发送者拥有超时计时器。每发送一个分组便会启动超时计时器,等待B的应答。若超时仍未收到应答,则A会重发刚才的分组。
分组出现差错:
若B收到分组,但通过检查和字段发现分组在运输途中出现差错,它会直接丢弃该分组,并且不会有任何其他动作。A超时后便会重新发送该分组,直到B正确接收为止。
分组丢失:
若分组在途中丢失,B并没有收到分组,因此也不会有任何响应。当A超时后也会重传分组,直到正确接收该分组的应答为止。
综上所述:当分组丢失 或 出现差错 的情况下,A都会超时重传分组。
(c) 应答丢失和应答迟到的情况
TCP会给每个字节都打上序号,用于判断该分组是否已经接收。
应答丢失:
若B正确收到分组,并已经返回应答,但应答在返回途中丢失了。此时A也收不到应答,从而超时重传。紧接着B又收到了该分组。接收者根据序号来判断当前收到的分组是否已经接收,若已接收则直接丢弃,并补上一个确认应答。
应答迟到:
若由于网络拥塞,A迟迟收不到B发送的应答,因此会超时重传。B收到该分组后,发现已经接收,便丢弃该分组,并向A补上确认应答。A收到应答后便继续发送下一个分组。但经过了很长时间后,那个失效的应答最终抵达了A,此时A可根据序号判断该分组已经接收,此时只需简单丢弃即可。
(3) 停止等待协议的注意点
每发送完一个分组,该分组必须被保留,直到收到确认应答为止。
必须给每个分组进行编号。以便按序接收,并判断该分组是否已被接收。
必须设置超时计时器。每发送一个分组就要启动计时器,超时就要重发分组。
计时器的超时时间要大于应答的平均返回时间,否则会出现很多不必要的重传,降低传输效率。
5.2 滑动窗口协议(连续ARQ协议)
(1) 连续ARQ协议
在ARQ协议发送者每次只能发送一个分组,在应答到来前必须等待。而连续ARQ协议的发送者拥有一个发送窗口,发送者可以在没有得到应答的情况下连续发送窗口中的分组。这样降低了等待时间,提高了传输效率。
(2) 累计确认
在连续ARQ协议中,接收者也有个接收窗口,接收者并不需要每收到一个分组就返回一个应答,可以连续收到分组之后统一返回一个应答。这样能节省流量。
TCP头部的ack字段就是用来累计确认,它表示已经确认的字节序号+1,也表示期望发送者发送的下一个分组的起始字节号。
(3) 发送窗口
发送窗口的大小由接收窗口的剩余大小决定。接收者会把当前接收窗口的剩余大小写入应答TCP报文段的头部,发送者收到应答后根据该值和当前网络拥塞情况设置发送窗口的大小。发送窗口的大小是不断变化的。
发送窗口由三个指针构成:
(a) p1
p1指向发送窗口的后沿,它后面的字节表示已经发送且已收到应答。
(b) p2
p2指向尚未发送的第一个字节。
p1-p2间的字节表示已经发送,但还没收到确认应答。这部分的字节仍需保留,因为可能还要超时重发。
p2-p3间的字节表示可以发送,但还没有发送的字节。
(c) p3
p3指向发送窗口的前沿,它前面的字节尚未发送,且不允许发送。
发送者每收到一个应答,后沿就可以向前移动指定的字节。此时若窗口大小仍然没变,前沿也可以向前移动指定字节。
当p2和前沿重合时,发送者必须等待确认应答。
(4) 接收窗口
接收者收到的字节会存入接收窗口,接收者会对已经正确接收的有序字节进行累计确认,发送完确认应答后,接收窗口就可以向前移动指定字节。
如果某些字节并未按序收到,接收者只会确认最后一个有序的字节,从而乱序的字节就会被重新发送。
(5) 连续ARQ的注意点
(a) 同一时刻发送窗口的大小并不一定和接收窗口一样大。
虽然发送窗口的大小是根据接收窗口的大小来设定的,但应答在网络中传输是有时间的,有可能t1时间接收窗口大小为m,但当确认应答抵达发送者时,接收窗口的大小已经发生了变化。此外发送窗口的大小还随网络拥塞情况影响。当网络出现拥塞时,发送窗口将被调小。
(b) TCP标准并未规定未按序到达的字节的处理方式。但TCP一般都会缓存这些字节,等缺少的字节到达后再交给应用层处理。这比直接丢弃乱序的字节要节约带宽。
(c) TCP标准规定接收方必须要有累计确认功能。接收方可以对多个TCP报文段同时确认,但不能拖太长时间,一般是0.5S以内。此外,TCP允许接收者在有数据要发送的时候捎带上确认应答。但这种情况一般较少,因为一般很少有两个方向都要发送数据的情况。
6 流量控制
6.1 利用滑动窗口实现流量控制
一般来说,我们总是希望数据传输的更快一些,但如果发送方把数据发送的很快,而接收方来不及接收,这就可能造成数据的丢失。流量控制就是让发送方的发送速率不要太快,让接收方来得及接收。流量控制是利用滑动窗口机制实现的。
我们假设A向B发送数据。在连接建立时,B告诉了A:“我的接收窗口是 rwnd = 400 ”(这里的 rwnd 表示 receiver window) 。因此,发送方的发送窗口不能超过接收方给出的接收窗口的数值。请注意,TCP的窗口单位是字节,不是报文段。TCP连接建立时的窗口协商过程在图中没有显示出来。再设每一个报文段为100字节长,而数据报文段序号的初始值设为1。大写ACK表示首部中的确认位ACK,小写ack表示确认字段的值。
从图中可以看出,B进行了三次流量控制。第一次把窗口减少到 rwnd = 300 ,第二次又减到了 rwnd = 100 ,最后减到 rwnd = 0 ,即不允许发送方再发送数据了。这种使发送方暂停发送的状态将持续到主机B重新发出一个新的窗口值为止。
我们考虑一种特殊情况,如果B在向A发送了零窗口报文段后不久,B的接收缓存又有了一些存储空间,于是B向A发送了一个rwnd=400的报文段,然而这个报文段在传送过程中丢失了,A就一直等待B发送非零窗口的报文通知,而B一直等待A发送数据,如果没有任何措施的话,这话死锁的局面会一直延续下去。
为了解决这个问题,TCP为每一个连接设有一个持续计时器(也叫坚持定时器)。只要TCP连接的一方收到对方的零窗口通知,就启动持续计时器。若持续计时器设置的时间到期,就发送一个零窗口控测报文段(携1字节的数据),对方在收到探测报文段后,在对该报文段的确认时给出现在的窗口值,如果窗口值仍然是零,则收到这个报文段的一方就重新设置持续计时器,如果窗口不为零,那么死锁的僵局就被打破了。
6.2 必须考虑传输速率
糊涂窗口综合证: TCP接收方的缓存已满,而交互式的应用进程一次只从接收缓存中读取1字节(这样就使接收缓存空间仅腾出1字节),然后向发送方发送确认,并把窗口设置为1个字节(但发送的数据报为40字节的的话)。接收,发送方又发来1个字节的数据(发送方的IP数据报是41字节)。接收方发回确认,仍然将窗口设置为1个字节。这样,网络的效率很低。要解决这个问题,可让接收方等待一段时间,使得或者接收缓存已有足够空间容纳一个最长的报文段,或者等到接收方缓存已有一半空闲的空间。只要出现这两种情况,接收方就发回确认报文,并向发送方通知当前的窗口大小。此外,发送方也不要发送太小的报文段,而是把数据报积累成足够大的报文段,或达到接收方缓存的空间的一半大小。
7 拥塞控制
7.1 引言
计算机网络中的带宽、交换结点中的缓存和处理机等,都是网络的资源。在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络的性能就会变坏。这种情况就叫做拥塞。
拥塞控制就是防止过多的数据注入网络中,这样可以使网络中的路由器或链路不致过载。拥塞控制是一个全局性的过程,和流量控制不同,流量控制指点对点通信量的控制。
7.2 慢开始与拥塞避免
发送方维持一个叫做拥塞窗口cwnd(congestion window)的状态变量。拥塞窗口的大小取决于网络的拥塞程度,并且动态地在变化。发送方让自己的发送窗口等于拥塞窗口,另外考虑到接受方的接收能力,发送窗口可能小于拥塞窗口。
慢开始算法的思路就是,不要一开始就发送大量的数据,先探测一下网络的拥塞程度,也就是说由小到大逐渐增加拥塞窗口的大小。
这里用报文段的个数的拥塞窗口大小举例说明慢开始算法,实时拥塞窗口大小是以字节为单位的。如下图:
当然收到单个确认但此确认多个数据报的时候就加相应的数值。所以一次传输轮次之后拥塞窗口就加倍。这就是乘法增长,和后面的拥塞避免算法的加法增长比较。
为了防止cwnd增长过大引起网络拥塞,还需设置一个慢开始门限ssthresh状态变量。ssthresh的用法如下:
当cwnd<ssthresh时,使用慢开始算法。
当cwnd>ssthresh时,改用拥塞避免算法。
当cwnd=ssthresh时,慢开始与拥塞避免算法任意。
拥塞避免算法让拥塞窗口缓慢增长,即每经过一个往返时间RTT就把发送方的拥塞窗口cwnd加1,而不是加倍。这样拥塞窗口按线性规律缓慢增长。
无论是在慢开始阶段还是在拥塞避免阶段,只要发送方判断网络出现拥塞(其根据就是没有收到确认,虽然没有收到确认可能是其他原因的分组丢失,但是因为无法判定,所以都当做拥塞来处理),就把慢开始门限设置为出现拥塞时的发送窗口大小的一半。然后把拥塞窗口设置为1,执行慢开始算法。如下图:
再次提醒这里只是为了讨论方便而将拥塞窗口大小的单位改为数据报的个数,实际上应当是字节。
7.3 快重传和快恢复
快重传要求接收方在收到一个失序的报文段后就立即发出重复确认(为的是使发送方及早知道有报文段没有到达对方)而不要等到自己发送数据时捎带确认。快重传算法规定,发送方只要一连收到三个重复确认就应当立即重传对方尚未收到的报文段,而不必继续等待设置的重传计时器时间到期。如下图:
快重传配合使用的还有快恢复算法,有以下两个要点:
(1) 当发送方连续收到三个重复确认时,就执行“乘法减小”算法,把ssthresh门限减半。但是接下去并不执行慢开始算法。
(2) 考虑到如果网络出现拥塞的话就不会收到好几个重复的确认,所以发送方现在认为网络可能没有出现拥塞。所以此时不执行慢开始算法,而是将cwnd设置为ssthresh的大小,然后执行拥塞避免算法。如下图:
7.4 随机早期检测RED
以上的拥塞避免算法并没有和网络层联系起来,实际上网络层的策略对拥塞避免算法影响最大的就是路由器的丢弃策略。在简单的情况下路由器通常按照先进先出的策略处理到来的分组。当路由器的缓存装不下分组的时候就丢弃到来的分组,这叫做尾部丢弃策略。这样就会导致分组丢失,发送方认为网络产生拥塞。更为严重的是网络中存在很多的TCP连接,这些连接中的报文段通常是复用路由路径。若发生路由器的尾部丢弃,可能影响到很多条TCP连接,结果就是这许多的TCP连接在同一时间进入慢开始状态。这在术语中称为全局同步。全局同步会使得网络的通信量突然下降很多,而在网络恢复正常之后,其通信量又突然增大很多。
为避免发生网路中的全局同步现象,路由器采用随机早期检测(RED:randomearly detection)。该算法要点如下:
使路由器的队列维持两个参数,即队列长队最小门限min和最大门限max,每当一个分组到达的时候,RED就计算平均队列长度。然后分情况对待到来的分组:
(1) 平均队列长度小于最小门限——把新到达的分组放入队列排队。
(2) 平均队列长度在最小门限与最大门限之间——则按照某一概率将分组丢弃。
(3) 平均队列长度大于最大门限——丢弃新到达的分组。
以概率p随机丢弃分组,让拥塞控制只在个别的TCP连接上执行,因而避免全局性的拥塞控制。
RED的关键就是选择三个参数最小门限、最大门限、丢弃概率和计算平均队列长度。平均队列长度采用加权平均的方法计算平均队列长度,这和往返时间(RTT)的计算策略是一样的。