TCP如何实现可靠性,及提高性能

1.确认应答机制(ACK)

这里写图片描述
TCP将每个字节的数据进行了编号,即为序列号。
每一个ACK都带有对应的确认序号,告诉发送者,已经收到这些序号的数据了,下一次从该序号的下一个位置开始发。

2.超时重传机制

这里写图片描述
​假设主机A未收到B发来的确认应答,可能是ACK丢失了。
序列号还有一个作用就是去重,出现丢包时,主机B会出现很多重复数据,那么TCP协议就能利用序列号识别重复的包,并将其丢掉。

超时重传中提到一个特定时间,这个时间是怎么确定的呢?

* 最理想的情况下,找到一个最小时间,保证确认应答一定能在这个时间内返回。

* 这个时间的长短,随着网络环境的不同也不太相同。

* 如果这个时间设置的太长,会影响整体的重传效率。

* 如果这个时间设置的太短,有可能会频繁发送数据的包。

TCP是一个比较高性能的传输协议,因此会动态计算这个时间。

* Linux中,超时500ms是一个单位进行控制,每次判定超时重发的超时时间都是500ms的整数倍。

* 如果重发一次之后,仍然得不到应答,等待2*500ms进行重传。

* 如果仍然得不到应答,等待4*500ms进行重传,以此类推,按指数形式递增。
* 累计到一定的重传次数,TCP认为网络或者对端主机出现异常,强制关闭连接。

3.滑动窗口

确认应答机制,每发送一个数据段,就要给一个ACK来确认,收到ACK后再发送下一个数据段。
但是这样做的性能比较差,数据往返的时间较长。
这里写图片描述
滑动窗口有效的缓解了这个问题。这里的滑动窗口指的是发送端的窗口,与对方接受端的窗口呈正相关。

窗口大小:指的是无需等待确认应答而可以继续发送数据的最大值,在上边这个例子中是4000个字节。
这里写图片描述
发送前4个段,不需要任何等待,直接发送。收到第一个ACK后,滑动窗口向后移动,继续发送第5个段(5001~6001)的数据,依次类推。

操作系统为了维护这一个滑动窗口,需要开辟缓冲区来记录当前还有哪些数据没有应答,只有确认过应答的数据,才能从缓冲区删了。这个缓冲区被称作“发送缓冲区”。

因此,我们知道,窗口越大,网络的吞吐率就越高。

对于滑动窗口,我们也会考虑到,要是数据出现丢包,那么会怎么进行重传的呢?

第一种情况:数据包已抵达,ACK丢失。

这里写图片描述
这种情况下不需要重传,后边的ACK会进行确认的。

第二种情况:数据包丢失(快重传)

这里写图片描述
当某一段报文丢失后,发送端会一直收到1001这样的ACK,如果连续收到三次同样的ACK,就会将对应的数据1001~2000重新发送。当接收端收到了1001后,再次返回ACK就是7001,接收端在这之前就将2001~7000的数据接收到了,被放到了接收端的操作系统内核的接收缓冲区中。这种机制被称为“快重传”。

4.流量控制

TCP可以根据接收端的处理能力,来决定发送数据的多少。

TCP报头中有“窗口大小”的字段,接收端将自己的接收的缓冲区大小放入这个字段,通过发送ACK时捎带信息给发送端。发送端会根据这个窗口大小决定发送速度。一旦发现自己的缓冲区快满了,就会将窗口大小设置成一个更小的值通知给发送端。

发送端接收到这个窗口后,就会减慢自己的发送速度。如果接收端缓冲区满了,就会将窗口的值设为0。这时,发送方不会发送数据,但是会定期发一个窗口探测数据段,即接收端把窗口的大小告诉给发送端。
这里写图片描述
TCP首部选项字节中还包含一个窗口扩大因子M,窗口字段的值左移M位。

5.拥塞控制

流量控制是主机到主机,网络控制是主机到网络。

当我们不知道当前的网络环境是怎么样的时候,假设当前网络已经比较拥堵时,在不清楚当前网络的状态下,发送大量的数据,会造成网络更加的拥堵,出现丢包。
这里写图片描述
TCP中引入慢启动机制,先发少量的数据,了解当前的网络拥堵状态。

