为什么有时候下载东西一开始很快,后来就越来越慢?
41 个回答
好吧,纸糊惯例:高票答案纯属一本正经的胡扯八道。
这个是FileZilla,server用来建立ftp服务器,client就不用说了,码农们最喜欢的ftp客户端软件,不知道有没有之一,反正天天见。
两者刚好一套。自己配个环境,试试看会不会出现“最后一段下载速度缓慢”问题。
Wireshark,协议分析利器。实际看看TCP协议运作过程。
不要看见贴几张图片就以为那是标准答案。能几乎每个关键点都说错,学成这样还真是难能可贵了。
1、“慢起动”速度真的慢,但持续时间极短。
慢起动阶段,TCP window从极小(如1、2……10字节等)开始,每个RTT周期下载速度最高增加一倍(RTT已知时,TCP window大小和传输速率成正比,故可认为每个RTT周期下载速度增加一倍)。
出现第一个丢包或满足其它条件时(其它条件较复杂,不讨论),慢起动停止。
对常见的宽带链路,哪怕RTT取50ms,那也是每50ms链路下载速率翻一番。1秒钟足够下载速率上涨2^20倍。
哪怕最不利情况下,几秒钟也足够跑到链路带宽上限了……
神TM的“初始下载速度快是因为慢开始”。
2、“拥塞控制”很稳。
如前所述,你先下个FileZilla-server搭建个环境,然后用FileZilla-client从上面下一个大文件(比如高清视频/游戏安装包什么的)。
打开任务管理器,性能-以太网,看看下载速率稳不稳,是不是一本道答案那样大起大落。
恰恰相反,几乎所有的拥塞控制算法都可以保证“实际数据传输速率在网络允许的最大带宽附近波动”——实际到任务管理器里面看看是不是这样。
神TM的“后面一直减半减半减半就会越来越慢越慢越慢”——真要把程序写成那样,搞网络的还是买块豆腐碰死算了。
3、“快速重传”对拥塞控制算法的影响很复杂,不同算法有不同的应对。
要谈论“快速重传”,需要先搞明白DUP ACK。
DUP ACK是这样一种机制:通常情况下,接收端收到发送端发来的包后,需要回一个ACK包。这个ACK包的作用是“通知发送端,编号xx的包我已经收到了,请继续发编号xx+1的包”。那么如果包10延误或丢失了,接收端就可能在收到包9后收到包11、12、13……协议规定,当收到这样不按顺序到来的包时,接收端也要回一个ACK,但这个ACK不能确认包11收到(因为TCP协议允许用一个ACK确认一系列包,比如对包11的ACK意味着包11之前的所有包已经全部收到),而是重复报告“包9已收到,请求包10”——这就叫DUP ACK。发送方见到这种DUP ACK,就明白接收方收到的包乱序了。
正常情况下,一个包必须经过足够长时间才能确认其丢失,然后发送端才会重传它。这个响应周期太长了,对传输速率影响太大。于是有些算法就利用DUP ACK:一旦连续出现3个DUP ACK,就认为报文已经丢失,于是立即重传DUP ACK请求的下一个包。这就叫“快速重传”。
快速重传大大加快了网络拥塞的发现/响应速度,依赖它的算法自然就会比“超时重传算法”更“精细”一些——换句话说,就是“在网络允许最大带宽”附近的波动更“细碎”一些,对网络带宽的利用率也更高。
第一次丢包时TCP window size的一半被称为"slow start threshold",大多流控算法遇到DUP ACK会直接回退到这个值(并不像最古老的算法那样重新执行slow start过程)。这就有效降低了波动幅度。再加上各种缓冲区起到的类“滤波”作用,从而可以用缓冲区内容“填充”网速“低谷”、又用缓冲区空间“削平”网速“高峰”:换句话说,虽然TCP算法的发送速率的确是“锯齿形”曲线,但实际的数据流却相对平缓,只是RTT(可理解为报文发送-响应间隔时间)周期性变化而已。 而最新的BBR算法直接依赖RTT的改变。它把网速稳定在RTT不会增长的最高速率上。 换句话说,它检测的是链路中缓冲区的使用:因为一旦用了缓冲区,那么RTT就会增加,所以RTT增加就说明有设备开始用缓冲区了。而使用缓冲区本身,又是网速已达上限的标志(因为不缓冲就得丢包)。把网速锚定在这里,显然是更科学、更合理的。
重复一遍:拥塞控制算法的目标是尽量提高网络带宽的利用率、同时尽量保证每个链路公平分享带宽。所谓“拥塞控制算法导致下载越来越慢”纯属胡扯八道。
PS:google最新的BBR算法无视DUP ACK。它比旧有算法更接近本质。
但这些和主题无关,故不再深入讨论。
————————————————————————————
如果大家亲自搭建ftp服务器做过实验,就会发现,哪怕下一个100G的大文件,整个下载过程都是非常平稳的:如果没有其它干扰且硬盘访问速度足够,那么你会发现,除了最开始速率略慢、但1秒不到就到了网速上限然后一直保持,直到文件全部下载完毕,没有任何大的波动。
其中,这个“1秒不到就逼近网速上限”就是“慢起动”过程;之后使得网速在几分钟乃至几小时内都一直保持稳定高速的,就是拥塞控制算法。
但是……网上到别人的服务器下载文件可不是这样啊?
无论如何,我们已经通过实验证明了,TCP/ftp协议本身没有问题。那么我们就不需要继续在上面浪费时间了。
那么,问题在哪呢?
1、很多朋友谈到“多线程下载”了。尤其对P2P下载,这的确是一个重要影响因素。
这是因为,前面提到过,TCP拥塞算法要保证“每个链路公平分享带宽”——嗯,如果一共10M带宽,我和张三等十人每人一条链接ftp下载,那么每人都能达到1M的下载速率,很公平,对吧?
但,如果张三耍流氓,他多开了一条链接呢?
现在成了每条链接0.9M带宽,仍然很公平,对吧?
……当然不对!
张三是两条链接,他有1.8M的下载速率;而其它9人每人只有1条链接,所以只有0.9M的带宽。
怎么办?多开链接呗。你敢开三条,我就开四条……你开200条,我开400条……谁开的多谁抢到的带宽就越多,傻子才不开。
(网管:现在你们明白为啥要禁P2P了吧)
(服务提供商也不干了:每条链接都得有个接受/发送缓冲区,这可都是内存啊。你们这么整,我服务器还不得累死。不行,我得配置个策略,每个用户/ip至多只准建立一条链接[实际一般为1~5条],多了就强制断开,反复重试我ban你ip)
(举例只是类似宿舍共用路由的情形;实际上网络任何部分都可能出现瓶颈。但一般来说,瓶颈多出现在末端,即用户路由器和内容提供商服务器那里;另外就是跨国通信带宽不足导致瓶颈——电信-联通这种奇葩情况不予讨论)
下载一个文件时,只剩最后一点点了,多开链接就不划算了——还没跑满带宽就搬回来了,开越多反而更慢,还讨人厌;还有的干脆从协议上就不支持过小的分块……所以多线程下载到最后,只能1条链接搬,速度自然就慢了。
但,如果仅仅是争抢带宽的话……经常P2P下载单链接也能跑到400K啊;为什么只剩一点点时,速度总是只剩2~3K?
这是因为:
2、资源提供者的心机
我们知道,P2P号称“下载者越多速度越快”;原理很简单,程序会自动让每个人把自己下载到的部分分享出去,下载的人越多,分享者自然也越多,速度当然就越快了。
但现实中,有些人只下载不上传(这种行为被形象的称为“吸血”,现在有不少专门的反吸血模块可以检测到他们并拒绝他们下载);同时,多数用户的下载带宽和上传带宽不对等(说的就是你,ADSL;其实现在的光纤宽带,一般也多是下载带宽几倍于上传带宽);最后,多数人夜里挂P2P软件,并且会设置“下载结束后关机”。
这就导致下载量总是远大于上传量——这么一来,“人越多反而越快”就成了一句空话,玩不起来了。
怎么办?
检查谁快下载完了,限他的速,让他多做会儿种。
类似的,普通的下载站,他们当然希望吸引更多用户;怎么吸引呢?丰富的内容+更高的下载速度。
但,想提供更高的下载速度,就必须购买更多的带宽……可买带宽是需要钱钱的……
怎么办?
当同时下载量太大时,发现一个用户刚开始下载,就给他一个更高的限速,允许他“光速下载”;下载一段时间后再限速——用户没耐心一直盯着看,刚开始速度快让他很高兴,中后期他不看了就降速节约带宽:这就解决了“更快的下载速度”和“带宽得花钱买”之间的矛盾。
于是,买下和竞争对手一样多的带宽,用户就是感觉我更快!
————————————————————
最后,很多人可能会奇怪:下载到最后一点点,速度总是很慢;我暂停/断开一小会儿,重新开始时下载速度就会很快,然后很快又降低到原来的程度了,这是为什么?
不,这可不是因为高票那个一本道答案的“慢起动”。
要说清这个,咱得从限速算法的原理说起。
具体细节太复杂了;简而言之,为了满足各种刁钻古怪的要求,限速算法的原理并不是直接限制网络传输速度(没法实现),而是“你想传出去每一个字节的数据,都得得到我的授权;而‘授权令’的产生速度很容易控制;那么如果我每秒只产生1M个授权令,你的传输速率自然最高只有1M/s;最后,规定‘授权令’可累积,但有上限”。
明白了这个,问题就很容易解答了:下载到最后速度很慢是因为限速,给你的授权令产生太慢,产生一点你用一点,传输速率自然就被限到几K了;暂停/断开后,授权令就给你累计下来,那么恢复传输后,你就可以一次性把积攒下来的授权给用完——于是瞬时传输速度就飙到了几百K甚至若干M;可一旦积攒的授权用完了,你还是只能等新的授权,传输速率自然就又回到了几K……
你以为自己得了实惠……实际上,瞬时几百K的速度用的还是暂停时本就该分给你的授权;反倒是,因为暂停/断开时间过长,你还因为溢出而浪费了一些“多余”的授权令……
PS:很多软件计算传输速率用的是平均值。所以看起来传输速率并不是暴起暴落,而是先600K后300K然后160K最后又回到20K;但实际上只有第一秒是600K,第二秒已经是20K了;但它把两秒的流量加起来一平均……
PS2:有些时候,因为最后一段时间被严重限速,导致相关线程优先级过低;然后如果软件实现有问题,就很容易在这段时间暴露出来。此时断开链接重试就类似网络版的“重启下说不定就好了”。对这种情况,断开重试的确有助于提高最后一段的下载速度。
PS3:家用路由器的CPU太弱、内存也太少,所以打开流控/QOS时,经常在一段时间的使用后导致软件状态紊乱。此时,经过路由器的报文延迟极大增加、频频出现丢包/乱序问题,且延迟时间忽高忽低。这也会严重影响下载速率(网游什么的当然更没法玩)。这种情况往往需要重启路由才能恢复正常(有的路由甚至可能因为过热而自动重启)。但如果关闭流控等高级功能,拿这种路由当交换机用,就极少出现这种问题了。
这大概也就是为何外部参数(速率、网口数、支持功能等)差不多的路由器,有的只要几十元,有的却敢“狮子大开口”要几千甚至几万的原因吧。
PS4:国内因为墙的干扰,有时下载正常内容也会遭遇墙发来的RST包。这也会导致下载中断。由于大多软件不知道如何处理RST包(这种报文太特殊,正常情况下压根就不应该出现),故此时往往只能通过人工干预恢复……
PS5:如果你对网络感兴趣,想知道更多……那么装个Linux吧。装虚拟机里面也行。
Linux实现了几乎所有的流控算法,你可以从中自由选择,以便观察它们在实际网络环境中的表现。
你可以很容易的使用TCP DUMP配合脚本,深入观察/分析各种算法的表现;还可以利用TC模拟网络抖动、丢包、乱序等几乎所有你能想到的奇葩状况,从而观察异常情况下各种流控算法的实际表现。
基本上是多线程分段式下载的结果。
现在很多下载算法都有一个默认分段下载,比如超过100M大小就分段,最多分10段,每个分段至少20M
这样一个400M的文件可能一开始被分成10段下载,每段40M,同时开10个tcp链接下载。
tcp下载速度和几个因素有关:时延,接收端窗口大小,丢包率。(参考: TCP传输性能剖析 - 推酷 http://www. tuicool.com/articles/re EVr2M ), TCP Throughput Calculator , A Very Simple Model for TCP Throughput | Blog | ThousandEyes | Network Monitoring Software ,其中最后一个的公式是“大众”参考公式(即简化版), T=\frac{MSS*C}{ RTT*\sqrt{p}} ,其中T是TCP吞吐量,MSS是窗口,RTT是双向时延,p是丢包率,C是一个常数,一般取 \sqrt{\frac{3}{2}} )。
当网络状况一样,窗口大小一样时,每个tcp线程最高速度确定。所以分段多线程下载要比单线程下载快很多。尤其是带宽很大时。
但是不同的线程下载速度并不是完全相等的。有的快有的慢(因为tcp总是尝试提高速度塞了再降),另外还有就是分段的最后一段可能不够长(比如一个360M的文件最后一个分段才10M其他段是40M)。快的线程结束后,会去看看其他线程剩下的部分是不是够20M,如果超过20M,可以再分成两段,原来那段继续下,新加入的一段则是提速的。这时,总速度还是10个线程的速度。
但是,总有剩下部分不够20M的时候。这时候下载程序就会降低线程数(已经完成任务的部分就退出)。所以会看到速度越来越慢,最后的速度就是一个单线程的速度。
所以将近结束时,会越来越慢。如果仔细观察,一般能观察到线性下降的过程。
当然,这个机制前提是服务器支持多线程下载。如果服务器不支持就不存在了。
看到有答案说到P2P了。也是类似的机制,分段,多线程。但是,每个段还是有下限。P2P的一个特殊点就是还要提供上传。如果本地已经给其他人提供了上传数据,那么需要等上传的“段”结束以后才能结束一个下载任务。