网络协议:可靠的数据流传输服务TCP

来源:互联网 发布:中世纪2全面战争优化9 编辑:程序博客网 时间:2024/06/08 08:18

本文将讨论:

TCP的基本概念;

数据流的收发;

TCP报文;

建立和关闭连接;

糊涂窗口;

 

现在所用的网络上,我们常用的运输层协议是TCPUDP。现在我们来讨论TCP

 

TCP具有的特征有4个,面向数据流(面向字节)、虚电路连接、传输缓冲和全双工连接。下面来依次细讲。

 

在两个进程相互传输数据的时候,我们实际上是以八位组的比特流来传输的,当然我们同时将这个八位组的比特流称为字节。同时,我们在使用TCP的时候,我们一定需要在两个进程间建立连接,就像两个电话中间连了一条电话线。在使用TCP进行数据传输的时候,应用程序不断的往对方发送八位组,这个八位组有时候是1个,有时候是两个,有时候是别的。每个应用程序都在使用自己认为适宜大小的八位组。接收的时候,TCP通过某种方式来把这些八位组按照发送的顺序组合起来,直接交付给上一层。当发现需要交付的数据不够填满一个缓冲区的时候,只要到达了某个值,TCP就会直接发送,当然这个发送还是遵循着TCP的规则。全双工连接就更容易理解了,对于一个应用程序而言,全双工连接包括了两个独立的、流向相反的数据流,并且这两个数据流之间不进行明显的交互。

 

 

数据流的收发

 

对于TCP,它的可靠性体现在只要接收方收到了一组八位组,就会给发送方返回一个ACK。这种是一种叫“带重传的肯定确认”技术,大部分可靠协议都用它。只有在收到了这个八位组回的ACK才会发下一个。发送方在发送每一个分组的时候都会启动一个定时器,并在定时器超时且没有收到ACK的情况下重发这个八位组。

 

那么,如果每一次都要等待ACK,那么收发效率都会很低很低。那该怎么办呢?有没有这样一种思路,我一次性的发若四组八位组1234,在回1的时候发5,回2的时候发6。如果回了4但是没回3并且过了超时时间,那么我就重新发3并且等待回3,回了3以后我就直接等待回5并且发7。对的TCP使用了这个方法,这种技术叫滑动窗口。可以用下图来理解



 

如果一个分组在发送后没回ACK,这个分组就是未被确认的,当回了ACK,窗口滑动,发送下一个分组。

 

当窗口为1的时候,就是前面提到的:你发一个组,我回一个包的状态。

 

滑动窗口的内部实现其实很有意思,它用了两个指针:头指针和尾指针描述了窗口的左边框和右边框,再在窗口内部设置了一个指针,代表着已经发送的八位组和未被发送的八位组,一开始,第三个指针和第一个指针处于同一个位置,随着八位组的不断发送,第三个指针不断向右偏移。接收方也是一样,需要接收数据,接收的方法也是使用了滑动窗口的方法。由于TCP是全双工的,所以TCP为两段各保留了两个窗口(一共四个),其中一个在发送的时候滑动,另一个在接收的时候滑动。

 

窗口的大小并不是不变的,每个确认里也并不只有ACK,还包含着一个窗口通告来说明接收方还能够接收多少八位组的数据。通过窗口通告,发送方会用来扩大或者减小窗口的大小。如果出现了接收方缓冲区满的情况,会通过窗口通告值来停止所有的传输。在缓冲区空间又可用的时候会通告一个非0的窗口值来再次触发数据流。当然,在窗口大小为0的时候有两种例外的传输,一种是在报文段的紧急位置为1,表明这是紧急数据。另外一种是,为了避免窗口大小到0后,非0通告丢失导致死锁,发送方会定期的对大小为0的窗口发出试探性的报文段。

 

 

TCP报文段

 

两台机器上的TCP软件之间传输的数据单元叫做报文段,通过报文段的交互来建立连接、传输数据、发出确认、通告窗口大小、关闭连接。由于TCP使用了捎带技术,也就是说,接收方给发送方回ACK的同时也可以在这个报文段里放数据。

 

一般来说,我们所说的一个TCP的首部只有20个字节,这20个字节是除开了选项的24位加上填充的8位合4个字节的,具体如图。


源端口和目的端口不提,序号指出了这个报文段在发送方的数据字节流的位置,确认号是接收方希望接收的下一个八位组的序号TCP报文段的长度随着所选择的选项变化,所以存在了首部长度和保留未用这些东西。窗口就是之前所说的滑动窗口,紧急指针前面提了,校验和用来保证对包括TCP首部在内的全部数据完整性的检验,现在重点来到码元比特。

 

码元比特占有6个字节,如下图所示


URG和紧急指针配套使用,ACK代表接收方已经收到了数据给发送方回的ACK,当这个报文段没有放满,而客户又希望立刻得到相应,此时会将PSH位置为1,通过PUSH技术强行发送。SYN用来建立连接,FIN用来关闭连接。