拥塞窗口:

* 发送开始时,定义拥塞窗口为1.
* 每收到一个ACK应答,拥塞窗口加1.
* 每次发送数据时,拥塞窗口和发送窗口的较小的那一个决定了滑动窗口的大小。

慢启动按照指数增长,初始较慢,增长速度快。当拥塞窗口超过慢启动的阈值时,不再按照指数增长,而是按照线性增长。每次超时重发时,慢启动阈值变成原来的一半,同时拥塞窗口置为1。这样下去,就可以缓解了网络的拥塞问题。

如果少量的丢包,我们经常超时重传,大量的丢包,就认为是网络拥塞。TCP开始通信后,网络吞吐量逐渐上升,当网络发生拥堵,吞吐量就会下降。TCP可以看成是网络数据的折中方案。

6.延迟应答

接收数据的主机如果立刻返回ACK应答,返回的窗口可能比较小。

假设这样一种情景,结束缓冲区为1M,一次收到500K的数据,如果立刻应答,返回的窗口就是500K,但实际上可能处理的速度非常快,很短的时间就将500K的数据从缓冲区消费掉了。接收端的处理能力可能远比这个大,如果稍微等一段时间再进行应答,返回的窗口可能就会为1M。

所以,接收窗口越大,网络的吞吐量就越大,减少应答数量,传输效率就越高。
但是不是所有的包都可以延迟应答,会有一些限制。

* 数量限制:每隔N个包就应答一次。

* 时间限制:超过最大延迟时间就应答一次。

数量限制和时间限制,在不同的操作下也会有不同,一般N取2,超时时间取200ms。

7.捎带应答

确认信息被附在往外发送的数据帧上(使用帧头中的ask域),实际上,确认报文搭了下一个外发数据帧的便车。

8.面向字节流

创建一个TCP的socket,同时在内核中创建一个发送缓冲区和一个接收缓冲区。
调用write,数据首先会写入发送缓冲区。如果发送的数据太短,会等待缓冲区长度合适的时候再一起发送,如果发送的数据太长,会进行分片。
接收的数据时,数据从网卡驱动程序到达内核的接收缓冲区。然后应用程序调用read从接收缓冲区拿数据。
TCP的一个连接,不仅有发送缓冲区,也有接收缓冲区。则这个连接中,既可以读数据,又可以写数据,被称为“全双工”。

UDP是面向数据报的,而TCP是面向字节流的,会存在一个问题,称为“粘包问题”。

粘包问题

站在传输层的角度,TCP是一个报文一个报文的,按照序号排放在缓冲区里,当站在应用层的角度,我们看到的是一串连续的字节数据。当看到这一连串的字节数据,应用程序就不知道从哪开始到哪是一个数据包。我们这个时候,可以联想到家里蒸了满满的一笼包子,你去拿的时候,就可能会几个包子粘在一起。

其实,联想到生活实际,我们知道避免这个问题,归根结底就是明确两个包之间的边界。

* 对于定长的包,每次都按固定大小读取。
* 对于变长的包,在报头中可以约定一个包的总长度,就明确了包的结束位置。
* 对于变长的包,也可以在包与包之间增加一个分隔符(http协议,空行将报文和有效字段分隔开)。

UDP中不存在粘包问题。

总结:

TCP即保证了可靠性,又要尽可能的提高性能。

可靠性

* 校验和
* 序列号
* 确认应答
* 超时重发
* 连接管理
* 流量控制
* 拥塞控制

提高性能

* 滑动窗口
* 快速重传
* 延迟应答
* 捎带应答

三个定时器

* 超时重传定时器
* 保活定时器(每隔一段时间发送一次数据,收到响应,表示活着)。
* TIME_WAIT定时器

基于TCP应用层协议

* HTTP
* HTTPS
* SSH
* Telnet
* FTP
* SMTP

猜你喜欢

转载自blog.csdn.net/zwe7616175/article/details/80456360
今日推荐