TCP交互数据流及成块数据流

引言

建立在TCP协议上的应用层协议有非常多,如FTP、HTTP、Telnet等。这些协议依据数据传输的多少能够分为两类:交互数据类型和成块数据类型。

  • 交互数据类型,如:Telnet。这类协议一般仅仅做小流量的数据交换,比方每按下一个键,要回显一些字符。
  • 成块数据类型,如:FTP,这类协议须要传输的数据比较多。一般传输的数据量比较大。

针对这两种不同的情况,TCP采用不同的策略进行数据传输

交互式数据

TCP交互数据流及成块数据流
上图是交互式输入的过程,假如每次发送一字节,上述过程如下:1、来自客户的交互按键;2、来自服务器的按键确认;3、来自服务器的按键回显;4、来自客户的按键回显确认。也就是自己输入发送到服务器,服务器确认,然后将键入的字符回显,最后客户端确定。假设在局域网中。通常不会有什么麻烦,由于局域网一般不会出现拥塞。但在广域网中,这些小分组则会添加网络拥塞出现的可能。为了提高这类数据的发送效率和降低网络负担,我们一般可以将报文段2和3进行合并,将按键确认与按键回显一起发送,这种技术就叫做经受时延的确认(使用了Nagle算法)。

Nagle算法介绍
该算法的重点是要求在TCP连接上最多只能有一个未被确认的未完成小分组,在该分组到达之前不能发送其他小分组,这给延迟确认提供了思路。对于接收方,接收到了小分组之后延迟确认;那么对于发送方,因为延迟被确认了,后面要发送的数据将被逐个字节地拷贝到TCP的发送缓存,前面分组确认到了以后,就可以将这些缓冲起来的数据组装成一个稍大的报文段一起发送出去。同一时候继续对随后到来的数据进行缓存。仅仅有收到前一个报文段的确认后才干继续发送下一个报文段。当数据到达较快而网络速率较慢时,用这样的方法可明显地降低所用的网络带宽。在较慢的广域网种,通常使用Nagle算法来减少小报文段的数目。

关闭Nagle算法
有时候也需要关闭Nagle算法,为了给某种操作提供实时反馈,需要关闭Nagle算法。

成块数据流

对于一些数据吞吐量要求较高的应用,总是希望每次发送尽可能多的数据到主机,对于这类应用。TCP使用滑动窗体协议,该协议允许发送方在停止并等待确认前能够连续发送多个分组,由于发送方必须每发一个分组就不必停下来等待确认,因此能够加速数据的传输,并且控制流量的作用

滑动窗口

IP层协议属于不可靠的协议,IP层并不关心数据是否发送到了对端,TCP通过确认机制来保证数据传输的可靠性,在比较早的时候使用的是send–wait–send的模式,其实这种模式叫做stop-wait模式,发送数据方在发送数据之后会启动定时器,如果数据或者ACK丢失,那么定时器到期之后,收不到ACK就认为发送出现状况,要进行重传,这样就会降低了通信的效率。
为了优化上述问题,比如我让发送的每一个包都有一个ID,接收端必须对每一个包进行确认,这样设备A一次多发送几个片段,而不必等候ACK,同时接收端也要告知它能够收多少,这样发送端发起来也有个限制,当然还需要保证顺序性,不要乱序,对于乱序的状况,我们可以允许等待一定情况下的乱序,比如说先缓存提前到的数据,然后去等待需要的数据,如果一定时间没来就丢弃掉,来保证顺序性。在TCP/IP协议栈中,滑动窗口的引入可以解决此问题。
发送方发送窗口和接收端接收窗口一一对应,告诉接收端接收区缓存是多少。
理解前提
1、“窗口”对应的是一段可以被发送者发送的字节序列,其连续的范围称之为“窗口”。
2、 “滑动”则是指这段“允许发送的范围”是可以随着发送的过程而变化的,方式就是按顺序“滑动”。在引入一个例子来说这个协议之前,我觉得很有必要先了解以下前提:

  • TCP协议的两端分别为发送者A和接收者B,由于是全双工协议,因此A和B应该分别维护着一个独立的发送缓冲区和接收缓冲区,由于对等性(A发B收和B发A收),我们以A发送B接收的情况作为例子;
  • 发送窗口是发送缓存中的一部分,是可以被TCP协议发送的那部分,其实应用层需要发送的所有数据都被放进了发送者的发送缓冲区;
  • 发送窗口中相关的有四个概念:已发送并收到确认的数据(不再发送窗口和发送缓冲区之内)、已发送但未收到确认的数据(位于发送窗口之中)、允许发送但尚未发送的数据以及发送窗口外发送缓冲区内暂时不允许发送的数据;
  • 每次成功发送数据(发送且收到了ACK)之后,发送窗口就会在发送缓冲区中按顺序移动,将新的数据包含到窗口中准备发送;
  • TCP建立连接的初始,B会告诉A自己的接收窗口大小,比如为‘20’,如下图所示。

发送方滑动窗口
TCP交互数据流及成块数据流
1. Sent and Acknowledged:这些数据表示已经发送成功并已经被确认的数据,比如图中的前31个bytes,这些数据其实的位置是在窗口之外了,因为窗口内顺序最低的被确认之后,要移除窗口,实际上是窗口进行合拢,同时打开接收新的带发送的数据
2. Send But Not Yet Acknowledged:这部分数据称为发送但没有被确认,数据被发送出去,没有收到接收端的ACK,认为并没有完成发送,这个属于窗口内的数据。
3. Not Sent,Recipient Ready to Receive:这部分是尽快发送的数据,这部分数据已经被加载到缓存中,也就是窗口中了,等待发送,其实这个窗口是完全有接收方告知的,接收方告知还是能够接受这些包,所以发送方需要尽快的发送这些包
4. Not Sent,Recipient Not Ready to Receive: 这些数据属于未发送,同时接收端也不允许发送的,因为这些数据已经超出了发送端所接收的范围。

