TCP协议 - 可靠性

在前篇文章中介绍了TCP协议的三大特性,其中可靠性是依赖一系列的机制,如:校验和,分组发送,超时重传,流量控制获得保证。算法

一.数据交互

TCP在交互数据时,采用多种机制保证可靠性,同时也保证TCP的性能,主要是分组、延迟ACK等等。服务器

1.分组确认

对于连续的数据传输有三种方式:网络

  1. 单个单个字节发送
  2. 将整个连续数据发送
  3. 将整个连续数据拆分红一个个的分组包,而后逐个发送

显然前两种方式都是比较极端,单个单个字节发送对于成块连续数据而言效率很是低,整块连续数据发送对于比较大的数据而言更不现实,TCP缓冲区有限,网络带框也是有限,对于过大数据不可能这样发送。tcp

在TCP协议栈中,有发送缓冲区和接收缓冲区用于缓冲存储即将发送的数据和收到的数据。当应用须要发送连续数据时,TCP将应用的数据存储到缓冲区中,TCP会根据必定的机制将缓冲区数据发送出去,应用同时也将数据写入缓冲区。接收方TCP在收到数据后,存入接收缓冲区中,TCP根据必定机制再将缓冲区中数据提交给应用处理。性能

TCP将成块的数据发送分红一个个的分组报文发送出去,其中分组报文大小不会超过MSS(Max Segment Size,最大的报文段大小)。TCP对发送的每一个字节都采用序号的方式进行标识追踪,序号在创建TCP链接时即已经肯定。发送端发送第一个字节数据的序号为创建TCP链接时的SEQ + 1。序号在这里有如下几种做用:大数据

  1. 序号用于标识追踪发送数据的每一个字节
  2. 接收端根据序号进行ACK确认
  3. 序号保证数据的有序性,接收端根据序号能够进行排序

标识发送数据的起始序号为TCP报文中的序号,没增长一个字节序号就增长1,因此发送的下个TCP报文的序号为上一个序号加上个报文的大小。经过这种方式TCP能够表示每一个已经发送的每一个字节。设计

对成块连续数据分组发送的方式,会带来不少问题,好比发送方如何确认接收方已经收到数据?3d

TCP采用ACK确认机制解决该问题,接收方在接受到数据后,必需要回复一个ACK的确认包告诉发送方已经收到该报文。其中有个确认序号,标识期待下次接收到的数据的起始序号。这个ACK的确认序号就是TCP报文段中的确认序号。blog

看到这个机制,对于了解消息MQ读者应该能联想到MQ是如何解决消息丢失的问题。能够说MQ的消息回执确认机制也正式来源于TCP的这个机制。上层不少设计都是来源于底层的设计思想。排序

经过确认机制能够保证报文丢失时,发送方可以感知到,这一点也是TCP的可靠性保证之一。

从以上图中,能够看到客户端发送的报文都有相应的确认报文用于通知客户端服务器端已经接收到。分组发送,ACK确认机制是TCP可靠性保证的机制之一

2.延迟的ACK

大多数客户服务器之间的通讯都是双方向的数据流动,在这种状况下若是是客户端应用请求TCP发送数据,而后服务端的TCP回复ACK确认,而后服务端TCP再发送应用层的响应值客户端,客户端再发送ACK。这样来回须要四次,无疑增长网络负担,使得网络拥塞。为了提高TCP的性能,尽量的减少网络负担,延迟ACK策略决定接收端TCP在接手到发送方的数据后,不当即回复ACK,而是通过一个ACK的延迟时间段后再回复ACK。若是这个时间段内有数据须要发送,则放在缓冲区,而后将ACK和这个数据发送做为一个报文段发给发送方。

通常这个延迟的时间是200ms,能够看出,TCP是重复利用网络资源,重复利用TCP数据报,达到最大化的网络传输。

从上图能够看出,每一个PSH包都会使捎带ACK的,这样将本来可能须要四次交互减低到只要两次。

3.Nagle算法

前面提到TCP有发送缓冲区,在发送时,应用的数据被内核写入该缓冲区后,TCP再发出去。分组发送时,若是是很是多的微小的分组数据包被不断发出去,则有可能形成网络拥塞,特别是在广域网上。

