突然发现上一篇文章贴图有问题, 关键我怎么调也调不好, 为了表达歉意, 我再贴一篇 gitbook 上的吧, 虽然违背了我自己的隔一篇在这里发一次的潜规则~ 其余完整版可以去 gitbook(https://www.gitbook.com/@rogerzhu/)看到
如果对和程序员有关的计算机网络知识, 和对计算机网络方面的编程有兴趣, 虽然说现在这种看不见的东西真正能在实用中遇到的机会不多, 但是我始终觉得无论计算机的语言, 热点方向怎么变化, 作为一个程序员, 很多基本的知识都应该有所了解而当时在网上搜索资料的时候, 这方面的资料真的是少的可怜, 所以, 我有幸前两年接触了这方面的知识, 我觉得我应该把我知道的记录下来, 虽然写的不一定很好, 但是希望能给需要帮助的人多个参考我的计划是用半年时间来写完这一系列文章, 这个标题也是我对太多速成文章的一种态度, 好了, 废话不再多扯了, 下面是其中的一节内容, 更多内容可以去 gitbook 上找到
TCP 是一个可靠的传输协议, 这个可靠是靠着众多富有智慧的设计保证的, 而了解这其中的奥秘不仅仅是认识 TCP 的核心, 而且对生活中协议的实现也有很多借鉴价值, 首先就从最基础的停等协议开始吧
停等协议
其实停等的概念在前面介绍 TFTP 的时候就介绍过, 停等协议其实更加正统的名字应该是属于停止等待 ARQ(Automatic Repeat-reQuest)的一部分, 翻译成中文还真的一时想不到用什么比较贴切为了更加能够符合大多数人的通用名称, 在后面的文字中, 我就用 ARQ 来代指停等这种过程了就像在前面看到的, 停止等待 ARQ 有两个重要的方面, 一个是停止和等待的过程, 另外一个是编号识别, 首先还是从最简单的正常模式开始看看什么叫做停止等待 ARQ
这副图中水平箭头的两端标识两个通信端, 上面的叫做 S 端, 下面的叫做 R 端吧还可以看到在 S 端和 R 端都有若干 6 个格子组成的长方形, 这些长方形分为两个部分, 在 S 端黄色的表示的是其接收缓冲区, 白色的那一列是发送缓冲区而下面的 R 端黄色的发送缓冲区, 白色的是接收缓冲区, 和 S 端相反而每个格子表示一个包, 也就是说 S 端有三个包要发送
首先 S 端发送了 1 号包, 在发送的过程中 S 端会拷贝一份这个 1 号包, 用处在下面就会介绍, 在 S 端的第二个长方形中用虚线表示了拷贝而 R 端在接收到了这个包之后, 会把 1 号包放到自己的接收缓冲区中, 然后产生一个对 1 号包的 ACK 包, 用黄色格子并且标号 1 表示这个回复包会发送回 S 端作为对 1 号数据包的确认至此, 一次通信过程就完成了, 后面的过程才会进行 S 端等着 R 端的确认才进行下一次, 这样保证了每一次的通信对于双方都是可靠的
但是, 由于网路本身并不是可靠的, 发送出去的 1 号包可能会因为网络网路本身的问题就消失了, 这个消失并不会通知到 S 端, 在如此一个复杂的网络里要设计出这样一个机制不仅费时而且太占用资源所以在 TCP 中, 超时重发是一个最简单而又有效的办法, 虽然会浪费一些时间, 但是相比于前面所述的机制成本要小太多
除了包发着发着就消失了, 另外一种可能出现的情况是包在图中迷路了, 花了好多时间才到达对端, 这个时间比发送端的重传计时器都长, 这个叫做延迟, 这个和网游里网络延迟不是一个概念下面就要详细的看看这些异常情况, 只有理解了这些异常情况才能继续进一步理解 TCP 中的 ARQ
首先第一种情况是发送端的包在网络中丢失了, 同样, 我还是画了图
假设在接下来的发送过程中, 2 号包在发送的过程中丢失了, 这个时候 TCP 实现中的拷贝包就有帮助了, 在等待了一段时间之后, 也就是重传定时器到期了之后, 再次重新发送这个 2 号包注意, 这个重传计时器的时间肯定要比一个包在信道中往返时间 (RTT) 要长一些为什么? 因为如果不是这样, 发送端如何确认是丢包了还是只是等待的人还没有来? 这个知识点, 在面试的时候问人能答上来的不会超过一半所以在图中, 这个空隙我故意画个大的, 在这里假设第二次传输就成功了, 在经过第二次重传之后, 就回到上面一种情况
下面介绍第二种可能发生的错误的情况, 假设发送端的包正确的到达了接收端, 但是接收端传回来的确认却丢失了
如果是 R 端的回复丢失, 其实站在 S 端这边, 他是不可能知道到底是 3 号包丢失了还是回送的 3 号包的应答包丢失了, 在 S 端, 这两种行为没有任何区别所以在重传计数器到期之后, S 端会重传其保存的 3 号副本而在这个包再次到达对端之后, R 端的接收缓冲区中已经有了 3 号包了, 这时又一次到达了 3 号包, 这个时候 R 端就会直接丢弃掉这个重复到达的 3 号包, 因为 R 端已经拥有了此时其实 R 端就能知道自己回复丢失了, 因为受到了重复包, 所以他再一次的发送这个对 3 号包的应答
上面所说的都是包在传输过程中丢失的情况, 除了这种情况, 还会有一种包走着走着就迷路了, 但是经过了一段时间之后, 它又在各种路由协议的指导下找到了正确的道路, 到达了对端而偏偏就在这个迷路的时间里, S 端因为半天没有收到对端的回复以为自己的包丢失了, 所以又重发了一遍的包这个情况用图来表示更加清晰
在这个图中 R 端的 3 号应答包迷失了, 在重传定时器到期之后 S 端又重传了 3 号包, 这个包让 R 端知道自己的 3 号应答包因为某种原因没有到达对端此时, R 端再次发送 3 号应答包, 并且成功到达了对端可过了一段时间之后, 那个迷路的 3 号应答包又一次的到达了 S 端, 而这个时候 S 端的接收缓冲区中已经有了 3 号应答包, S 端会简单的丢弃掉这个重复的 3 号应答包
等待的过程总是漫长的
上面说的等待停止 ARQ 确实可以保证数据可靠的在一个不可靠的信道上传输, 但是有一个很现实的问题, 对于这种每次发送一个包然后再确认一个包效率实在是太低, 上面所画的只是一次通信的过程, 如果我们按照上面的方式把 6 次成功的过程画在一个图上就是下面 这张图的上面的样子
而下面这个图就是现在要介绍的连续 ARQ 协议了, 连续 ARQ 相对于停等 ARQ, 其信道的利用率大大提高, 同样发送 6 个包, 前面的停等 ARQ 比连续 ARQ 要花了更多的时间说了这么多, 那么什么叫做连续 ARQ 呢? 连续 ARQ 就是不要那么快的发送确认, 等接收端接收到几个包之后再发送确认, 这样做的好处, 第一明显的是减小了通信的流量, 其次也省去了很多时间, 这一点, 从图中的长短也能看出来其变化
在实际的实现过程中, TCP 会维护一个被称之为窗口的东西和累积确认的机制来实现这个连续 ARQ 用数学的概念来描述, 可以理解为是一个将要发送所有数据的一个子集, 这个子集中包括的是可以发送的数据包范围抽象成一个图的话就如下面一样:
比如说要发送的数据包一共有 8 个, 这些包都存在于 TCP 发送端的发送缓冲区中, 而图中绿色的就是目前可以一次发送最大的数据包的范围, 称之为滑动窗口, 具体的来说就是在窗口内的数据包可以一次性发出去而不需要等待对端的确认, 而不是像停等模式那样非要一包一包的发然后等待对方的确认
而对端会根据自身的情况选择可以确认的数据包, 以上图为例, 对端在收到 2 号数据包之后发送了对编号为 2 的 ACK 包, 在 TCP 的设计中, ACK 包的中的确认序号标识该序号之前的包都已经收到, 这种机制叫做累积确认发送端在接收到 2 号 ACK 包之后, 就可以更改最大可以一次性发送的数据包范围, 因为这个时候发送端已经知道有 1,2 号包已经妥善的投递到对端这种操作就像我们推窗户一样, 窗户向后面滑动, 是不是很形象? 所以有时候计算机工作者也很艺术很生活的
正如前面所述的, 任何异常情况都是 TCP 这样一个声称可靠的协议需要考虑和解决的在上面的窗口机制中, 窗口 "中的所有数据包可以一次性发出而不需要等待任何确认那么假设这么一种情况, 如果 1-5 的包一次性发出去了, 但是 2,3 号包丢失了, 最后 1,4,5 号包到了, 对端会怎么样? 按照前面的累积确认" 机制, 对端不可能发送编号为 5 的 ACK 包, 因为这样会导致发送端认为 5 号包以前的都妥善的接收了实际上, 对端只能发送对 1 号包的 ACK 包, 因为这样才能不打破累积确认 "的规则那么发送端的窗口只能向后移动一个包, 这种机制叫做"Go-back-N", 俗成" 滚回未确定的时候 ", 在这个情况下, 发送端只能 go back 到 2 号包, 重新发送 2-6 号包
滑动窗口的概念在 TCP 中十分的经典, 在下一篇中会更加详细的讲述这个著名的 "滑动窗口"
为了更好的宣传我的 gitbook, 算是给需要的人提供个帮助, 以下是特殊时间:
来源: https://www.cnblogs.com/ZXYloveFR/p/8663306.html