TCP/IP详解部分笔记整理(三)传输层(TCP)

运输层

主要为两台主机上的应用程序提供端到端的通信。在TCP/IP协议族中,有两个互不相同的传输协议:TCP(传输控制协议)和UDP(用户数据报协议)。

TCP协议

TCP和UDP是两种最为著名的运输层协议,二者都使用IP作为网络层协议。虽然TCP使用不可靠的IP服务,TCP提供一种面向连接的可靠的字节流服务。

所谓面向连接的,是指在彼此通信前要先建立连接。同时这种点对点的连接表明了TCP不支持多播和广播。

所谓可靠的,是指TCP有一堆保证数据传输准确的机制。

所谓字节流,是指TCP接收端并不知道发送端每次向该连接写入了多少数据,只关心通过限制能从连接中最大字节数。

图片6

 

 

段位说明

源端口和目的端口:各占2字节(16位).端口是传输层与应用层的服务接口.用于寻找发端和收端应用程序.这两个值加上IP首部中的源端IP地址和目的端IP地址唯一确定一个TCP连接,传输层的复用和分用功能都要通过端口才能实现

序号:占4字节.TCP连接中传送的数据流中的每一个字节都编上一个序号.发送的字节序号,如果是新建立的连接,则第一个包的seq为0,否则为上一个数据包的确认序号。同一个包中的序号和确认序号是不同的

确认号:占4字节,是期望收到对方的下一个报文段的数据的第一个字节的序号,等于接收到数据包的序号seq+数据包的长度len

首部长度:占4位,tcp包首部的长度,它指出TCP报文段的数据起始处距离TCP报文段的起始处有多远.

保留:占6位,保留为今后使用,但目前应置为0

紧急URG:当URG=1时,表明紧急指针字段有效.它告诉系统此报文段中有紧急数据,应尽快传送(相当于高优先级的数据)

确认ACK:只有当ACK=1时确认号字段才有效.当ACK=0时,确认号无效

PSH(PuSH):接收TCP收到PSH=1的报文段,就尽快地交付接收应用进程,而不再等到整个缓存都填满了后再向上交付

RST(ReSeT):当RST=1时,表明TCP连接中出现严重差错(如由于主机崩溃或其他原因),必须释放连接,然后再重新建立运输连接

同步SYN:同步SYN=1表示这是一个连接请求或连接接受报文

终止FIN:用来释放一个连接.FIN=1表明此报文段的发送端的数据已发送完毕,并要求释放运输连接

检验和:占2字节.检验和字段检验的范围包括首部和数据这两部分.在计算检验和时,要在TCP报文段的前面加上12字节的伪首部

紧急指针:占16位,指出在本报文段中紧急数据共有多少个字节(紧急数据放在本报文段数据的最前面)

选项:长度可变.TCP最初只规定了一种选项,即最大报文段长度MSS.MSS告诉对方TCP:“我的缓存所能接收的报文段的数据字段的最大长度是MSS个字节.”[MSS(MaximumSegmentSize)是TCP报文段中的数据字段的最大长度.数据字段加上TCP首部才等于整个的TCP报文段]

填充:这是为了使整个首部长度是4字节的整数倍

其他选项:

窗口扩大:占3字节,其中有一个字节表示移位值S.新的窗口值等于TCP首部中的窗口位数增大到(16+S),相当于把窗口值向左移动S位后获得实际的窗口大小

时间戳:占10字节,其中最主要的字段时间戳值字段(4字节)和时间戳回送回答字段(4字节)

选择确认:接收方收到了和前面的字节流不连续的两2字节.如果这些字节的序号都在接收窗口之内,那么接收方就先收下这些数据,但要把这些信息准确地告诉发送方,使发送方不要再重复发送这些已收到的数据

 

TCP各种状态

建立连接协议(三次握手)

(1)客户端发送一个带SYN标志的TCP报文到服务器。这是三次握手过程中的报文1。

(2)服务器端回应客户端的,这是三次握手中的第2个报文,这个报文同时带ACK标志和SYN标志。因此它表示对刚才客户端SYN报文的回应;同时又标志SYN给客户端,询问客户端是否准备好进行数据通讯。