TCP交互数据流及成块数据流
在这个图中,我们将字节从1至11进行标号。接收方通告的窗口称为提出的窗口,它覆盖了从第4字节到第9字节的区域,表明接收方已经确认了包括第3字节在内的数据,且通告窗口大小为6,我们知道窗口大小是与确认序号相对应的。发送方计算它的可用窗口,该窗口表明多少数据可以立即被发送。当接收方确认数据(给发送方发了ACK)后,这个滑动窗口不时地向右移动。窗口两个边沿的相对运动增加或减少了窗口的大小,我们使用三个术语来描述窗口左右边沿的运动:
(1)窗口左边沿向右边沿靠近为窗口合拢,这种现象发生在数据被发送和确认时。
(2)窗口右边沿向右边移动为窗口张开,这种现象发生在另一端的接收进程读取已经确认的数据并释放了TCP的接收缓存时。
(3)窗口右边沿向左边移动为窗口收缩。RFC强烈建议不要使用这种方式
如果左边沿到达右边沿,则称其为一个0窗口,此时发送方不能够发送任何数据。

举个例子
TCP并不是每一个报文段都会回复ACK的,可能会对两个报文段发送一个ACK,也可能会对多个报文段发送1个ACK(累计ACK),比如说发送方有1 / 2 / 3,3个报文段,先发送了2,3 两个报文段,但是接收方期望收到1报文段,这个时候2,3报文段就只能放在缓存中等待报文1的空洞被填上,如果报文1,一直不来,报文2 ,3也将被丢弃,如果报文1来了,那么会发送一个ACK对这3个报文进行一次确认。

TCP交互数据流及成块数据流
1. 假设32~45这些数据,是上层Application发送给TCP的,TCP将其分成四个Segment来发往internet。
2. seg1 32~34,seg2 35~36,seg3 37~41,seg4 42~45 这四个片段,依次发送出去,此时假设接收端之接收到了seg1 seg2 seg4
3. 此时接收端的行为是回复一个ACK包说明已经接收到了32~36的数据,并将seg4进行缓存(保证顺序,产生一个保存seg3的hole)。
4. 发送端收到ACK之后,就会将32~36的数据包从发送并没有确认切到发送已经确认,提出窗口,这个时候窗口向右移动。
5. 假设接收端通告的Window Size仍然不变,此时窗口右移,产生一些新的空位,这些是接收端允许发送的范畴
6. 对于丢失的seg3,如果超过一定时间,TCP就会重新传送(重传机制),重传成功会seg3 seg4一块被确认,不成功,seg4也将被丢弃

就是不断重复着上述的过程,随着窗口不断滑动,将真个数据流发送到接收端,实际上接收端的Window Size通告也是会变化的,接收端根据这个值来确定何时及发送多少数据,从对数据流进行流控。原理图如下图所示:

滑动窗口大小是动态调整
主要是根据接收端的接收情况,动态去调整Window Size,然后来控制发送端的数据流量,总结一点,就是接收端可以根据自己的状况通告窗口大小,从而控制发送端的接收,进行流量控制。
TCP交互数据流及成块数据流
首先在握手阶段,确定了双方的窗口大小。其中发送方的窗口大小是依据接收端窗口大小确定的。所以握手就把双方的接收和发送窗口全部确定了下来。
例如上述:
左边接收窗口是4096 、发送窗口初始化6144。
右边接收窗口是6144 、发送窗口初始化4096。

首先注意到的是在报文段2中提供的窗口大小为6144字节。由于这是一个较大的窗口,因此客户立即连续发送了6个报文段(4~9),然后停止(因为服务器缓冲区仅仅6144,暂时还没有确认)。报文段10确认了所有的数据(从第1到6144字节),但提供的窗口大小却为2048,这很可能是接收程序没有机会读取多于2048字节的
数据(此时发送端窗口左边界快速合拢到6145处,右边界扩张到6144+2048)。报文段11和12完成了客户的数据传输,且最后一个报文段带有FIN标志。
报文段13包含与报文段10相同的确认序号,但通告了一个更大的窗口大小。报文段 14确认了最后的2048字节的数据和FIN,报文段15和16仅用于通告一个更大的窗口大小。报文段17和18完成通常的关闭过程。

PUSH标志

发送方使用该标志通知接收方将所收到的数据全部提交给接收进程(赶紧将接收缓存区的数据读出去,不要让它占领了缓冲区)。这里的数据包括与PUSH一起传送的数据以及接收方TCP已经为接收进程收到的其他数据。在一个交互程序中,当客户发送一个命令给服务器时,它设置PUSH标志并停下来等待服务器的响应;通过允许客户应用程序通知其TCP设置PUSH标志,客户进程通知TCP在向服务器发送一个报文段时不要因等待额外数据而使已提交数据在缓存中滞留。类似地,当服务器的TCP接收到一个设置了PUSH标志的报文段时,它需要立即将这些数据递交给服务器进程而不能等待判断是否还会有额外的数据到达。

慢启动

在后面一篇博客仔细讲解慢启动算法。在TCP刚刚开始连接的时候,发送方通过慢启动发送数据报。