在这里顺带提一下捎带技术。捎带技术是指,接收方在接收到发送方后所回的ACK和接收方需要反传给发送方的东西放在一起。但实际上,大多数应用程序都不会同时在两个方向上传输数据,所以捎带技术用的比较少。我们常见的也就是三次挥手的被动方在一个报文段里将FINACK同时置为1

 

我们前面提到过,TCP是可靠的传输协议,其实,将乱序的报文段整理成有序的也是可靠的 一种体现。那么,TCP是通过什么来实现的呢?

 

前面介绍报文段的时候我们提到了序号,是的,TCP就是通过序号来对收到的八位组重排,接收方对正确收到的数据流中最后收到的八位组确认,每一个确认给出一个序号,序号比最后收到的八位组大1.

 

再来聊聊TCP的定时器。我们知道,在跨网络的两方发送数据完全是不能够知道最佳时延的,TCP为了将整个定时器最优化,会记录下每一次发送时间并且求平均值。也就是说,TCP自身能够动态的改变定时器的时间。

 

拥塞现象

 

拥塞现象是指由于在一个或者若干个网络节点的数据报负载过重导致的时延过长的现象。当产生拥塞现象的时候,路由器会把大量的数据报放在队列中,知道能够路由他们。由于路由器的存储能力有限,在其达到最大值的时候,路由器会丢弃报文。

 

对于用户来说,这种现象将表现为时延增加,速度变慢。当然,如果超过了定时器时间,TCP会添乱似得重传,这会导致整个网络陷入更加糟糕的状况。

 

为了避免上述现象,TCP必须在拥塞发生的时候减少传输,最坏情况下是将传输率置为0

 

现在的TCP使用了两种技术对这个现象进行处理:慢启动和加速递减。这两个是相关的,实现也很容易。对于每个报文段,都有一个储存接收方的窗口大小。在没有拥塞的情况下,这个大小就是发送方的窗口大小。

 

拥塞发生的标志是报文段被丢弃。在这个时候,立即将滑动窗口的大小减半,并且在重传的时候将定时器时间乘2。这个策略的意图是迅速的减少通信量,以便路由器获得足够的时间来清除发送队列里的数据报。

 

在拥塞结束以后,TCP并不会立即恢复原有窗口大小,而是在增加通信量的时候只将窗口大小乘2.来保证互联网在拥塞清除或者新连接建立的时候不会被突然到来的通信重新破坏。

 

建立和关闭连接

 

TCP使用三次握手和四次挥手来建立、关闭连接。建立、关闭过程如图




糊涂窗口

 

 糊涂窗口是由于双方以不同的工作速率工作的时候(假设是接收方慢发送方快),就会导致接收方的缓冲区被轻易的填满,发送方的窗口大小为0,接收方处理一个发送方发送一个。这样将进程间传送的有效载荷被严重降低的行为叫做糊涂窗口

 

解决糊涂窗口有很多种方法:延迟发送、Nagle算法、Cork算法等等。这里选择延迟发送、Nagle进行讲解。

 

延迟发送包含两种:一种是TCP对收到的报文段确认,但是等到窗口空间达到启发式策略所限定的限度之后才会发送。第二是在窗口大小不足以避免糊涂窗口的限度时,延迟发送。TCP标准推荐第二种。

 

延迟发送存在优点也存在缺点。优点有很多,如果在延迟确认的时候收到了更多的数据,回一个确认就好了,除此之外还能够应用捎带技术顺一些东西回来。此外,由于TCP只有在接收应用程序从缓冲区中读取数据之后才能移动窗口,所以短暂的延迟还能够再放一个窗口通告,这样就不需要再多发送一个窗口通告了。

 

延迟发送的缺点也很明显,如果接收方的确认迟延太大的时候,发送方会重传报文。此外,推迟确认有可能会造成确认值的混乱,导致增大了定时器重传时间。所以,TCP在实现的时候延迟发送的最大时间为500毫秒。

 

Nagle算法

 

为了尽可能的利用网络带宽,TCP总是尽可能的发送足够大的数据,所以Nagle算法就是尽可能的发送大块数据,避免网络中存在着大量小数据块。

 

Nagle算法认为,在任意时刻,最多存在着一个未被确认的小段。所谓小段,是指的小于NSS尺寸的数据块,未被确认指的是在这个数据块发送出去后没有收到ACK

1)如果包长度达到MSS,则允许发送;

2)如果该包含有FIN,则允许发送;

3)设置了TCP_NODELAY选项,则允许发送;

4)未设置TCP_CORK选项时,若所有发出去的小数据包(包长度小于MSS)均被确认,则允许发送; 

5)上述条件都未满足,但发生了超时(一般为200ms),则立即发送。

 

 

 



原创粉丝点击