12 TCP:传输控制协议(初步)
12.1 引言
以太网上的很多协议自身不包含可靠传输机制,他们可能会使用一种类似于校验和或者CRC这样的数学函数来检测接收到的数据是否包含差错,但是他们不会去尝试纠正差错。尤其对于IP协议和UDP协议,根本没有实现差错纠正功能。虽然一些基于IP协议或者UDP协议的上层协议会提供一定次数的重传机制,但是如果不成功依然会放弃重传。
通讯媒介可能会丢失或者改变被传递的信息。目前有两种主要的手段来尽量避免信息在传输过程中的出错:
-
差错校验码: (基本上是添加一些荣誉的比特,使得即使某些比特被修改,真实的信息也能被恢复出来),使用差错校正码来纠正通讯问题是除了差错的一种非常重要的方法。
-
ARQ: (Automatic Repeat Request), 这种方法简称自动重复请求,它构成了很多协议的基础,其中便包括TCP协议。
12.1.1 ARQ和重传
在讨论IP数据包时经常遇到的几个问题:分组重新排序、分组复制、分组丢失。一个直接处理分组丢失的方法是重新发送分组知道它被正确的接收。
这需要一种方法来判断:
1) 接收方是否受到分组 ?
2) 接收方接收到的分组是否和发送方发的数据完全一样 ?
接收到通过给发送方发送信号来确定自己已经收到一个分组,这种方法称为 确认(acknowledagment)或者ACK。
最基本的形式应该是:
i. 发送方发送一个分组,然后等待一个ACK。
ii. 当接收到收到这个分组后,应该发送对应的ACK;
iii. 发送方接收到这个ACK, 他再发送另外一个分组报文。之后一直重复这样的流程
但是这里有引入另外的问题:
(1) 发送方对一个分组的ACK应该等待多长时间 ?
(2) 如果ACK报文丢失了怎么办 ?
(3) 如果分组被接收到,但是里面包含错误信息怎么办 ?
答案是:
(1) 第一个问题比较深奥。决定等待多长时间与发送发期待(expect)为一个ack等待的时间有关。后续讨论
(2) 第二个问题比较容易: 如果一个ACK丢失了,发送方不能轻易的将这种情况与原分组丢失的情况区分开,因此它简单的再次发送原分组
即可。 这样的话,接收方可能会接收到多个重复的副本,因此接受方必须处理这种情况。
(3) 第三个问题:我们使用校验和的形式来检测分组中是否包含差错,如果包含差错,则不回复相应的ACK报文,这样发送方会重新发送该分组。
当目前为止,即使最简单的应用场景也会遇到一个问题:接收方可能会接收到多个重复的副本。
解决这个问题需要使用序列号(sequence number)来处理。接收方通过报文分组中的序列号来判断当前的报文是否已经接收到。如果是,则将该分组丢弃。
到目前为止介绍的协议虽然是可靠的,但是效率低,吞吐量小。因为每次发送一个分组都需要**“停止和等待”**。对于不会损坏和丢失太多分组的网络,低吞吐量的原因在于网络经常处于闲暇的状态-----如果我们允许多个分组同时进入网络,就可以使它“更繁忙”,从而提高网络吞吐量。
很明显,允许多个分组同时进入网络中,会是事情变得更加复杂:
- 发送方
(1)我们不仅要决定什么时间将分组注入网络,还要决定注入多少个分组;
(2)并且必须指出等待ACK时,怎样维持计时器;
(3)同时还需要保存每一个尚未确认分组的副本以防重传。
- 接收方
(1)接收方需要更为复杂的ACK确认机制:可以区分哪些分组已经接收到,哪些分组尚未接收;
(2)此外接收方还需要更复杂的缓存机制:允许维护“次序杂乱”的分组
- 其他
(1)接收方和发送方速率不匹配该怎么处理 ?,例如发送的快,接收速率慢。
12.1.2 分组窗口和滑动窗口
为了解决所有这些问题,我们假设每一个分组有一个序列号开始。我们定义一个分组窗口(window)作为已被发送方发送但是尚未完成确认的分组集合,我们把这个窗口中的分组数量称为窗口大小(window size)。
上图显示了当前三个分组的窗口,整个窗口大小为3。
1) 3号分组已经被发送并且收到对方的ACK确认报文,因此发送方保存的副本可以被释放。
2) 7号分组已经准备好,但尚未被发送。因为它尚未进入“窗口”中。
现在我们想象数据从发送方流向接收方,ACK从相反的方向流动,发送方可能下一步接收到分组4的ACK报文。当这种情况发生时,窗口向右边滑动一个分组,分组4的副本可以被释放,而分组7可以被发送。窗口的这种滑动给这种类型的协议增加了一个名字:“滑动窗口”(sliding window)协议
采用“滑动窗口”的方法可以对付目前为止表述过的许多问题。一般说来,这个窗口结构在发送方和接收方都会存在:
-
发送方
记录哪些分组可以被释放、哪些分组已经发送正等待确认、哪些分组不可以被发送。
-
接收方
记录哪些分组已经被接收确认、哪些分组是下一步期望的(包括已经分配多少内存来保存他们)、哪些分组即使被接受也会被丢弃。
尽管窗口结构便于记录在发送方和接收方之间流动的数据,但是关于窗口应该有多大,或者接收方处理不过来发送的数据时会发生什么,它都没有提供相应的方法。
12.1.3 变量窗口:流量控制和拥塞控制
如上图所示,三者的速率为q>m>n时,网络的瓶颈在于接收方。当接收方相对于发送方处理出具太慢时会出现问题。我们常用的方法是:强迫发送方慢下来,这被称为流量控制(flow control)。常见的流量控制有两种方式:
-
基于速率(speed-based)
他是给发送方指定某个速率,同时确保发送的数据永远不会超过这个速率。这种类型的流量控制比较适合应用程序,可用于组播或者广播的发现。
-
基于窗口(window-based)
它是基于滑动窗口的方式,在这种情况下,窗口大小是随着时间大小可变的。接收方用来通知发送方窗口大小的报文成为窗口通告(window advertisement)或者窗口更新(window update)。 逻辑上将虽然窗口更新和ACK报文是分离的,但是
实际上窗口更新和ACK经常由同一个分组携带
,意味着发送方往往会在它的窗口滑动到右边时同时调整它的窗口的大小
。
如上图所示,三者的速率为m>n>q时,网络的瓶颈在于中间的网络设备。此时如果发送方和接收方采用超过中间网络设备能够处理的最大速率同样会造成数据的丢失。这种情况称为拥塞控制(congestion control)。拥塞控制主要指的是发送方的速率大于中间某一路由器设备的速率时,我们需要采取措施,不至于压垮其与接收方之间的网络。
12.1.4 变量窗口:设置重传超时
这个值不确定,是一个基于大量的数据统计结果。
整理自《TCPIP详解 卷1》