TCP协议相关

TCP

TCP最核心的一点就是可靠的传输协议,那么TCP是靠什么来保证可靠的传输服务呢?

三次握手

就如同打电话一样

A:你能听到我的声音吗?

B:我能听到你的声音,你能听到我的声音吗?

A:我也能听到你的声音。

这样就能在有限的三次握手中确认双方都能够收到对方的消息。原本的握手是四次,不过把B的回答和问话优化为了一句。

具体的实现则是通过ack和syn这两个字段实现的:

  • 第一步:client 发送 syn 到server 发起握手;
  • 第二步:server 收到 syn后回复syn+ack给client;
  • 第三步:client 收到syn+ack后,回复server一个ack表示收到了server的syn+ack;

四次挥手

为什么握手需要3次,挥手却要需要4次呢?

客户端和服务端在进行通信,客户端请求完了。

客户端:我这边已经搞定了,要关闭连接。

服务端:收到!同意你的关闭请求。

这时,客户端不能再发送数据,服务端也不能再接受数据了。但是服务端向客户端的传输可能没有结束,不能马上告诉客户端自己也完事了。不过会告诉客户端,我已经收到了。当过了一会服务端也搞定了则主动给客户端发送信息。

服务端:我这边也搞定了,需要关闭连接。

客户端:收到!

此时,服务端收到客户端的回答后关闭连接,之后客户端没有收到回复后,证明服务端已经关闭,就自动关闭连接

具体的实现通过fin和ack这两个字段实现的:

  • 第一步: client主动发送fin包给server
  • 第二步: server回复ack(对应第一步fin包的ack)给client,表示server知道client要断开了
  • 第三步: server发送fin包给client,表示server也可以断开了
  • 第四部: client回复ack给server,表示既然双发都发送fin包表示断开,那么就真的断开吧

超时重传

超时重传是TCP协议保证数据可靠性的一个重要机制,其原理是在发送某一个数据以后就开启一个计时器,在一定时间内如果没有得到发送的数据报的ACK报文,那么就重新发送数据,直到发送成功为止。三次握手,正常的数据传输,以及四次握手过程中只要出现超时情况,都会触发重传。

滑动窗口

为什么需要滑动窗口?

tcp以一个段为单位,每发送一段就进行一次确认应答处理。这样包的往返时间就会变长,通信性能就会变低。

A:1

B:收到1

A:2

B:收到2

......

为什么不能一次发送多个数据来提高性能呢?

A:1,、2、3、...

B:收到1、2、3、...

这就是窗口的由来,而无需等待确认就可以发送的数据的最大值就是窗口的大小。这个机制会大量用到缓冲区,通过对多个段同时进行确认。如果窗口内的部分数据出现了丢包,还是要进行重传,所以发送端需要这些数据包的缓存,直到收到了他们的确认应答。

收到确认的情况下,将窗口滑送到确认应答的序号的位置,这样可以顺序的讲多个段同时发送来提高性能,

考虑一种情况,就是发送成功了,但是返回的应答丢失了。首先按照以往的模式来的话,返回的应答丢失也要失败重传。有了窗口后,一定时间内不会马上重传,还是会发送数据。这样避免了重复的发送数据。

考虑另一种情况,有一段数据发送不成功。如果有一段数据发送不成功,其别的数据返回的时候不仅返回自己数据的应答,还会有未接受到的数据的确认应答。当窗口比较大的时候出现丢包的时候,会一直受到丢失的数据的确认应答,当这种情况出现3次后,缺失的数据将再次被进行重发。

拥塞控制

有了窗口机制大大提升了网络的传输效率。但是这样有一个缺点就是。网路是个共享的环境,经常因为其他主机之间的通信使得网络堵塞。如果再发送较多的数据,很有可能导致整个网络瘫痪。tcp的拥塞控制主要有4个算法:1. 慢启动;2. 拥塞避免;3. 快重传;4. 快恢复;1和2是拥塞发生前的算法,3是拥塞发生时,而4是拥塞发生后。

