
文章插图
终于收到ACK的发送主机
当发送方收到ACK回复时,它看到里面的回复号为L+1,也就是发送方下一个应该发送的TCP片段序号 。发送方推断出之前的片段已经被正确的接收,随后发出L+1号片段 。ACK回复也有可能丢失 。对于发送方来说,这和接收方拒绝发送ACK回复是一样的 。发送方会重复发送,而接收方接收到已知会过的片段,推断出ACK回复丢失,会重新发送ACK回复 。
通过ACK回复和重新发送机制,TCP协议将片段传输变得可靠 。尽管底盘是不可靠的IP协议,但TCP协议以一种“不放弃的精神”,不断尝试,最终成功 。(技术也可以很励志)

文章插图
面对“挫折”,TCP协议的态度: never give up
TCP协议和UDP协议走了两个极端 。TCP协议复杂但可靠,UDP协议轻便但不可靠 。在处理异常的时候,TCP极端负责,而UDP一副无所谓的样子 。我们可以顺便“黑”一下UDP协议:

文章插图
同样面对“挫折”,UDP的态度: who cares...
滑窗
上面的工作方式中,发送方保持发送->等待ACK->发送->等待ACK...的单线工作方式,这样的工作方式叫做stop-and-wait 。stop-and-wait虽然实现了TCP通信的可靠性,但同时牺牲了网络通信的效率 。在等待ACK的时间段内,我们的网络都处于闲置(idle)状态 。我们希望有一种方式,可以同时发送出多个片段 。然而如果同时发出多个片段,那么由于IP包传送是无次序的,有可能会生成乱序片段(out-of-order),也就是后发出的片段先到达 。在stop-and-wait的工作方式下,乱序片段完全被拒绝,这也很不效率 。毕竟,乱序片段只是提前到达的片段 。我们可以在缓存中先存放它,等到它之前的片段补充完毕,再将它缀在后面 。然而,如果一个乱序片段实在是太过提前(太“乱”了),该片段将长时间占用缓存 。我们需要一种折中的方法来解决该问题:利用缓存保留一些“不那么乱”的片段,期望能在段时间内补充上之前的片段(暂不处理,但发送相应的ACK);对于“乱”的比较厉害的片段,则将它们拒绝(不处理,也不发送对应的ACK) 。

文章插图
总有那么几个“出格”片段
滑窗(sliding window)被同时应用于接收方和发送方,以解决以上问题 。发送方和接收方各有一个滑窗 。当片段位于滑窗中时,表示TCP正在处理该片段 。滑窗中可以有多个片段,也就是可以同时处理多个片段 。滑窗越大,越大的滑窗同时处理的片段数目越多(当然,计算机也必须分配出更多的缓存供滑窗使用) 。

文章插图
同时处理多个片段
我们假设一个可以容纳三个片段的滑窗,并假设片段从左向右排列 。对于发送方来说,滑窗的左侧为已发送并已ACK过的片段序列,滑窗右侧是尚未发送的片段序列 。滑窗中的片段(比如片段5,6,7)被发送出去,并等待相应的ACK 。如果收到片段5的ACK,滑窗将向右移动 。这样,新的片段从右侧进入滑窗内,被发送出去,并进入等待状态 。在接收到片段5的ACK之前,滑窗不会移动,即使已经收到了片段6和7的ACK 。这样,就保证了滑窗左侧的序列是已经发送的、接收到ACK的、符合顺序的片段序列 。
对于接收方来说,滑窗的左侧是已经正确收到并ACK回复过的片段(比如片段1,2,3,4),也就是正确接收到的文本流 。滑窗中是期望接收的片段(比如片段5, 6, 7) 。同样,如果片段6,7先到达,那么滑窗不会移动 。如果片段5先到达,那么滑窗会向右移动,以等待接收新的片段 。如果出现滑窗之外的片段,比如片段9,那么滑窗将拒绝接收 。
下面一个视频中,尝试模拟可容纳三个片段的滑窗(固定大小)的工作过程 。
可点下面链接: http://v.youku.com/v_show/id_XNDg1NDUyMDUy.html
上面的视频是用Python和matplotlib包制作的 。蓝色点表示片段,红色点表示ACK 。为了说明乱序片段,我故意让片段和ACK的速度从两个值中随机选择 。
可以看到,随着滑窗的滑动,越来越多的片段被正确的传送 。利用滑窗,我们一定程度上实现了对乱序数据的缓存 。但是,过于乱序的数据依然会被拒绝 。我们之前说的stop-and-wait的工作方式,相当于发送方和接收方的滑窗都只能容纳一个片段 。
推荐阅读
- 你知道 HTTP 是如何使用 TCP 连接的吗?
- Node.js 环境在 Windows 系统下安装与搭建教程
- wireshark网络小故障分析定位
- 你知道 HTTP 是如何使用 TCP 连接的吗?今天我就来告诉你
- 四种心态与禅茶的相融合
- 婴儿抽搐与抖动的区别
- 洗衣粉与84能混用吗 洗衣粉可不可以和84同时使用
- 阿联酋的气候与温度 阿联酋温度
- 安溪茶业秋季配套商品展览订货会特色与看点
- 徒步鞋和登山鞋的区别,徒步鞋的选购
