众所周知,TCP/IP协议栈是事实的网络通信协议,而在保证可靠的网络通信上理解TCP至关重要,本文尝试从工程的视角分析为了保障可靠传输,可能会面对那些问题即对应策略,以及在保证可靠传输时同时提高传输效率,最后给出实际TCP的处理策略。
1.可靠传输思考
1.1.为什么需要做传输控制
传输控制是为了保证可靠传输,想象互联网是一个异构、多跳级联通信信道,通信信息是一个有损信道,即信息可能会删除或改变,通信链路的所有环节都可能导致传输数据出错。多跳级联通信信道可能出现的问题远比想象复杂,除了传输出错,还会遇到分组重新排序、分组复制、分组丢失问题。
若没有TCP协议,IP层数据链路层所有的协议都没有说如何可靠的传递数据。它们可能会用校验和、CRC这种的数据函数还检查收到的数据是否有问题,但很可惜,它们都没有纠正错误,如果发现错误,其他协议(传输控制层及其以下除了TCP以外的其他协议)可能会要求重试,如果重试一定次数还是失败,则放弃。
1.2.纠错手段思考
在讨论传输问题和传输控制前,我们有必要了解一下已有的2种纠错手段。
信息理论的鼻祖香农告诉我们在有损信道里可通过的信息量的根本限制。编码理论和信息理论密切相关,编码理论提供不同的编码手段使得信息能在通信信道里尽可能免于出错,使用差错纠正码,通过添加冗余的比特,使得即使某些比特被损坏也能恢复过来。纠错码是处理差错的一种非常重要方法。
另一种方式是上面提到过的重试大法,尝试重新发送,直到信息最终被接收,这种方式有个专业术语,称为ARQ(Automatic Repeat Request,自动重复请求),ARQ是构成许多协议的基础,也包括TCP。
2.传输问题和对应策略
2.1.确认机制与重传
重传可以有效处理分组丢失问题,首先,发送方需要知道是否需要重传,方案是需要接收方给予反馈。接收方需反馈给发送方自己是否已成功接收到正确的信息,这种方式成为确认(acknowledgment,ACK),接收方是否需要反馈分两种情况:
- 接收方是否已收到分组。
- 接收方收到的分组是否与之前发送方发来的一致。
描述一下确认场景:发送方发送一个分组,然后开始等待ACK;当接收方成功收到这个分组时,发送ACK;发送方收到ACK再发送下一个分组,然后一直持续下去。这里抛出3个问题:
- 发送方对一个ACK应该等待多久时间,也就是超时时间设置多少比较合适?
- 如果ACK丢失了该怎么办?
- 如果分组被接收者接收,但发现分组是错的,这个时候该怎么办?
第一个问题其实还挺难一下给出方案的,咱们留到后文回答。
第二个问题,由于发送方无法区分是分组丢了,还是ACK丢了,所以只有重传分组。当然重传后又会有新问题需要解决:接收方可能会收到两个或多个拷贝,因此他必须区分哪些分组是重复分组,如何处理重复分组我们在下一节讨论。
第三个问题,可以通过编码技术检测,当检测出错时,发送方重发分组。
使用编码来检测一个大的分组中的差错一般都很简单,仅仅使用一些比特就能纠错。当然编码技术也有简单复杂之分,检验和和CRC受欢迎的原因是因为编码简单、成本低(简单编码只能用于检测而无法纠错)。
2.2.重复信息处理
上面讨论的2个问题的解决方案都可能造成接收方接收到重复(duplicate)分组,这个问题可通过序列号(sequence number)来处理。每个分组都携带了序列号,当接收方收到分组可依据序列号判断是否接收过这个分组,如果接收过则丢弃。
本节总结下,为了保证可靠传输给的药方是信息确认和重传,出现重复信息可通过序列号来做过滤。
3.如何提升传输效率
上节讨论了如何实现可靠传输,但有没有想过,这样做虽然可以保证可靠传输,但由于是顺序传输,效率其实是很低的:发送方注入一个分组到通信路径,然后开始阻塞等待直到收到ack才去发送下一个分组或者等待超时后发起重试,这种操作有一个专有名词叫“停止和等待”。这种串行操作是相当低效的,这种协议的吞吐量很低(吞吐量与分组大小成正比,与往返时延成反比)。
通过提供并发度可以解决低效问题:允许同一时间由更多的分组被发送端注入,让网络忙起来,从而提高吞吐量。但很明显,提高并发度会使问题更复杂,必须面对以下问题:
- 发送方需要考虑在哪个时间点注入分组到网络、注入多少分组?
- 等待ACK时计时器如何维持,是每个分组一个计时,还是其他方式?
- 如果需要重传肯定需要保存未确认分组副本,哪些是未确认副本,为此可能需要一个发送方缓存机制。
- 接收方需要一个更复杂的ACK机制:可以区分哪些分组已经收到,哪些还没收到,为此接收方可能需要一个更复杂的缓存机制用于保存乱序到达的分组。如果对于没按顺序到达的分组简单丢弃,然后要求发送方重传这样做很没效率,所以使用缓存机制是高效的。
- 如果接收方接收速率比发送方要慢怎么办?比如发送方以很高的速率发送,接收方由于需要处理或出现内存限制等问题而丢掉分组,通信链路中间的路由器也可能出现这种收发速率不一致问题。
3.1.滑动窗口
为了解决以上问题,我们有必要引入滑动窗口。
正如前面描述的,我们定义一个分组窗口(window)作为已被发送方注入但还没完成确认的分组集合,我们把这个分组数量称为窗口大小(window size)。你可以想象把通信对话中发送的所有分组排成长长的一行,但只能通过窗口看到已发送待确认的分组,如下图所示:
如图所示,窗口大小为3。3号分组已经被发送和确认,因此,由发送方保存的3号副本可以释放。7号分组是发送方已准备好待发送分组,由于滑动窗口大小是3,而当前窗口内有3个未确认分组,因此7号分组还无法进入窗口内。现在想象一下接口方继续收到发送方发来的分组4,接收方确认分组正确后回复确认,发送端接收到ack后4号分组状态变成已确认发送分组可以被释放了,窗口将右移,7号分组将注入到网络中并进入窗口内。窗口的这种滑动给这种类型的协议增加了一个名字:滑动窗口协议。
滑动窗口在发送和接收两端同时存在,在发送端它记录着:哪些分组可被释放,哪些分组正在等待ack,哪些分组暂时还不能发送。在接收端,它记录着哪些分组可以被接收和确认,哪些分组是一步期望的,哪些分组即使被接收也将会因内存限制而被丢弃。
滑动窗口既可以解决分组发送量问题,也可以控制流量(在接下来马上介绍)。
这里抛一个问题,窗口应该设置多大比较合理,是不是越大越好?如果接收方处理不过来发送方数据怎么办。
3.2.流量控制和拥塞控制
流量控制
前面讨论了通过编码技术可以检验数据正确性,通过重试尽最大努力将分组发出去,通过确认机制保证可靠传输,当收发两端速率不一致时还需要做发送端流控。当链路阻塞导致丢包率增大时,需要做拥塞控制。
流量控制是这么定义的:当接收方跟不上时强迫发送方慢下来的控制方式叫流量控制(flow control)。常见的流量控制有2种方式:
- 基于速率(rate-based)流量控制,它是给发送方指定某个速率,确保数据永远不能超过这个速率发送。这种方式适合流应用程序。
- 基于窗口(window-based)流量控制,和刚才讲解的滑动窗口模型不同的是窗口大小不是固定的,而是允许随时间变动。
我们本次要讨论的是基于窗口的流控,回到上节提出的问题,当收发两端速率不批量时,接收方可以通知发送方调整窗口的大小,称为窗口通告(window advertisement)或者说窗口更新。发送方收到该值调整窗口大小。窗口通知分组可以和ack公用一个分组,可以节约带宽资源,也就是说发送方在窗口滑动时同时调整窗口大小。
那具体是如何通过控制滑动窗口大小控制发送流量呢?
首先,一开始假设创建大小是W,也就是说允许W个分组同时发送,通信速率正比于 SW/R b/s,W是窗口大小,S是分组大小,R是往返时延。根据这个线性公式,很容易看出在分组大小和往返时间一定情况下,通过控制滑动窗口基本就控制了发送端通信速率。
以上就是滑动窗口调整如何影响发送速率进而做到流量控制的原理。
拥塞控制
除了收发速率不一致需要调整窗口外,在通信链路中存在内存有限的路由器,他们与低俗网络链路抗争着,此时发送速率仍可能超过某个路由器的能力从而导致丢包,这需要通过拥塞控制(一种特殊的流量控制)来处理。
拥塞控制涉及发送方减低速度以不至于压垮网络。如果说接收方告诉发送方调整滑动窗口大小这种方式叫明确(explict)发信,那么发送方用于猜测(guess)它需要慢下来,这种方法叫隐性(implicit)发信,涉及根据其他某些证据来决定减慢速度。
拥塞控制技术仍在不断发展,完全解决拥塞的所有问题几乎是不可能,但仍然有成熟的解决方案能较好的处理拥塞问题,我们在以后的文章中重点分析。
3.3.设置超时重传时间
在2.2节我们提了一个问题ack等待时间设置多长比较合适,如果等待太久,发送端阻塞时间太长通信链路利用率不高,这节咱们来讨论一下。
发送方等待时间一般由几部分构成:发送分组所用时间、接收方处理时间、发送ACK和传输所用时间。不幸的是这几个时间没有一个确切知道的,且时间还会随着主机或路由器的负载增加或减少而改变,这就给设置超时时间带来了困难。
人工去配置超时时间基本是不现实的,更好的办法是采用一种自适应算法,让协议尝试去估计耗时,术语叫“往返时间估计”(round-trip-time estimation)。访问时间估计基于统计过程,总的来说是:选择一组RTT样本的样本均值作为真实的RTT,这种样本均值会随着时间改变,不断采样调整就能估算出合理的超时时间。
很显然,超时时间应该设置成比样本均值大的某个值,具体多大呢?设置比样本均值大太多同样会造成网络空闲,从而降低吞吐量,这个话题也是值得继续深入讨论的,但不在咱们今天的讨论范围。
总结
本文尝试缕清可靠传输的一般思路,首先认识到异构网络数据传输的不可靠,为了发现和纠正分组错误需要使用到编码技术、当发现分组丢失或分区数据不一致时,需要对分组进行重传,但重传可能导致接收端收到分组副本,为此引入了序列号用于区分分组。实现可靠传输需要用到确认机制,采用停止等待确认可能造成网络吞吐量很小,为此采用并发注入分组到网络的方式来提高网络的吞吐量。引入并行发送分组可能造成接收端处理不过来的情况出现,为此需要引入流量控制,TCP采用的是滑动窗口流控,除了滑动窗口显示流控外,还有拥塞控制这种隐性流控,通过丢包率估计这时是否需要降低还是增加发送速率。对于发送端超时时间设置需要依靠过去的统计过程进行预测。
The end.
转载请注明来源,否则严禁转载。原文链接