rwnd与cwnd
  • rwnd是用于流量控制的窗口大小,主要取决于接收方的处理速度,由接收方通知发送方被动调整。
  • cwnd是用于拥塞处理的窗口大小,取决于网络状况,由发送方探查网络主动调整。
慢启动算法

对于刚刚加入网络的链接,要一点点的提速,不要妄图一步到位。

  1. 连接刚建好,初始化cwnd = 1(当然,通常不会初始化为1,太小),表明可以传一个MSS大小的数据。

  2. 每收到一个ACK,cwnd++,线性增长。

  3. 每经过一个RTT,cwnd = cwnd * 2,指数增长(主要增长来源)。

  4. 还有一个ssthresh(slow start threshold),当cwnd >= ssthresh时,就会进入拥塞避免算法。

如果网速很快的话,ack返回快,那这个慢启动一点也不慢,将会以指数的速度增长。

拥塞避免算法

前面说过,当cwnd >= ssthresh(通常ssthresh = 65535)时,就会进入拥塞避免算法(Congestion Avoidance):缓慢增长,小心翼翼的找到最优值

  1. 每收到一个Ack,cwnd = cwnd + 1/cwnd,显然,cwnd > 1时无增长。
  2. 每经过一个RTT,cwnd++,线性增长(主要增长来源)。

慢启动算法主要呈指数增长,粗犷型,速度快(“慢”是相对于一步到位而言的);而拥塞避免算法主要呈线性增长,精细型,速度慢,但更容易在不导致拥塞的情况下,找到网络环境的cwnd最优值。

快重传

慢启动与拥塞避免算法作用在拥塞发生前,采取不同的策略增大cwnd;如果已经发生拥塞,则需要采取策略减小cwnd。那么,TCP如何判断当前网络拥塞了呢?很简单,如果发送方发现有Seq发送失败(表现为“丢包”),就认为网络拥塞了

丢包后,有两种重传方式,对应不同的网络情况,也就对应着两种拥塞发生时的控制算法:

  1. 超时重传。TCP认为这种情况太糟糕,调整力度比较大:
    1. ssthresh = cwnd /2
    2. cwnd = 1,重新进入慢启动过程(网络糟糕,要慢慢调整)
  2. 快速重传。TCP认为这种情况通常比RTO超时好一些,主流实现TCP Reno的调整力度更柔和(TCP Tahoe的实现和RTO超时一样暴躁):
    1. ssthresh = cwnd /2
    2. cwnd = cwnd /2,进入快速恢复算法(网络没那么糟,可以快速调整,见下)

可以看到,不管是哪种重传方式,ssthresh都会变成cwnd的一半,仍然是指数回退,待拥塞消失后再逐渐增长回到新的最优值,总体上在最优值(动态)附近震荡。

回退后,根据不同的网络情况,可以选择不同的恢复算法。慢启动已经介绍过了,下面介绍快速恢复算法。

快速恢复

如果触发了快速重传,即发送方收到至少3次相同的Ack,那么TCP认为网络情况不那么糟,也就没必要提心吊胆的,可以适当大胆的恢复。为此设计快速恢复算法(Fast Recovery),下面介绍TCP Reno中的实现。

回顾一下,进入快速恢复之前,cwnd和sshthresh已被更新:

  1. ssthresh = cwnd /2
  2. cwnd = cwnd /2

然后,进入快速恢复算法:

  1. cwnd = ssthresh + 3 * MSS (尝试一步到位)
  2. 重传重复Ack对应的Seq
  3. 如果再收到该重复Ack,则cwnd++,线性增长(缓慢调整)
  4. 如果收到了新Ack,则cwnd = ssthresh ,然后就进入了拥塞避免的算法了

你可能感兴趣的:(TCP协议相关)