(3)客户必须再次回应服务段一个ACK报文,这是报文段3。

图片7

 

连接终止协议(四次手)

由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。

(1)TCP客户端发送一个FIN,用来关闭客户到服务器的数据传送

(2)服务器收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。和SYN一样,一个FIN将占用一个序号。

(3)服务器关闭客户端的连接,发送一个FIN给客户端。

(4)客户段发回ACK报文确认,并将确认序号设置为收到序号加1。

图片8

 

图片9

 

 

各个状态解释:

CLOSED:表示初始状态。

LISTEN:表示服务器端的某个SOCKET处于监听状态,可以接受连接了。

SYN_RCVD:这个状态表示接受到了SYN报文,在正常情况下,这个状态是服务器端的SOCKET在建立TCP连接时的三次握手会话过程中的一个中间状态,很短暂,基本上用netstat你是很难看到这种状态的,除非你特意写了一个客户端测试程序,故意将三次TCP握手过程中最后一个ACK报文不予发送。因此这种状态时,当收到客户端的ACK报文后,它会进入到ESTABLISHED状态。

SYN_SENT:这个状态与SYN_RCVD遥想呼应,当客户端SOCKET执行CONNECT连接时,它首先发送SYN报文,因此也随即它会进入到了SYN_SENT状态,并等待服务端的发送三次握手中的第2个报文。

SYN_SENT状态表示客户端已发送SYN报文。

ESTABLISHED:表示连接已经建立了。

FIN_WAIT_1:其实FIN_WAIT_1和FIN_WAIT_2状态的真正含义都是表示等待对方的FIN报文。而这两种状态的区别是:FIN_WAIT_1状态实际上是当SOCKET在ESTABLISHED状态时,它想主动关闭连接,向对方发送了FIN报文,此时该SOCKET即进入到FIN_WAIT_1状态。而当对方回应ACK报文后,则进入到FIN_WAIT_2状态,当然在实际的正常情况下,无论对方何种情况下,都应该马上回应ACK报文,所以FIN_WAIT_1状态一般是比较难见到的,而FIN_WAIT_2状态还有时常常可以用netstat看到。FIN_WAIT_2:实际上FIN_WAIT_2状态下的SOCKET,表示半连接,也即有一方要求close连接,但另外还告诉对方,我暂时还有点数据需要传送给你,稍后再关闭连接。

TIME_WAIT:表示收到了对方的FIN报文,并发送出了ACK报文,就等2MSL后即可回到CLOSED可用状态了。如果FIN_WAIT_1状态下,收到了对方同时带FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态。

CLOSING:这种状态比较特殊,实际情况中应该是很少见,属于一种比较罕见的例外状态。正常情况下,当你发送FIN报文后,按理来说是应该先收到(或同时收到)对方的ACK报文,再收到对方的FIN报文。但是CLOSING状态表示你发送FIN报文后,并没有收到对方的ACK报文,反而却也收到了对方的FIN报文。那就是如果双方几乎在同时close一个SOCKET的话,那么就出现了双方同时发送FIN报文的情况,也即会出现CLOSING状态,表示双方都正在关闭SOCKET连接。

CLOSE_WAIT:这种状态的含义其实是表示在等待关闭。当对方close一个SOCKET后发送FIN报文给自己,你系统毫无疑问地会回应一个ACK报文给对方,此时则进入到CLOSE_WAIT状态。如果没有还有数据发送给对方,就可以close这个SOCKET,发送FIN报文给对方,也即关闭连接。所以你在CLOSE_WAIT状态下,需要完成的事情是等待关闭连接。LAST_ACK:这个状态还是比较容易好理解的,它是被动关闭一方在发送FIN报文后,最后等待对方的ACK报文。当收到ACK报文后,也即可以进入到CLOSED可用状态了.

特殊情况状态

半关闭

TCP连接是个全双工通道,所以可以同时支持发送和接收。可以认为将一个通道分成两部分,就像高速公路一样,一条大路中间分隔,两边的方向完全相反。半关闭是指连接的一端发送完本地数据后,将发送的那半个连接关闭,告诉对端不用再等待接收数据,而对端照样可以发送数据,本端可以读取数据。

