从Telnet到TCP,从HTTP2.0到QUIC
Posted 世纪顶点科技有限公司
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从Telnet到TCP,从HTTP2.0到QUIC相关的知识,希望对你有一定的参考价值。
当几乎所有人都在关注TCP BBR算法的时候,其实还有另外一条脉络,花开两朵,各表一枝,从历史发展的角度梳理故事的情节。
事情从20世纪70年代开始。
人们最初的想法是不要让应用感知网络的存在,分层模型早已有之,网络层协议会处理网络细节。这就使得不管最终谁成了传输协议的标准,都一定要是端到端的。从业务的角度看,人们希望通过网络远程登录一台UNIX主机,这也就使得Telnet成了TCP/IP体系下最为古老的协议之一,一直到今天,我们依然在使用它(虽然现在它已经退化成端口测试工具了)。
Telnet这一类程序的特点:
远程输入必须到达主机,一个字节都不能丢;
远程输出必须到达终端显示,一个字节都不能丢;
输入的顺序必须和主机接收的顺序一致,不能乱序;
输出的顺序必须和终端接收的顺序一致,不能乱序。
这些特征可以总结为强时序依赖,很难跳过一些步骤提前做以后的事情,因为未来的输出依赖于此前的输入。这注定未来承载Telnet的协议需要构建的是一个双向串行流。
为了在一个不可靠的网络中构建端到端的可靠的双向串行流,主机需要做什么以及怎么做?
从资源的角度来看,20世纪70年代是一个资源匮乏的年代,无论是带宽还是内存,如果说网络是不可靠的,为了在主机端保证可靠性,排队论告诉我们这注定会让主机的内存利用率指数级膨胀,最终系统崩溃。
另一方面,请了解一下停等协议,这应该是最简单的实现上述可靠按序需求的主机端方案(端到端需求)了!
人们很自然地会从停等协议得到扩展,如果能1个字节停等,那就能2个字节停等,那为什么不是n个字节停等?因此,这注定了所谓的TCP协议实现所具有的超级特征,即:
n字节停等,n字节积累确认;
n字节滑动窗口,资源限制;
基于数据包的超时重传。
这就是TCP!积累确认和滑动窗口是其基本特征,其它的都是后面加入的,一开始的时候并没有拥塞控制,这是一个非常值得注意的点。从反面来讲。
如果TCP一开始就考虑了拥塞控制,它可能就不会选择积累确认,而是会采用完全选择确认的方式,而我们知道TCP的SACK是在迭代了很多版本后才专门为更精确的进行拥塞控制引入的,注意到这个因果关系将会对优化拥塞控制算法有很大的帮助。
积累确认是一种非常简单的实现方式,只要有按序到达的数据包便迅速消费掉,非按序到达的数据包到达就直接丢弃(TCP的原始版本没有SACK),这反过来满足了主机内存资源匮乏时代的限制性要求,然而,这种简单的实现方式注定使得TCP发送端在收到积累性确认后,无法区分该确认是针对原始数据包的还是针对重传数据包的,这个问题将会对未来所有的拥塞控制算法相关的RTT测量带来极大的困扰!
TCP完美满足了Telnet这种程序的需求,如果把坐在Telnet终端前的人换成另一个程序,事情依然完美,但这是BSD socket的缘故,这使得BSD socket成为了IPC的常规方式。
在Telnet之外,文件传输是另一个典型业务场景,总之,我们可以把TCP总结为可以把一块数据从一个地方原封不动搬运到另一个地方的协议。
1989年开始衍生出来的HTTP0.9是一个请求文件的协议,它向服务器请求一个文件,随即服务器将文件会送给请求者。这像极了Telnet,即输入一个ls,然后主机将目录内容列表显示出来。这也注定了一开始HTTP就会选择TCP作为它的承载协议。因为HTTP和TCP搭档实在是太成功了,TCP载着HTTP度过了HTTP的童年。也许TCP会一直这么载着HTTP…
进入青春期后的HTTP1.x使得互联网发生了重大的变化,互联网突然显得热闹了起来,Web浏览器的出现和发展让互联网应用的交互性得到加强,同时页面也更加漂亮了,这些反过来又催生了更多更好玩更有用的互联网应用。
当人们输入一个URL点击回车的时候,已经不再仅仅是GET一个文件这么简单了,你会发现实际上可能发出了100个请求,每一个页面上小小的gif可能都是一个文件,就更不要说那些复杂的CSS,js脚本之类的了。
事情就发展了10多年,大概从20世纪90年代末到2010年左右,苹果公司iPhone的发布和成功让移动互联网开始发展,作为互联网本身的衍生,移动互联网依然采用了已经非常成的HTTP作为其应用的承载,而HTTP依然使用TCP作为其数据的承载(事实上,没有标准规定HTTP一定必须使用TCP来承载)。
然而,之前在PC端发生的故事并没有在手机端延续下来,这让人不禁感叹,这世界变化快。各种资源逐渐丰盈起来,CPU和内存价格不断降低,使得一部千元智能手机的性能可以秒杀十几年前的PC,而带宽资源的丰盈以及资费的逐渐降低使得用3G,4G网络在路上在车上看视频成了一种时尚,现如今,抖音短视频这是迎合了这股波峰。
旧瓶装新酒还可能吗?
可能已经不适合了。相比Telnet和简单文件传输,我们现在面对很多问题:
音视频业务,只要关键帧不丢太多,是允许丢包的,滑动窗口并不必要;
同一个URL回传的文件太多,TCP丢包会造成滑动窗口阻滞,队头拥塞;
Web页面交互和文件传输以及视频播放同时进行;
…
这并不怪TCP,因为TCP天生就不是为多路复用场景而设计的。
是要革命的时候了。
不过这一次,Google的做法是直接从应用本身入手,这就催生了HTTP2.0,作为一个大版本的升级,HTTP2.0相比HTTP1.x而言的变化可不是一点两点,推荐一本书《HTTP/2基础教程》,本文不再赘述。
与此同时,Google将HTTP2.0的一些特征抽象出来,形成了QUIC,如果说TCP是专门为Telnet和单独文件传输而被催生的,那么QUIC就是为HTTP2.0而被催生的,舞台和故事的情节依然没变,只是换了演员和道具。HTTP更换了TCP。
主角QUIC闪亮登场!
毕竟是HTTP2.0的嫡系,且看QUIC是如何快刀切割TCP在HTTP2.0下所面临的问题的。
多路复用问题
在大的框架下,QUIC从设计之初就是多路复用的,一个QUIC连接可以包含多个虚拟的Stream,虽然在物理上所有的Stream在同一个连接上交错传输,但在逻辑上每一个Stream都有自己的序列号命名空间,其按序性和可靠性独立控制,彻底解决了队头拥塞问题。
每一个Stream都有自己的包序列号空间,如果Stream 1的某个包没有按序到达,那么只需要停等该Stream的按序包即可,其它的按序到达的Stream依然可以继续向上层交付。
One thing to keep in mind is HTTP2.0面对的是多路复用问题,并且Stream本身就是HTTP2.0里的概念。
拥塞控制问题
QUIC解决TCP拥塞控制各种问题的手段有两个及其重要的:
完全的时间序发送(导致完全的选择确认)
确认的不可变更
我们知道,TCP发包时是按空间序进行的,即将包的序列号编码到数据序列本身,比如一个文件的字节序列是什么样的,该文件被传输时的包序列号就是什么样的。虽然这种实现方式非常直观,但是这会带来一个问题,即无法应对网络的不稳定性,虽然发送端一厢情愿地希望一个序列按照序列本身的样子被传输,但是网络环境往往并非如你所愿,一旦发送丢包,乱序,发送端就不得不采用激进且莽撞的方式去应对。比如,已经发出了序号10的包,然而序号为5的包超时了,于是重发了序号为5的包,这意味着有一个更小序号的包在更大序号10之后发出了,空间序被时间所打乱,如果此时收到了序号为5的包确认,将无法区分该确认是针对哪个数据包的。这意味着TCP的RTT无论如何都是无法测准的。
有人问。BBR算法甚至在BBR之前引入的RACK机制不就是时间序发包吗?熟悉代码的朋友可以看看tcp_sock结构体的delivered字段的含义。
看看TCP的协议头就会知道,即便发送端再渴望采用什么时间序,最终TCP头里带回来的还只是一个积累确认和不多的几个选择确认(而且还要双方均支持且开启!),这是个不全面的算法。
QUIC协议里说:对于发送端每发送一个数据包,不管是新包还是重传包,均为其编码一个新的基于Stream包序列空间的单调递增的序列号,这意味着确认必须完全是选择确认,不可能是积累确认。如果发送端发出了2,3,4,5序列,然后接收端的确认是3,5,那么,发送端会将2,4重新编码为6,7发送之,数据包在Stream本身的按序性则由Stream帧本身的offset字段来保证,与传输行为完全无关!
有了这个时间序特征,发送端便完全可以区分原始包和重传包了,进而得到了更精确的RTT。同时,完全的选择确认可以让发送端获取精确的带宽预估数据,这将使得为拥塞控制而进行的测量将会是准确的,而非猜测的。
当然,它也有弊端。QUIC的完全时间序决定了其完全选择确认,这意味着缺失了积累确认后,接收端的所有确认必须是不可反悔不可抵赖的,即如果选择确认了某个包,那就不能过一段时间说没有确认这个包。这意味着在乱序度很高的网络上,对端主机将会消耗大量内存来存储ofo(out of order)数据包。这就是消耗内存,时间导致的乱序必须由空间来弥补,空间换时间。
总而言之,QUIC将“可靠”和“按序”的处理相分离了,分离解耦的双方便可以采用不同的策略了。
为什么TCP当初没有采用这个策略呢?在资源匮乏的年代,停等对于Telnet和文件传输不是挺好的吗?
连接迁移问题
请看这个:
OpenVPN移动性改造-靠新的session iD而不是IP/Port识别客户端:https://blog.csdn.net/dog250/article/details/29180765
QUIC也是类似的机制。这便解决了TCP和应用强耦合的关系,只要TCP断开,应用也要断开。
滑动窗口问题
严格来讲,QUIC并不使用严格的基于严格包序列的滑动窗口,而是使用基于最大被确认包序列的窗口,这有一点基于数量而不是序列的窗口的意思。毕竟在内存已经不值钱的时代,offset最大的包都被确认了,就意味着可以继续发更大offset的包,至于之前没收到的,无非就是丢了或者乱序迟到了而已,重发即可。
这种流控策略区分了主次,即大部队先行,留下一小拨人为伤亡者善后即可,解除了单独一个Stream的队头拥塞问题,这个和多路复用一起,便解决了所有的队头拥塞问题。
安全性问题和0RTT问题
和TCP不同,QUIC协议甚至协议包头都是加密且被认证的,和明文的TCP不同,QUIC甚至可以实现0RTT的握手协商,怎么做到的呢?
QUIC协议是如何做到0RTT加密传输的(addons):https://blog.csdn.net/dog250/article/details/80935534
QUIC,一个基于UDP的实现在用户态的非常快的QUIC,如果看到了QUIC的特性,那么也许现在就是摒弃TCP的时候了!
由此看来,既然QUIC如此优秀,TCP存在的意义除了保护既有的兼容性投资之外,还剩下什么呢?TCP过时了。但现在大多数的应用依然在使用TCP。
本文内容转载至dog250
https://blog.csdn.net/dog250/article/details/80948185
以上是关于从Telnet到TCP,从HTTP2.0到QUIC的主要内容,如果未能解决你的问题,请参考以下文章