Tcp及流量控制/拥塞控制

一 TCP三次握手

               

(右图为简化版的)

起初服务器和客户端都为CLOSED状态。在通信开始前,双方都得创建各自的传输控制块(TCB)。 服务器创建完TCB后遍进入LISTEN状态,此时准备接收客户端发来的连接请求。

第一次握手 

客户端向服务端发送连接请求报文段。该报文段的头部中SYN=1,ACK=0,seq=x。请求发送后,客户端便进入SYN-SENT状态。

(1) SYN=1,ACK=0表示该报文段为连接请求报文。(SYN=1的报文段不能有数据部分,但要消耗掉一个序号)

(2) x为本次TCP通信的字节流的初始序号

  

第二次握手

服务端收到连接请求报文段后,如果同意连接,则会发送一个应答:SYN=1,ACK=1,seq=y,ack=x+1。 
该应答发送完成后便进入SYN-RCVD状态。

(1) PS1:SYN=1,ACK=1表示该报文段为连接同意的应答报文。

(2) PS2:seq=y表示服务端作为发送者时,发送字节流的初始序号。

(3) PS3:ack=x+1表示服务端希望下一个数据报发送序号从x+1开始的字节。

 

第三次握手

当客户端收到连接同意的应答后,还要向服务端发送一个确认报文段,表示:服务端发来的连接同意应答已经成功收到。 
该报文段的头部为:ACK=1,seq=x+1,ack=y+1。 
客户端发完这个报文段后便进入ESTABLISHED状态,服务端收到这个应答后也进入ESTABLISHED状态,此时连接的建立完成!

 

为什么连接建立需要三次握手,而不是两次握手? 

防止失效的连接请求报文段被服务端接收,从而产生错误。

若建立连接只需两次握手,客户端并没有太大的变化,仍然需要获得服务端的应答后才进入ESTABLISHED状态,而服务端在收到连接请求后就进入ESTABLISHED状态。此时如果网络拥塞,客户端发送的连接请求迟迟到不了服务端,客户端便超时重发请求,如果服务端正确接收并确认应答,双方便开始通信,通信结束后释放连接。此时,如果那个失效的连接请求抵达了服务端,由于只有两次握手,服务端收到请求就会进入ESTABLISHED状态,等待发送数据或主动发送数据。但此时的客户端早已进入CLOSED状态,服务端将会一直等待下去,这样浪费服务端连接资源。

 

二 TCP四次挥手

                      

 TCP连接的释放一共需要四步,因此称为『四次挥手』。 我们知道,TCP连接是双向的,因此在四次挥手中,前两次挥手用于断开一个方向的连接,后两次挥手用于断开另一方向的连接。

 

第一次挥手 
若A认为数据发送完成,则它需要向B发送连接释放请求。该请求只有报文头,头中携带的主要参数为: 
FIN=1,seq=u。此时,A将进入FIN-WAIT-1状态。

(1) FIN=1表示该报文段是一个连接释放请求。

(2) seq=u,u-1是A向B发送的最后一个字节的序号。

 

第二次挥手 
B收到连接释放请求后,会通知相应的应用程序,告诉它A向B这个方向的连接已经释放。此时B进入CLOSE-WAIT状态,并向A发送连接释放的应答,其报文头包含: 
ACK=1,seq=v,ack=u+1。

(1) ACK=1:除TCP连接请求报文段以外,TCP通信过程中所有数据报的ACK都为1,表示应答。

(2) seq=v,v-1是B向A发送的最后一个字节的序号。

(3) ack=u+1表示希望收到从第u+1个字节开始的报文段,并且已经成功接收了前u个字节。

A收到该应答,进入FIN-WAIT-2状态,等待B发送连接释放请求。

第二次挥手完成后,A到B方向的连接已经释放,B不会再接收数据,A也不会再发送数据。但B到A方向的连接仍然存在,B可以继续向A发送数据。

 