复位报文

无论何时一个报文段发往基准的连接(referencedconnection)出现错误,TCP都会发出一个复位报文段(这里提到的“基准的连接”是指由目的IP地址和目的端口号以及源IP地址和源端口号指明的连接。)TCP产生复位的常见情况是到不存在的端口的连接请求,异常终止一个连接,检测半关闭连接。对于UDP,当一个数据报到达目的端口时,该端口没在使用,它将产生一个ICMP端口不可达的信息。发送一个复位报文段而不是FIN来中途释放一个连接,这称为异常释放

异常终止一个连接对应用程序来说有两个优点:(1)丢弃任何待发数据并立即发送复位报文段;(2)RST的接收方会区分另一端执行的是异常关闭还是正常关闭。应用程序使用的API必须提供产生异常关闭而不是正常关闭的手段。SocketAPI通过“lingeronclose”选项(SO_LINGER)提供了这种异常关闭的能力。我们加上-L选项并将停留时间设为0。这将导致连接关闭时进行复位而不是正常的FIN。

同时打开

两个应用程序同时执行主动打开的情况是可能的,虽然发生的可能性较低。每一端都发送一个SYN,并传递给对方,且每一端都使用对端所知的端口作为本地端口,tcp协议在遇到这种情况时,只会打开一条连接。这个连接的建立过程需要4次数据交换,而一个典型的连接建立只需要3次交换(即3次握手)但多数伯克利版的tcp/ip实现并不支持同时打开。

图片10

 

 

同时关闭

如果应用程序同时发送FIN,则在发送后会首先进入FIN_WAIT_1状态。在收到对端的FIN后,回复一个ACK,会进入CLOSING状态。在收到对端的ACK后,进入TIME_WAIT状态。这种情况称为同时关闭。同时关闭也需要有4次报文交换,与典型的关闭相同。

图片11

 

问题

1、为什么建立连接协议是三次握手,而关闭连接却是四次握手呢?

因为发送和接收并不是同一条路[分组交换]

在IP层,A-B和B-A经过的可能是不同的路由

服务端的LISTEN状态下的SOCKET当收到SYN报文的建连请求后,它可以把ACK和SYN(ACK起应答作用,而SYN起同步作用)放在一个报文里来发送。但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可以未必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文多数情况下都是分开发送的。

2、为什么TIME_WAIT状态还需要等2MSL后才能返回到CLOSED状态?

虽然双方都同意关闭连接了,而且握手的4个报文也都协调和发送完毕,按理可以直接回到CLOSED状态(就好比从SYN_SEND状态到ESTABLISH状态那样);但是因为我们必须要假想网络是不可靠的,你无法保证你最后发送的ACK报文会一定被对方收到,因此对方处于LAST_ACK状态下的SOCKET可能会因为超时未收到ACK报文,而重发FIN报文,所以这个TIME_WAIT状态的作用就是用来重发可能丢失的ACK报文。

数据传输

TCP的交互数据流

图片12

 

时延确认:通常TCP在接收到数据时并不立即发送ACK;相反,它推迟发送,以便将ACK与需要沿该方向发送的数据一起发送(有时称这种现象为数据捎带ACK)。绝大多数采用的时延是200ms。

Nagle算法:该算法要求一个TCP连接上最多只能有一个未被确认的未完成的小分组,在该分组的确认到达之前不能发送其他的小分组。相反,TCP收集这些少量数据的分组,并在确认到来时以一个分组的方式发出去。在交互数据流中,Nagle算法将产生时延,因此常常关闭Nagle算法。SocketAPI用户可以使用TCP_NODELAY选项来关闭Nagle算法。

TCP的成块数据流

正常数据流

1:TCP通常不会对每个到达的数据分段进行确认操作,一个ACK报文可以确认多个成块数据报文,一般情况下两个成块数据报文段需要一个ACK报文确认。即ACK并不立即发送,而是使用一个200ms的定时器,在有数据需要发送、或定时器到时才发送ACK。理由如下:

A:当收到一个报文后,此TCP连接被表示成一个未完成的延时确认,当再次收到一个报文后,此时连接有两个未确认的报文段,TCP马上发送一个ACK。

B:当第三个数据报文到达后,第四个报文到达前,这时此TCP连接已经过了200MS延时。因此一个ACK被发送。

C:这样周而复始,出现一个ACK确认两个数据报文的情况。D:而且ACK得产生很大程度上和其接受数据报文段的时间紧密相关,即和Client端发送数据报的频率相关,和网络拥塞程度相关,和Client及Server两端的处理能力相关。

滑动窗口

TCP头里有一个字段叫Window,又叫Advertised-Window,这个字段是接收端告诉发送端自己还有多少缓冲区可以接收数据。于是发送端就可以根据这个接收端的处理能力来发送数据,而不会导致接收端处理不过来。

图片13

 

TCP窗口

首先,要理解,client和server各自协议栈都有自己的buffer,应用层读写数据的源都是协议栈buffer里。以接收端为例,应用程序调用read()时,会从buffer里移走数据到用户空间,应用程序读的速度越快(read(1024)必然比read(1)要快),那么buffer里的内容消费的越快,buffer也会越空。那么TCP就可以告诉client,我有空闲空间,你可以发送更多的数据来。”更多”是多少?这就说窗口,窗口就是量化接收端和服务端当前能处理数据的能力。滑动窗口的大小意味着接收方还有多大的缓冲区可以用于接收数据。发送方可以通过滑动窗口的大小来确定应该发送多少字节的数据。

窗口大小TCP窗口大小一般由接收方确认,即在TCP建链的第二个SYN+ACK报文的WIN字段确认。接收方提供的窗口大小通常可以由接受进行控制。默认窗口大小事4096个字节。

用三个术语描述窗口左右边沿的运动

图片14

 

A:窗口合拢,即窗口左边延右边滑动,表示发送方发送了数据或者接收到了确认。

B:窗口张开,即窗口右边延右边滑动,表示数据已经被用户空间进程接受并且释放了缓存。

C:窗口收缩,即窗口右边延左边滑动,表示已发送的数据接收端并未处理

图片15

 

以该图为例可以总结如下几点:

1) 发送方不必发送一个全窗口大小的数据。

2) 来自接收方的一个报文段确认数据并把窗口向右边滑动。这是因为窗口的大小是相对于确认序号的。

3) 正如从报文段7到报文段8中变化的那样,窗口的大小可以减小,但是窗口的右边沿却不能够向左移动。

4) 接收方在发送一个A CK前不必等待窗口被填满。在前面我们看到许多实现每收到两个报文段就会发送一个A CK。

注意

当滑动窗口为0时,发送方一般不能再发送数据报,但有两种情况除外,一种情况是可以发送紧急数据,例如,允许用户终止在远端机上的运行进程。另一种情况是发送方可以发送一个1字节的数据报来通知接收方重新声明它希望接收的下一字节及发送方的滑动窗口大小。

PUSH标志

1:PUSH是TCP包头中的一个标志位,发送方在发送数据的时候可以设置这个标志位。该标志通知接收方将接收到的数据全部提交给接受进程。这里所说的接收数据包括与此PUSH包一起传输的数据以及之前就为该进程传输过的数据。

2:当Server端收到这些数据后,它需要立刻将这些数据提交给应用层进程,而不再等待是否还有额外的数据到达。3:PUSH标志位的设置现在都有TCP协议自行处理,而不是交给应用层处理。如,当发送的数据会清空发送缓存时,栈就会自动将此包设置PUSH标志。

慢启动

慢启动的意思是,刚刚加入网络的连接,一点一点地提速,不要一上来就像那些特权车一样霸道地把路占满

1:TCP在局域网环境中效率是很高的,但是到了广域网情况就不同了。在发送方和接收方之间可能存在多个路由器及一些速率比较慢的链路,而且一些中继路由器必须缓存分组,还可能分片,这是TCP效率可能会出现问题。

2:TCP需要支持一种被称为“慢启动”的算法。改算法通过观察新分组进入网络的速率应该与另一端返回确认的速率相同时而进行工作。

