TCP-流量控制

前言

一直忙着面试,拿到了比较喜欢的offer,终于空出时间继续做做笔记,嗯,工作要换了,但学习停不了! 有机会也会出点这段时间遇到的奇葩面经~

概述

一般来说,我们总希望数据传输的更快一点。

但如果发送方把数据发送的过快,接收方就可能来不及接收,这就会造成 数据的丢失

所谓 流量控制(flow control) 就是让发送方的发送速率不要太快,要让接收方来得及接收。

利用滑动窗口实现流量控制

利用 滑动窗口 机制可以很方便地在 TCP 连接上实现对发送方的 流量控制

下面我会举一个例子来说明如何利用滑动窗口机制进行流量控制。ok,来看看本帅画的美图:

利用可变窗口进行流量控制

假设 A 向 B 发送数据。在连接建立时, B 告诉了 A: “我的接收窗口 rwnd = 400” (这里的 rwnd 表示 receiver window)。 因此, 发送方的发送窗口不能超过接收方给出的接收窗口的数值。 请注意, TCP的 窗口单位是字节,不是报文段。 TCP 连接建立时的窗口协商过程在图中没有显示出来。

再设每一个报文段为 100 字节长,而数据报文段序号的初始值为 1(见图中第一个箭头上面的序号 seq = 1。 图中右边的注释可帮助理解整个流量控制的过程)。

请注意, 图中箭头上面大写的 ACK 表示首部的确认为 ACK, 小写 ack 表示确认字段的值。

这里我们可以看到, 接收方主机 B 进行了三次流量控制。 第一次把窗口减小到 rwnd = 300, 第二次又减到 rwnd = 100, 最后减到 rwnd = 0 ,即不允许发送方再发送数据了。 这种使 发送方暂停发送的状态将持续到主机 B 重新发送一个新的窗口为止。

另外, 可以看到 B 主机发送了三个报文段 (ACK = 1, xxx) , 都设置了 ACK = 1 ,也是只有在 ACK = 1 时确认号字段才会有意义。

持续计时器

现在我们考虑一种情况,在上图中,B 向 A 发送了零窗口的报文段不久后, B 的接收缓存又有了一些存储空间。 于是 B 向 A 发送了 rwnd = 400 的报文段。 然而这个报文段在传送过程中 丢失 了。 A 一直等待收到 B 发送的数据。如果没有其它措施, 这种互相等待的 死锁 局面将一直持续下去。

为了解决这个问题, TCP 为每一个连接设有一个 持续计时器(persistence timer)。 只要 TCP 连接的一方收到对方的零窗口通知,就启动持续计时器。 若持续计时器设置的时间到期,就发送一个 探测报文段 (仅携带 1 字节的数据), 而对方就在确认这个探测报文时返回现在的窗口值。 如果窗口仍然为零, 那么收到这个报文段的一方就重新设置持续计时器。 如果窗口不为零, 那么死锁的僵局就打破了。

必须考虑的传输效率

前面已经讲过, 应用进程把数据传送到 TCP 的发送缓存后, 剩下的发送任务就由 TCP 来控制了。可以使用不同的机制来控制 TCP 报文段的发送时机。 例如:

  1. 第一种机制是 TCP 维持一个变量, 它等于 最大报文段长度 MSS。 只要缓存中存放的数据达到 MSS 字节时, 就组装成一个 TCP 报文段发送出去。
  2. 第二种机制是由发送方的应用进程指明要求发送报文段, 即 TCP 支持的 推送(push) 操作。
  3. 第三种机制是发送方的一个计时器期限到了, 这是就把当前已有的缓存数据装入报文段(长度不能超过 MSS) 发送出去。

但是,如何控制 TCP 的发送时机仍然是个较为复杂的问题。

举个例子

一个交互式用户使用一条 TELNET 连接 (运输层为 TCP 协议)。 假设用户只发 1 个字符。加上 20 字节首部后, 得到 21 字节长的 TCP 报文段。再加上 20 字节的 IP 首部, 形成 41 字节长的 IP 数据报。

(1字符) + (20TCP首部) + (20IP首部) = (41IP数据报)

在接收方 TCP 立即发出确认,构成的数据报是 40 字节长(假定没有数据发送)。若用户要求远程主机回送这一字符, 则又要发送 41 字节长的 IP数据报 和 40字节长的确认 IP 数据报。这样,用户仅发一个字符时线路上就需要传输总长度为 162 字节共 4 个报文段 ,噢吉纳我的天,这也太多了把!?

这么看,当带宽比较差的情况下,这种传输方式效率就很低下。因此适当的推迟回复确认报文,并尽量少用捎带确认的方法。

Nagle 算法

在 TCP 中广泛使用的 Nagle 算法。

若发送应用进程把要发送的数据逐个字节地发送到 TCP 的发送缓存。 则发送方就把第一个数据字节先发出去,把后面到达的数据字节都缓存起来。当发送方收到对第一个数据字符的确认后,再把发送缓存中的所有数据组装成一个报文段发送,同时继续对随后到达的数据进行缓存。只有在收到对前一个报文段的确认后才继续发送下一个报文段。当数据到达较快而网络速率较慢时,用这样的方法可明显减少所用的网络带宽。

Nagle 算法还规定,当到达的数据已达到发送窗口大小的一半或者已达到报文段的最大长度时,就立即发送一个报文段。这样做,就可以有效的提高网络的吞吐量。

糊涂窗口综合症(silly window syndrome)

这个问题有时候也会使 TCP 性能变差。例如:

TCP 接收方的缓存已满,而交互式的应用进程一次只从接收缓存中读 1 个字节 (接收缓存腾出 1 个字节), 然后向发送方发送确认,并把窗口大小设置为 1 个字节(但发送的数据报是 40 个字节长)。接着,发送方又发来 1 个字节的数据(这里发送方的 IP 数据报是 41 字节长)。 接收方返回确认,仍然将窗口设置为 1 个字节。这样下去就会严重影响到网络传输的效率。

如何解决?

可以让 接收方等待一段时间, 使得接收缓存已有足够空间容纳一个最长报文段,或者 等到接收缓存已有一半的空闲时间 。只要出现这两种情况之一,接收方就发出确认报文,并向发送方通知当前窗口大小。此外,发送方也不要发送太小的报文段,而是把数据积累成足够大的报文段,或达到接收方缓存空间的一半大小。

这两种方法可以配合使用,使得在发送方不发送很小的报文段的同时,接收方也不要在缓存刚刚有了一点小的空间就急忙把这个很小的窗口大小信息通知给发送方。

总结

不知道还记不记得,优化http网络速率的主要的两个问题,就在于 延迟带宽 ,而本文可以得出,TCP 的流量控制其实就是在带宽上做优化,怎么在更短的时间内让接收方和发送方传输更多的数据?

重点还是在于 滑动窗口机制避免数据传输浪费避免短数据传输 的情况。

另外还有 TCP的拥塞控制 相关的知识我会后续记录。

这里主要回顾了下大学的计算机网络,~如果有影响到的地方,可以联系我删除:[email protected]

你可能感兴趣的:(TCP-流量控制)