TCP的可靠性传输的实现

一、可靠性的七种实现机制
1、确认应答(ACK)机制
TCP将每个字节的数据都进行了编号,即为序列号。确认序号=序号+1

这里写图片描述
每个ACK都有对应的确认序列号,意思是告诉发送者已经收到了数据,下一个数据应该从哪里开始发送。
2、超时重传机制
超时重传的两种情况
(1)如果主机A发送给主机B的报文,主机B在规定的时间内没有及时收到主机A发送的报文,我们可以认为是ACK丢了,这时就需要触发超时重传机制。

这里写图片描述
(2)如果主机A未收到B发来的确认应答,也可能是因为ACK丢了。因此主机B会收到很多重复的数据,那么,TCP协议需要能够识别出那些包是重复的包,并且把重复的包丢弃,这时候我们可以用前面提到的序列号,很容易做到去重的效果
这里写图片描述
超时重传时间的确认
超时重传的时间设置的太短,会引起很多报文的不必要重传;
时间设置的过长,又会使网络的空闲时间增大,降低传输效率;
TCP采用了一种自适应算法,它记录一个报文发出的时间,以及收到相应确认的时间,者两个时间之差就是报文的往返时间RTT;
最理想的情况下,找到一个最小的时间,保证确认应答一定能在这个时间内返回
Linux中,超时以500s为一个单位进行控制,每次判定超时重发的超时时间都是500ms的整数倍;如果重发一次仍然得不到应答,等待2*500ms后再进行重传;如果仍然得不到应答,等待4*500ms进行重传,依次类推,以指数形式递增;累积到一定的重传次数,TCP认为网络或者对端主机出现异常,强制关闭连接。
3、连接管理机制
TCP是面向连接的,进行可靠性传输。
4、流量控制
所谓流量控制,就是让发送方的发送速率不要太快,要让对方来得及接受,流量控制的实现方法:接收端将自己可接受的缓冲区大小放入TCP首部中的”窗口大小字段”,通过ACK通知发送端;窗口大小字段越大,说明网络的吞吐量越高;接收端一旦发现自己的缓冲区快满了,就会将窗口大小设置成一个更小的值给发送端;发送端接受到这个窗口之后,就会减慢自己的发送速率;如果接受缓冲区满了,就会将窗口设置为0,这时发送方不在发送数据,但是需要定期发送一个窗口探测数据段,使接收端把窗口大小告诉发送端。
利用滑动窗口来实现流量控制。
发送方的发送窗口,不能超过接收方给出的接受窗口的数值。
当发送一次数据,等到确认应答时才可以发送下一个数据段,这样的效率会很低,我们利用滑动窗口,无需等待确认应答而可以继续发送数据的最大值;收到第一个ACK后,滑动窗口向后移;*操作系统为了维护这个滑动窗口,需要开辟发送缓冲区来记录当前还有那些数据没有应答,只有确认应答过的数据,才能从缓冲区删除掉;窗口越大,网络的吞吐率越高***TCP为每一个连接设有一个持续计时器,只要TCP连接的一方收到对方的零窗口通知,就启动持续计时器,若持续计时器设置的时间到期,就发送一个零窗口探测报文段(仅携带一个字节的数据),对方就在确认这个探测报文段时给出了现在的窗口值,如果仍然是0,那么收到这个报文段的一方就重新设置持续计时器;如果窗口不是0,那么死锁的僵局就可以打破。
这里写图片描述
传送过程中,出现了丢包的两种情况:
(1)数据包在传送过程中丢失了
这里写图片描述
当发送端的某一段报文丢失后,发送端会一直收到1001这样的ACK;如果发送端主机连续三次收到同样的”1001”这样的应答,就会将对应的数据1001-2000重新发送;当成功接收到1001之后,再次返回的ACK就是6001了,接收端在之前就已经收到(2001-7000)了,被放到了接收端操作系统内核的接受缓冲区中。这种机制被称作快重传
(2)数据包已经递达,ACK丢了
这里写图片描述
这种情况下,部分ACK丢失了不要紧,因为可以通过后续的ACK进行确认。
5、拥塞控制
虽然有了滑动窗口机制,如果一开始就发送大量数据,很有可能引发很多问题。
TCP加入慢启动机制,先发少量的数据探探路,看看当前网络的拥塞状态,再决定按照多大的速率进行传送,刚开始时,定义拥塞窗口的大小为1,每次接收到一个ACK应答,拥塞窗口值加1,每次发送数据包的时候,将拥塞窗口和接收端主机反馈的窗口大小做比较,取较小的值作为实际发送的窗口。这样的拥塞窗口增长的速度是指数级别的,慢启动只是指初始时慢,但是增长速度很快,不久就可以造成网络拥塞。为了不让窗口一直加倍增长,我们引入一个慢启动的阈值,当拥塞窗口超过这个阈值的时候,不在按指数方式增长,而是按照线性方式增长。
这里写图片描述
当TCP初次启动时,慢启动阈值等于窗口的最大值,每次超时重发的时候,慢启动阈值会变为原来的一半,同时拥塞窗口置回1。
少量丢包,我们仅仅触发超时重传;大量丢包,我们就认为是网络拥塞。
当TCP通信开始后,网络吞吐量会逐渐上升,随着网络发生拥堵,吞吐量又会立即下降。
拥塞控制,归根结底是TCP协议想尽可能快的把数据传输给对方,但又要避免给网络造成最大压力的最好方案。
二、TCP提高性能的四种机制
1、滑动窗口
TCP的滑动窗口都是以字节为单位的。凡是已发送过的数据,在未收到确认之前,都必须暂时保留,以便在超时重传时使用。发送窗口里面的序号表示允许发送的序号,发送窗口后沿的后面部分表示已经发送并且已经得到确认。
假若发送一个报文段,确认的时间到了还未收到ACK,这时就会触发重传机制,经过了一段时间收到了确认报文,如何判断此报文是对先发送报文的确认还是对后发送报文的确认?
这里写图片描述