3:慢启动为发送方的TCP增加了另一个窗口:拥塞窗口(congestionwindow),记为cwnd。当与另一个网络的主机建立TCP连接时,拥塞窗口被初始化为1个报文段(即另一端通告的报文段大小)。每收到一个ACK,拥塞窗口就增加一个报文段(cwnd以字节为单位,但是慢启动以报文段大小为单位进行增加)。发送方取拥塞窗口与通告窗口中的最小值作为发送上限。拥塞窗口是发送方使用的流量控制,而通告窗口则是接收方使用的流量控制。

慢启动的算法(cwnd全称CongestionWindow):

1)连接建好的开始先初始化cwnd=1,表明可以传一个MSS大小的数据。

2)每当收到一个ACK,cwnd++;呈线性上升

3)每当过了一个RTT,cwnd=cwnd*2;呈指数让升

4)还有一个ssthresh(slowstartthreshold),是一个上限,当cwnd>=ssthresh时,就会进入“拥塞避免算法”(后面会说这个算法)

图片16

 

紧急方式

A:TCP提供了“紧急方式(urgentmode)”,它使一端可以告诉另一端有些具有某种方式的“紧急数据”已经放置在普通的数据流中。另一端被通知这个紧急数据已被放置在普通数据流中,由接收方决定如何处理。可以通过设置TCP首部中的两个字段来发出这种从一端到另一端的紧急数据已经被放置在数据流中的通知。URG比特被置1,并且一个16bit的紧急指针被置为一个正的偏移量,该偏移量必须与TCP首部中的序号字段相加,以便得出紧急数据的最后一个字节的序号。

B:TCP本身对紧急数据知之甚少。没有办法指明紧急数据从数据流的何处开始。TCP通过连接传送的唯一信息就是紧急方式已经开始(TCP首部中的URG比特)和指向紧急数据最后一个字节的指针。

C:用于:最常见的例子是Telnet。当交互用户键入中断键时,使用紧急方式来完成这个功能的例子。

TCP的超时与重传

超时重传是TCP协议保证数据可靠性的另一个重要机制,其原理是在发送某一个数据以后就开启一个计时器,在一定时间内如果没有得到发送的数据报的ACK报文,那么就重新发送数据,直到发送成功为止.

往返时间的测量

超时,如何设置超时时间间隔RTO超时时间的计算是超时的核心部分,TCP要求这个算法能大致估计出当前的网络状况,虽然这确实很困难。要求精确的原因有两个:

(1)定时长久会造成网络利用率不高。

(2)定时太短会造成多次重传,使得网络阻塞.

TCP采用了一个高度动态的算法,来不断的调整时间间隔,这个算法就是Jacobson1988算法

在此算法中,TCP需要维护几个变量:

1)、RTT:对往返时间的当前最佳估计值

当一个数据段被发送出去后,TCP启动定时器,如果在定时器过期之前确认数据段回来的话,则TCP测量一下这次确认所花的时间M,然后根据如下公式更新RTT。

RTT=aRTT+(1-a)M

其中a是平滑因子,典型的值是7/8

这个公式的意思就是说,旧的RTT占有7/8的权重,新的往返时间占有1/8的权重

2)、D:平滑的平均偏差

有了RTT,如何选择RTO仍然需要考量,

它的计算公式是:

D=aD+(1-a)|RTT-M|

¨RTO的计算公式:RTO=RTT+4D

这个算法简单高效

¨Karn算法:

Jacobson算法只用于处理正常的情况,但是当发生重传后,如果收到一个确认,这时候就不用这个算法来调整RTO值了。因为你无法判断这个确认是针对第一次传输,还是后来的重传。在这种情况下,采用Karn算法来调整RTO的值。Karn算法很简单:

1)、对于发生重传的数据段,在收到确认后,不更新RTT

2)、在重传的时候,RTO是倍增的,直到达到最大值的限制。如果重传超过一定的次数,TCP连接会断开

3)、在重传并收到确认后,如果下一次的数据段没有发生重传(即一次性收到确认),则又恢复Jacobson算法

