我们一直在讲TCP是有连接,可靠的,面向字节流的传输协议,那么TCP是如何实现其可靠性呢?主要从下列三个方面来保证TCP可靠性:
无差错到达
,滑动窗口保证发送方发送数据和接收方速率匹配
,拥塞控制保证发送发发送数据的速率与当前网络情况相匹配
来保证。当我们的网络不具备理想的传输条件时不能采取任何的措施来实现可靠传输,由此我们就要使用一些靠传输协议
。
当出现差错时让发送方重传出现差错的数据,同时在接收方来不及处理收到的数据时,及时告诉发送方适当降低发送数据的数据,即实现TCP传输时的应答确认、超时重传,主要有两种协议:停止等待协议和连续ARQ协议。
1、概念
从字面意思上我们就可以很好的理解,就是每发送完一个分组就停止发送,等待对方的确认。在收到确认后再发送下一个分组。
在全双工通信的双方既是发送方也是接收方,我们现在假定A发送数据,B接收数据,因此A叫做发送方,B叫做接收方,传送给的数据单元都成为分组。
在使用停止等待协议时,A,B端会有以下几种情况出现:无差错情况,出现差错,确认丢失,确认迟到
。下面我们来一一讲述。
2、不同情况下的停止等待协议
2.1无差错的情况
从下图中,我们可以看到A发送一个分组过后要等待B回复确认过后才能发送下一个分组。
2.2出现差错的情况
出现差错的情况一般有两种:
这时B就会自动丢弃有差错的分组,并不会去通知A,A只要超过一段时间仍然没有收到确认,就认为刚才发送的分组丢失了,就重传前面发送的分组,这也就是我们之前所说的超时重传机制
,如下图所示:
【补充】超时重传机制
【注意】
2.3确认丢失
当B所发送的对M1确认丢失了导致A在设定的超时重传时间内没有收到确认。A会在超时计时器到期后重传M2。这时B又收到了重传的分组M2,这时会采取两个行动:
具体的如下图所示:
2.4确认迟到
当传输过程没有出现差错,但B对分组M1的确认迟到了,A会收到重复的确认,对重复确认的处理就是收下后丢弃。B仍然会收到重复的M1,并同样要丢弃重复的M1,并重传确认分组,如下图所示:
3、总结
1、概念
接收方一般都是采用累积确认的方式,对按序到达的最后一个分组发送确认。
关键点就在于如下图的窗口内5个分组都可以连续发送出去,而不需要等待对方的确认。
发送方每收到一个确认,就把发送窗口向前滑动一个分组的位置。 如下图表示发送方收到了对第一个分组的确认,于是就把发送窗口向前移动一个分组的位置,如果原来已经发送了前5个分组,那么现在就可以发送窗口内的第6个分组了。
2、特点
【举个栗子】如果发送方发送了前5个分组,而中间的第3个分组丢失了,这时接受方只能对前两个分组发出确认。发送方无法知道后面三个分组的下落,而只好把后面的三个分组都再重传一次,这就叫做Go-back-N(回退N)
,表示需要再退回来重传已发送地N个分组。可见当通信线路质量不好时,连续ARQ协议会带来负面的影响。
1、什么是滑动窗口?
在我们的确认应答机制当中,对每一个发送的数据段,都要给一个ACK确认应答,收到ACK后再发送下一个数据段。
但是仔细想想这种机制的话似乎性能比较差,尤其就是数据往返时间较长的时候。由此,我们就提出了滑动窗口来一次发送多个数据。
2、滑动窗口的作用?
主要是用来流量控制的。所谓流量控制就是让发送方的发送速率不要太快,让接收方来得及接收。
【举个栗子】
假如A向B发送数据,在连接建立时,B通过报文段首部告诉A:“我的接收窗口rwnd=400”,因此,发送方即A的发送窗口不能超过接收方给出的接收窗口的数值。这里的TCP窗口单位是字节,不是报文段。设置一个报文段DATA为100字节长,数据报文段seq的初始值设为1,ack表示确认字段的值,那么流量控制如下图:
在整个过程中,接受方的主机B进行了 三次流量控制:
3、窗口类型
接收端窗口rwnd
:接收端缓冲区大小。接收端将此窗口值放在TCP报文的首部中的窗口字段,传送给发送端。拥塞窗口cwnd
:发送端缓冲区大小发送窗口swnd
:发送窗口的上限值= Min [rwnd, cwnd]。4、运行机制
如下图所示,假设窗口大小是4000.那么就是说图上10001-5001之间的数据是可以一次性全部发送,并且不需要等到ACK响应的。当发送完这4000的数据之后,此时等待。等到ACK响应中确认收到10001-2000的数据时,窗口右移。
从图中可以看到,发送窗口的位置由窗口的前沿和后验位置共同确定。对于已经收到ACK应答的数据显然是不需要保留的。后面未发送的数据没有为这部分保留临时存放的缓存空间。
1、概念
从字面意思我们可以非常容易的来理解零窗口问题就是发送端在窗口变成0,这样发送端就不会发送数据了。
但是此时当接收端B接收缓存中有了一些存储空间时,B向A发送了rwnd=400的报文段,但是这个报文段在传送过程中丢失了,A一直等待收到B发送的非零窗口通知,而B也一直等待A发送的数据,如果没有其他措施,这种相互等待的死锁局面将一直延续下去,这就是零窗口问题。
2、解决办法
为了解决这个问题,TCP为每一个连接设有 一个持续计时器
。
零窗口探测报文段
(仅携带1字节的数据),而对方就在确认这个探测报文段时给出了现在的窗口值。1、概念
设想一种情况:TCP接收方的缓存已满,而交互式的应用进程一次只从接收缓存中读取1个字节,这样就使得接收缓冲区仅腾出1个字节:
这样循环进行下去,使网络的效率很低,这就是我们说的糊涂窗口综合征,又称为小窗口问题,总结来说就是每次发送很小的字节,却需要40字节的固定头部,数据报固定开销远远大于数据部分,这样会导致网路效率低
。
2、弊端和解决方法
仔细想想,这样的处理方式似乎有些许不足。要知道TCP+IP头有40个字节,为了这几个字节,要搭上这么大的开销,显然不是很经济的做法。所以我们提出了以下的方式来解决。
发送方
:发送方也不要发送太小的报文段,否则会引起接收端的多次确认,浪费内存,而是把数据报积累成足够大的报文段,或达到接收方缓存的空间的一般大小,这就是Nagle算法
。接收端
:可以 让接受方等待一段时间,使得接收缓存已有足够空间容纳一个最长的报文段,或者 等到接收缓存已有一半空闲的空间。只要出现 这两种情况之一,接收方就发出确认报文,并向发送方通知当前窗口大小。这两种办法可配合使用,使得在发送方不发送很小的报文段的同时,接受方也不要在缓存刚刚有了一点小的空间就急忙把这个很小的窗口大小信息通知给发送方。
1、使用场景
该算法是默认打开的,如果需要关闭可以在socket设置TCP_NODELAY选项来关闭。
一般用于需要小包场景的程序——比如像telnet或者ssh这样交互性比较强的程序。解决糊涂窗口综合征。
2、算法详情
Nagle算法还规定
, 当到达的数据已达到发送窗口大小的一半或已达到报文段的最大程度时,就立即发送一个报文段。
1、什么是拥塞控制?
简单来说,拥塞控制就是考虑到当前的网络情况,动态调整窗口大小,没有发送拥塞的情况,则窗口增大,拥塞了就窗口减小。如此往复,最终应该接近与接收端窗口大小。
2、窗口类型
接收端窗口rwnd
:接收缓冲区的大小,接收端将此窗口值放在TCP报文的首部中的窗口字段,传送给发送端。存放按序到达的,但尚未被接收应用程序读取的数据和未按序到达的数据。拥塞窗口cwnd
:大小取决于网络的拥塞程度,并且在动态的变化。发送窗口swnd
:发送窗口的上限值=Min=[rwnd,cwnd],存放已发送未确认的数据和可发送但还没发送的数据即,当rwnd
3、拥塞控制和流量控制的区别?
在讨论这几种拥塞控制的方法之前我们假定:数据是单方向传送,而另一方向只传送确认;接收方总是有足够大的缓存空间,因而发送窗口的大小由网络的拥塞程度来决定。
下图表示了拥塞动态控制的一个过程
发送方维持一个叫做拥塞窗口cwnd的状态变量。拥塞窗口的大小取决于网络的拥塞程度,并且动态的在变化。发送方可以根据没有按时收到应当到达的确认报文就可以猜想网络可能出现了拥塞。
1、慢启动
从上图我们可以清楚的看到,在慢启动阶段是由小到大逐渐增大发送窗口。
在刚开始发送数据的时候,因为不知道当前的网络环境是怎样的,为了避免大量数据一次性发送而造成的拥塞想想。先设置cwnd=1,使得发送方在开始时只发送一个报文段(目的是探测一下网络的拥塞情况),然后再逐渐成指数性增大cwnd。
2、拥塞避免
当达到门限值过后,以线性递增的方式增大拥塞窗口(这里的递增时间间隔为一个往返时间RTT)
【注意】慢启动和拥塞避免在传输开始时配合使用,为了防止拥塞窗口cwnd增长过快引起网络阻塞,还需要设置一个 慢开始门限ssthresh,用法如下:
当cwnd,使用慢开始算法
当cwnd>ssthresh时
,停止使用慢开始算法,改用拥塞避免算法。当cwnd=ssthresh时
,即可以使用慢开始算法,也可以使用拥塞避免算法。3、快速重传
4、快速恢复
【思考?拥塞窗口大小为什么先以指数增加再以线性增加?】
窗口大小首先以指数递增去探测一下网络的拥塞程序,执行拥塞避免算法后,拥塞窗口线性增大,防止网络过早出现拥塞。
5、拥塞控制的作用?
下面这张图就能清楚的说明其作用。
从上图中我们可以明确的看到