第三次挥手 
当B向A发完所有数据后,向A发送连接释放请求,请求头:FIN=1,ACK=1,seq=w,ack=u+1。B便进入LAST-ACK状态。

 

第四次挥手 
A收到释放请求后,向B发送确认应答,此时A进入TIME-WAIT状态。该状态会持续2MSL时间,若该时间段内没有B的重发请求的话,就进入CLOSED状态,撤销TCB。当B收到确认应答后,也便进入CLOSED状态,撤销TCB。

 

为什么要四次挥手?

TCP 是全双工通信,可以双向传输数据。任何一方都可以在数据传送结束后发出连接释放的通知,待对方确认后进入半关闭状态。当另一方也没有数据再发送的时候,则发出连接释放通知,对方确认后就完全关闭了 TCP 连接。

举个例子:A 和 B 打电话,通话即将结束后。

  1. 第一次挥手:A 说“我没啥要说的了”
  2. 第二次挥手:B 回答“我知道了”,但是 B 可能还会有要说的话,A 不能要求 B 跟着自己的节奏结束通话
  3. 第三次挥手:于是 B 可能又巴拉巴拉说了一通,最后 B 说“我说完了”
  4. 第四次挥手:A 回答“知道了”,这样通话才算结束。

 

为什么不能把服务器发送的 ACK 和 FIN 合并起来,变成三次挥手?

因为服务器收到客户端断开连接的请求时,可能还有一些数据没有发完,这时先回复 ACK,表示接收到了断开连接的请求。等到数据发完之后再发 FIN,断开服务器到客户端的数据传送。

 

如果第二次挥手时服务器的 ACK 没有送达客户端,会怎样?

客户端没有收到 ACK 确认,会重新发送 FIN 请求。

 

为什么第四次挥手客户端需要等待 2*MSL(报文段最长寿命)时间后才进入 CLOSED 状态?

第四次挥手时,客户端发送给服务器的 ACK 有可能丢失,如果服务端因为某些原因而没有收到 ACK 的话,服务端就会重发 FIN,如果客户端在 2*MSL 的时间内收到了 FIN,就会重新发送 ACK 并再次等待 2MSL,防止 Server 没有收到 ACK 而不断重发 FIN。

若A发完确认应答后直接进入CLOSED状态,那么如果该应答丢失,B等待超时后就会重新发送连接释放请求,但此时A已经关闭了,不会作出任何响应,因此B永远无法正常关闭。

MSL(Maximum Segment Lifetime) : 一个片段在网络中最大的存活时间,2MSL 就是一个发送和一个回复所需的最大时间。如果直到 2MSL,Client 都没有再次收到 FIN,那么 Client 推断 ACK 已经被成功接收,则结束 TCP 连接。

 

 

三 流量控制

1 滑动窗口

数据的传送过程中很可能出现接收方来不及接收的情况,这时就需要对发送方进行控制以免数据丢失。利用滑动窗口机制可以很方便地在TCP连接上对发送方的流量控制。TCP的窗口单位是字节,不是报文段,发送方的发送窗口不能超过接受方给出的接收窗口的数值

考虑一种特殊的情况,某一时刻接收方无法接收任何数据,就会发送零窗口大小的报文,此时发送方将发送窗口设置为0,停止发送数据; 之后接收方有接收能力,发送了非零窗口大小的报文,但是这个报文中途丢失,那么发送方的发送窗口就一直为0导致死锁。为此,TCP为每一个连接设有一个持续计时器. 当TCP连接的一方收到对方的零窗口通知时就启动持续计数器。若持续计时器时间到期,就发送一个零窗口探测报文段,那么收到这个报文段的一方就重置持续计数器,给出现在的窗口值。

 

2 发送时机

  (1) TCP维持一个变量MSS,等于最大报文段长度。只要缓冲区存放的数据达到MSS字节时,就组装成了一个TCP报文段发送出去。 
  (2) 由发送方的应用进程指明要发送的报文段,即:TCP支持推送操作。 
  (3) 发送方的一个计时器期限到了,这时就把当前已有的缓存数据装入报文段(但长度不能超过MSS大小)发送出去。

 

