上篇讲的是拥塞控制,这篇讲流量控制。还是以运输粮食为场景。
虽然运输的拥塞控制能够在运输效率和拥塞之间达到平衡,但是运输粮食的整体效率还受一个关键因数影响,那就是对岸西村粮食的卸货能力。假设粮食运送到对岸后,要卸放在一个仓库里,随后被运往其他地方。如果运输的太快,仓库载货能力有限,粮食又没有及时被消耗,工人们只好等待仓库腾出地方来卸下新的粮食。
显然这样会影响这个工程的效率。后来工人们又想了一个方案:每次卸完粮食返回时,告诉东村的搬运工人当前对方仓库的容量。这样东村的工人就做到心中有数,当仓库容量非常充裕时,每船的粮食数据就多装点,如果仓库已经快满了,就减少每船粮食的数量。注意这里只是调整的每船盛装粮食的数量,没有调整发送船的数量。发送船只的数量是用来调节拥塞的。
为了加快工作效率,减少沟通成本,工人们的智慧又发挥出来了。他们规定每只船上都有3面旗帜。一面旗帜A写上运输粮食的编号,另一面旗帜B在船只返航时写上西村仓库还能盛装的数量。还一面旗帜C写上已收到粮食的最大序号。例如一只船要发送编号201-500的粮食,A面旗帜上写上最小的编号数201,当该船粮食安全运输到西村后,西村卸货的工人清点该船粮食,共计收到300数量的粮食。此时他们要告诉东村的工人已经收到了300数量的粮食,根据A面旗帜的数字201,就在C面旗帜写上501,表明已收到300(501-201)数量的粮食,同时也是期望他们下次发送编号为501开始之后的粮食。返回时还需要报告东村,西村仓库还能装多少粮食的数量。如果西村仓库还能盛装600的粮食,西村工人就在B面旗帜上写上600。当工人们看到返回船只B面旗帜上为600,就知道此刻最多只能发送600数量的粮食,如果粮食还够600的话,即要发送编号为501-1100共计600数量的粮食。同时还规定如果仓库已满就在返回船只B面旗帜上写0,等到仓库有了空余地方,专门会有人过去通知东村工人。
按照这种方式运输粮食一直相安无事。可是有一天却发生了情况,导致运输中断了。这是怎么回事呢?工人同心协力,组织得当,很快对岸仓库就被塞满,返回的船只上B面旗帜都书写为0。这样东村工人只能干等着,等西村有人来通知仓库有空余地方了。可是那天水面实在太堵了,西村跑腿工人后来竟然发生事故沉船了。东村工人左等不来右等不来报信的,运输被迫中断。
为了防止该类事件再次发送,工人们又规定,如果被告知对方仓库已满,就开启一个闹钟,时间到了就派一只小船前去打听对方仓库的情况。这样即使西村派过来的跑腿工人不幸丢失,也能保证运输继续下去。
上述场景中ABC面旗帜就代表TCP协议头中的3个字段,一个是32位系列号,一个是32位确认号,一个是16位窗口大小。如下图所示:
发送数据时的示意图为:
另外上面场景中的闹钟对应TCP协议栈中的持续定时器(persist timer),他的作用是,发送方只要接收到了0窗口通告,就开启该定时器,周期性的向接收方发送1字节的0窗口探测报文。
欢迎关注公众号『漫话互联网那些事』。一个把互联网/编程技术当故事讲的公众号。如果有任何技术或者职业方面的问题需要我提供帮助,也可通过这个公众号与我取得联系。