若收到的确认是对重传报文的确认,却被主机当成是对原报文段的确认,这样计算出来的RTT和超时重传时间RTO就会偏大,若后面发送的数据又是经过超时重传才收到确认报文段,按此方法的到的超时重传时间就会越长。
若收到的确认是对原来报文的确认,却被主机当成是对重传文段的确认,这样计算出来的RTT和超时重传时间RTO就会偏小,这样会导致报文段过多的重传。
2、快速重传
3、延迟应答
如果接收数据的主机立刻返回ACK应答,这时候返回的窗口可能比较小。可以等待适量的时间,可以使返回的窗口值增大。
4、捎带应答
客户端服务器在应用层也是一收一发的,客户端给服务器发送一个消息后,相应的服务器也会给客户端回一个消息。那么我们可以在服务器给客户端回消息时捎带回应ACK
三、TCP当中的粘包问题,UDP不存在粘包问题
首先粘包中的”包”指的是应用层的数据包,在TCP协议头中,没有像UDP一样的报文长度,只有一个序号一样的字段。从应用层的角度来看,应用层交付给传输层的数据都是一连串的字节数据,应用程序看到这一连串的字节数据,并不知道是从哪个部分到哪个部分是一个完整的应用数据包。而从传输层的角度来看,TCP是一个报文过来的,按序号排好序的放在缓冲区中。
如何避免粘包问题?
在应用层将包分开,明确两个包的边界
对于定常的包,保证每次都按固定大小读取即可;
对于变长的包,可以在报头的位置约定一个包总长度的字段,从而就知道了包的结束位置;
对于边长的包,还可以在包和包之间使用明确的分隔符(应用层协议是由程序员自己来确定的)
四、TCP异常的情况(常见面试题)
进程终止:进程终止会释放文件描述符。仍然可以发送FIN,和正常关闭没什么区别
机器重启:和进程终止的情况相同
机器掉电/网线断开:接收端认为连接还在,一旦接收端有写入操作,接收端发现连接已经不在了,就会进行reset,即使没有写入操作,TCP自己也内置一个保活定时器,定期访问对方是否还在,如果对方不在,也会把连接释放;
基于TCP的应用层协议有:HTTP、HTTPS、SSH、Telent、FTP、SMTP
TCP一般应用于可靠性传输,应用于文件传输,重要状态更新等重要场景;UDP用于对高速传输和实时性要求较高的通信领域。
如何用UDP实现可靠性传输?(面试题)
可以参考TCP
引入序列号,保证数据顺序;引入确认应答,保证对端收到了数据;引入超时重传,如果隔一段时间没有应答,就重新发送数据。

猜你喜欢

转载自blog.csdn.net/wyn126/article/details/80472411