3 Nagle算法

发送方把第一个数据字节发送出去,把后面到达的数据字节缓存起来。当发送方接收对第一个数据字符的确认后,再把发送缓存中的所有数据组装成一个报文段再发送出去,同时继续对后面的数据进行缓存。只有在收到前一个报文段的确认后,才继续发送下一个报文段。TCP规定一个连接最多只能有一个未被确认的未完成的小分组,在该分组的确认到达之前不能发送其他的小分组。当数据到达较快而网络速率较慢时,用这样的方法可明显地减少所用的网络带宽。

Nagle算法还规定: 当到达的数据已达到发送窗口大小的一半或已经达到报文段的最大长度,就可立即发送一个报文段

 

 

4 延迟ACK

如果tcp对每个数据包都发送一个ack确认,那么只是一个单独的数据包为了发送一个ack代价比较高,所以tcp会延迟一段时间,如果这段时间内有数据发送到对端,则捎带发送ack,如果在延迟ack定时器触发时候,发现ack尚未发送,则立即单独发送;

延迟ACK好处:

(1) 避免糊涂窗口综合症;

(2) 发送数据的时候将ack捎带发送,不必单独发送ack;

(3) 如果延迟时间内有多个数据段到达,那么允许协议栈发送一个ack确认多个报文段;

 

5  当Nagle遇上延迟ACK

试想如下典型操作,写-写-读,即通过多个写小片数据向对端发送单个逻辑的操作,两次写数据长度小于MSS,当第一次写数据到达对端后,对端延迟ack,不发送ack,而本端因为要发送的数据长度小于MSS,所以nagle算法起作用,数据并不会立即发送,而是等待对端发送的第一次数据确认ack;这样的情况下,需要等待对端超时发送ack,然后本段才能发送第二次写的数据,从而造成延迟

  

6 糊涂窗口综合症 

TCP接收方的缓存已满,而交互式的应用进程一次只从接收缓存中读取1字节(这样就使接收缓存空间仅腾出1字节),然后向发送方发送确认,并把窗口设置为1个字节(但发送的数据报为40字节的的话)。然后,发送方又发来1个字节的数据(发送方的IP数据报是41字节),接收方发回确认,仍然将窗口设置为1个字节。这样,网络的效率很低。要解决这个问题,可让接收方等待一段时间,使得或者接收缓存已有足够空间容纳一个最长的报文段或者等到接收方缓存已有一半的空闲空间。只要出现这两种情况,接收方就发回确认报文,并向发送方通知当前的窗口大小。此外,发送方也不要发送太小的报文段,而是把数据报积累成足够大的报文段,或达到接收方缓存的空间的一半大小。

 

 

四 拥塞控制

网络拥塞现象是指到达通信子网中的某一部分流量过多,使得该部分网络来不及处理,以致引起至整个网络性能下降的现象,严重时导致网络通信业务陷入停顿的现象。拥塞控制是通过拥塞窗口处理

发送报文段速率确定: 
   [1]. 全局考虑防止拥塞 <- - 拥塞窗口 (Congestion Window) - -> 发送端流量控制,发送端根据自己估计的网络拥塞程度而设置的窗口值; 
   [2]. 接收端的接收能力 <- - 接收窗口 (Reciver Window) - -> 接收端流量控制,接收端根据目前的接收缓存大小所许诺的最新窗口值;

    发送方窗口的上限值 = Min [ rwind, cwind ] 
  rwind < cwind 时,接收方的接收能力限制发送方窗口的最大值。 
  当cwind < rwind 时,网络的拥塞限制发送方窗口的最大值。

针对拥塞控制共有4种算法: 慢启动 拥塞避免 快重传 快恢复