TCP共使用以下四种计时器,即重传计时器、坚持计时器、保活计时器时间等待计时器。这几个计时器的主要特点如下:

1、重传计时器当TCP发送报文段时,就创建该特定报文段的重传计时器。可能发生两种情况:

(1)、若在计时器截止时间到(通常是60秒)之前收到了对此特定报文段的确认,则撤销此计时器。

(2)、若在收到了对此特定报文段的确认之前计时器截止期到,则重传此报文段,并将计时器复位。

2、坚持计时器为了对付零窗口大小通知,TCP需要另一个计时器。假定接收TCP宣布了窗口大小为零。发送TCP就停止传送报文段,直到接收TCP发送确认并宣布一个非零的窗口大小。但这个确认可能会丢失。我们知道在TCP中,对确认是不需要发送确认的。若确认丢失了,接收TCP并不知道,而是会认为它已经完成任务了,并等待着发送TCP接着会发送更多的报文段。但发送TCP由于没有收到确认,就等待对方发送确认来通知窗口的大小。双方的TCP都在永远地等待着对方。要打开这种死锁,TCP为每一个连接使用一个坚持计时器。当发送TCP收到一个窗口大小为零的确认时,就启动坚持计时器。当坚持计时器期限到时,发送TCP就发送一个特殊的报文段,叫做探测报文段。这个报文段只有一个字节的数据。它有一个序号,但它的序号永远不需要确认;甚至在计算对其他部分的数据的确认时该序号也被忽略。探测报文段提醒对端:确认已丢失,必须重传。坚持计时器的值设置为重传时间的数值。但是,若没有收到从接收端来的响应,则需发送另一个探测报文段,并将坚持计时器的值加倍和复位。发送端继续发送探测报文段,将坚持计时器设定的值加倍和复位,直到这个值增大到门限值(通常是60秒)为止。在这以后,发送端每隔60秒就发送一个探测报文段,直到窗口重新打开。

3、保活计时器使用在某些实现中,用来防止在两个TCP之间的连接出现长时期的空闲。假定客户打开了到服务器的连接,传送了一些数据,然后就保持静默了。也许这个客户出故障了。在这种情况下,这个连接将永远地处理打开状态。要解决这种问题,在大多数的实现中都是使服务器设置保活计时器。每当服务器收到客户的信息,就将计时器复位。保活计时器通常设置为2小时。若服务器过了2小时还没有收到客户的信息,它就发送探测报文段。若发送了10个探测报文段(每一个相隔75秒)还没有响应,就假定客户出了故障,因而就终止该连接。

4、时间等待计时器是在连接终止期间使用的。当TCP关闭一个连接时,它并不认为这个连接马上就真正地关闭了。在时间等待期间中,连接还处于一种中间过渡状态。这就可以使重复的FIN报文段(如果有的话)可以到达目的站因而可将其丢弃。这个计时器的值通常设置为一个报文段的寿命期待值的两倍。

注意

一个连接中,有且仅有一个测量定时器被使用。也就是说,如果TCP连续发出3组数据,只有一组数据会被测量。

ACK数据报不会被测量,原因很简单,没有ACK的ACK回应可以供结束定时器测量。

重传

有了超时就要有重传,但是就算是重传也是有策略的,而不是将数据简单的发送。

重传时发送数据的大小

数据在传输的时候不能只使用一个窗口协议,我们还需要有一个拥塞窗口来控制数据的流量,使得数据不会一下子都跑到网路中引起“拥塞”。也曾经提到过,拥塞窗口最初使用指数增长的速度来增加自身的窗口,直到发生超时重传,再进行一次微调。但是没有提到,如何进行微调,拥塞避免算法和慢启动门限就是为此而生。

所谓的慢启动门限就是说,当拥塞窗口超过这个门限的时候,就使用拥塞避免算法,而在门限以内就采用慢启动算法。所以这个标准才叫做门限,通常,拥塞窗口记做cwnd,慢启动门限记做ssthresh。下面我们来看看拥塞避免和慢启动是怎么一起工作的

算法概要(直接从书中拷贝)

对一个给定的连接,初始化cwnd为1个报文段,ssthresh为65535个字节。