Nagle算法要求在一个TCP链接上最多只有一个未确认的分组,在该分组的确认包到达以前,发送将不能在发送其余的分组。应用层须要发的数据都被存在缓冲区,待收到确认包后,将缓冲区中的数据一块发送出去。该算法旨在解决大量的微小分组在低速带宽上频繁发送可能形成网络拥塞的问题。

若是更加直接的说,Nagle算法便是将发送的积累沉淀,知道达到必定的条件后再做为大的分组发送,避免大量微小分组形成网络拥塞。这个条件是:

  1. 若是缓冲区中数据大于MSS
  2. 禁用Nagle算法或则TCP链接上没有未确认的分组
  3. 有紧急数据须要发送

知足以上条件之一,将才会发送缓冲区的数据。

4.延迟ACK和Nagle共同做用

Nagle算法旨在解决大量微小分组形成的拥塞问题,可是若是在带宽较大且网络负载不大的局域网上,且应用对延迟很是敏感的时,Nagle算法则不合适。由于应用须要发送的数据被缓冲,未被发出而获得响应,产生延迟。

特别当Nagle算法遇到ACK延迟,二者共同做用时,该状况尤其明显。由于一个TCP链接上只存在一个未确认的分组,且该分组又被ACK延迟。二者共同做用,带来更大的延迟。对于延迟敏感应用则加重这种状况。

对于大多数局域网内的应用交互,能够经过设置TCP链接套接字,禁用Nagle算法。让一个链接上能够存在多个未确认分组,能够连续发屡次分组,从而下降延迟。可是对于在广域网上因为流量较大和RRT较长缘由,禁用Nagle算法不只可能会形成网络拥塞,并且产生的网络延迟可能更严重。

Socket套接字提供应用层Socket参数TCP_NODELAY参数用于开启和关闭Nagle算法。能够经过Java的API了解:

4.ACK的累积

发送端经过Nagle算法累积发送数据,从而避免大量微小分组出如今网络中。从而发送一个较大分组,解决拥塞和必定的延迟问题。接收端若是对接收到的每一个报文段都进行ACK,那么网络中将存在大量的ACK包,从而加剧网络负载。

TCP设计ACK累积策略,该策略并非针对每一个TCP报文都须要进行ACK确认。接收端的TCP建立一个针对发送端IP的队列,接收到的报文都进入该队列。接收到TCP报文段后,启动延迟ACK定时器。在该定时时间段内,仍然会接收发送过来的数据进入队列。在必定条件下,而后回复已经收到的最大的数据SEQ + 1做为ACK回复确认序号。以下通讯过程:

从上图能够看出,当svr和bsdi在创建链接后,svr连续发送了三个TCP报文,可是bsdi并非对每一个报文都进行了ACK,当序号为1的PSH包到达后,bsdi的TCP启动延迟ACK,而后又接收到了1025序号的TCP报文,此时有两个未确认的报文,因此发送了一个2049的ACK。当接收方收到2049的ACK后,即知道前面一个序号为1的报文也已经接收到了,否则接收端是不会回复2049的ACK的。

经过ACK累积,能够减小ACK包,从而提高网络利用率,下降拥塞的可能。

二.超时重传

TCP的可靠性另外一方面的保证机制即超时重传。上节说到经过ACK的方式让发送方知道接收方是否收到了数据。可是数据和ACK包都有可能会丢,即便接收方收到了,可是ACK却丢失。那样对于发送方而言,仍然不知道接受方是否接受到了数据。

TCP协议设计超时重传机制。发送方的TCP在发送TCP报文后,针对该报文启动重传定时器,若是当定时器溢出超时后,仍然没有收到该报文的ACK,则从新发送该报文。而后再启动相应的定时器等待ACK,直到发送成功或者重发必定次数失败后。

如下客户单经过telnet程序演示TCP的超时重传机制的过程。

发送端发送"hello2."时因为网络链接断开,而后重复发送。经过tcpdump能够看出

参考

TCP的交互与成块数据流

相关文章
相关标签/搜索