我们假定: (1) 数据单方向传送,而另外一个方向只传送确认。  (2)接收方总是有足够大的缓存空间,因为发送窗口的大小由网络的拥塞程度来决定

 1 慢启动

发送方维护一个拥塞窗口cwind的状态变量,拥塞窗口的大小取决于网络的拥塞程度,动态变化。通过逐渐增加cwind的大小来探测可用的网络容量,防止连接开始时采用不合适的发送量导致网络拥塞。

当主机开始发送数据时,如果通过较大的发送窗口立即将全部数据字节都注入到网络中,由于不清楚网络状况,有可能引起网络拥塞。较好的方法是试探,从小到大逐渐增大发送端拥塞窗口的cwind数值。

当rwind足够大时,为防止拥塞窗口cwind的增长引起网络拥塞,还需要另外一个变量,慢开始门限ssthresh

当cwind < ssthresh时,使用上述慢启动算法;

当cwind > ssthresh时,停止使用慢启动算法,改为拥塞避免算法

 

2 拥塞避免

让拥塞窗口cwind缓慢地增大,每经过一个往返时间RTT就把发送方的拥塞窗口cwind+1, 而不是加倍。这样拥塞窗口cwind线性缓慢增长,比慢开始算法的拥塞窗口增长速率缓慢地多。

无论慢启动开始阶段还是在拥挤避免阶段,只要发送方判断网络出现拥塞(没收到ACK),就把慢启动门限ssthresh设置为出现拥塞时的cwind的一半。然后把拥塞窗口cwind重新设置为1,执行慢启动算法。目的是迅速减少发送到网络中的流量。

控制过程:

(1) TCP连接初始化,将拥塞窗口cwind设置为1个报文段,即cwind = 1

(2) 执行慢开始算法,cwind按指数规律增长,直到cwind == ssthresh时,开始拥塞避免算法,cwind按线性规律增长

(3) 当网络发生阻塞,把ssthresh值更新为拥塞前cwind的一半(12 = 24/2),cwind重新设置为1,再按照此流程执行

 

 

3 快重传和快恢复

一条TCP连接有时会因等待重传计时器的超时而空闲较长的时间,慢开始和拥塞避免无法很好地解决这类问题,因此提出了快重传和快恢复的拥塞控制方法

为使发送方及早知道有报文没有达到对方,快重传算法要求接受方每收到一个报文段后就立即发出重复确认。快重传算法并非取消了重传机制,只是在某些情况下更早地重传丢失的报文段。即,当TCP源端收到3个相同的ACK确认时,即认为有数据包丢失,则源端重传丢失的数据包,而不必等待超时。由于发送方尽早重传未被确认的报文段。因此,采用快重传后可以使整个网络吞吐量提高20%

快重传算法要求首先接收方收到一个失序的报文段后就立刻发出重复确认,而不要等待自己发送数据时才进行捎带确认。接收方成功的接受了发送方发送来的M1、M2并且分别给发送了ACK,现在接收方没有收到M3,而接收到了M4,显然接收方不能确认M4,因为M4是失序的报文段。如果根据可靠性传输原理接收方什么都不做,但是按照快速重传算法,在收到M4、M5等报文段的时候,不断重复的向发送方发送M2的ACK,如果接收方一连收到三个重复的ACK,那么发送方不必等待重传计时器到期,由于发送方尽早重传未被确认的报文段。

控制过程:

(1) 当发送方连续收到3个重复确认时,执行"乘法减小"算法,慢启动门限减半,为了预防网络发生拥塞

(2) 由于发送方现在认为网络很可能没有发生拥塞,因此不执行慢启动。而是把cwind值设为新的门限值,然后执行拥塞避免算法,cwind值线性增大,避免了当网络拥塞不够严重时采用"慢启动"算法而造成过大地减小发送窗口尺寸的现象,这就是快恢复

posted @ 2018-03-29 17:13  balfish  阅读(2180)  评论(0编辑  收藏  举报