TCP输出例程的输出不能超过cwnd和接收方通告窗口的大小。拥塞避免是发送方使用的流量控制,而通告窗口则是接收方进行的流量控制。前者是发送方感受到的网络拥塞的估计,而后者则与接收方在该连接上的可用缓存大小有关。

当拥塞发生时(超时或收到重复确认),ssthresh被设置为当前窗口大小的一半(cwnd和接收方通告窗口大小的最小值,但最少为2个报文段)。此外,如果是超时引起了拥塞,则cwnd被设置为1个报文段(这就是慢启动)。

当新的数据被对方确认时,就增加cwnd,但增加的方法依赖于我们是否正在进行慢启动或拥塞避免。如果cwnd小于或等于ssthresh,则正在进行慢启动,否则正在进行拥塞避免。慢启动一直持续到我们回到当拥塞发生时所处位置的半时候才停止(因为我们记录了在步骤2中给我们制造麻烦的窗口大小的一半),然后转为执行拥塞避免。

快速重传和快速恢复算法

这是数据丢包的情况下给出的一种修补机制。一般来说,重传发生在超时之后,但是如果发送端接受到3个以上的重复ACK的情况下,就应该意识到,数据丢了,需要重新传递。这个机制是不需要等到重传定时器溢出的,所以叫做快速重传,而重新传递以后,因为走的不是慢启动而是拥塞避免算法,所以这又叫做快速恢复算法。流程如下:

当收到第3个重复的ACK时,将ssthresh设置为当前拥塞窗口cwnd的一半。重传丢失的报文段。设置cwnd为ssthresh加上3倍的报文段大小。

每次收到另一个重复的ACK时,cwnd增加1个报文段大小并发送1个分组(如果新的cwnd允许发送)。

当下一个确认新数据的ACK到达时,设置cwnd为ssthresh(在第1步中设置的值)。这个ACK应该是在进行重传后的一个往返时间内对步骤1中重传的确认。另外,这个ACK也应该是对丢失的分组和收到的第1个重复的ACK之间的所有中间报文段的确认。这一步采用的是拥塞避免,因为当分组丢失时我们将当前的速率减半。

ICMP会引起重新传递么?

不会,TCP会坚持用自己的定时器,但是TCP会保留下ICMP的错误并且通知用户。

ICMP的差错

1)一个接收到的源站抑制引起的拥塞窗口cwnd被置为1个报文段大小来发起慢启动,但是慢启动门限ssthresh没有变化,所以窗口将打开直至它或者开放了所有的同路(受窗口大小和往返时间的限制)或者发生了拥塞

2)一个接收到的主机不可达或网络不可达实际上都被忽略,然后执行重传超时中的指数退避

重新分组

当TCP超时并重传时,它不一定要重传同样的报文段。相反,TCP允许进行重新分组而发送一个较大的报文段,这将有助于提高性能。因为TCP是使用字节序号而不是报文序号来进行识别它所要发送的数据和进行确认。

例如当一行被重传时,然后再键入一行,则我们预期下一个重传包含这两次数据,即将两次数据合并为一个数据,然后重传

 

 

TCP拥塞控制机制
TCP的拥塞控制由4个核心算法组成:“慢启动”(Slow Start)、“拥塞避免”(Congestion voidance)、“快速重传 ”(Fast Retransmit)、“快速恢复”(Fast Recovery)。

拥塞避免

从慢启动可以看到,cwnd可以很快的增长上来,从而最大程度利用网络带宽资源,但是cwnd不能一直这样无限增长下去,一定需要某个限制。TCP使用了一个叫慢启动门限(ssthresh)的变量,当cwnd超过该值后,慢启动过程结束,进入拥塞避免阶段。对于大多数TCP实现来说,ssthresh的值是 65536(同样以字节计算)。拥塞避免的主要思想是加法增大,也就是cwnd的值不再指数级往上升,开始加法增加。此时当窗口中所有的报文段都被确认时,cwnd的大小加1,cwnd的值就随着RTT开始线性增加,这样就可以避免增长过快导致网络拥塞,慢慢的增加调整到网络的最佳值。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">