草庐IT

详解HTTP协议版本(HTTP/1.0、1.1、2.0、3.0区别)

-YIN 2023-05-01 原文

HTTP/1.0、/1.1、/2.0、/3.0区别

HTTP1.0

HTTP/1.0是无状态、无连接的应用层协议。

无连接

  无连接:每次请求都要建立连接,需要使用 keep-alive 参数建立长连接、HTTP1.1默认长连接keep-alive
  无法复用连接,每次发送请求都要进行TCP连接,TCP的连接释放都比较费事,会导致网络利用率低

队头阻塞

  队头阻塞(head of line blocking),由于HTTP1.0规定下一个请求必须在前一个请求响应到达之前才能发送,假设前一个请求响应一直不到达,那么下一个请求就不发送,后面的请求就阻塞了。

缓存

在HTTP1.0中主要使用header里的协商缓存 last-modified\if-modified-since,强缓存 Expires来做为缓存判断的标准

If-Modified-Since

Expires是RFC 2616(HTTP/1.0)协议中和网页缓存相关字段。用来控制缓存的失效日期。

Expires 字段声明了一个网页或 URL 地址不再被浏览器缓存的时间,一旦超过了这个时间,浏览器都应该联系原始服务器。RFC告诉我们:“由于推断的失效时间也许会降低语义透明度,应该被谨慎使用,同时我们鼓励原始服务器尽可能提供确切的失效时间。”

其他问题
  HOST域:认为每个服务器绑定唯一一个IP地址,因此在请求消息的URL中没有主机名,HTTP1.0没有host域。而现在在一台服务器上可以存在多个虚拟主机,并且它们共享一个IP地址。

  HTTP1.0不支持断点续传功能,每次都会传送全部的页面和数据。如果只需要部分数据就会浪费多余带宽



HTTP/1.1

特点

  1. 简单
    HTTP 基本的报文格式就是 header + body,头部信息也是 key-value 简单文本的形式,易于理解

  2. 灵活、易扩展
    各类请求方法、URL、状态码,等每个组成都没有固定死,开发者可以自定义与扩充
    HTTP在应用层其下层可以灵活变化(https就是HTTP与TCP之间增加SSL/TSL安全传输协议)

  3. 应用广泛、支持跨平台

优缺点

  1. 无状态
    好处:服务器不用额外资源记录,减轻服务器负担,提高CPU内存利用效率
    坏处:每次都要确认验证信息;一般通过Cookie解决(Cookie 通过在请求和响应报文中写入 Cookie 信息来控制客户端的状态。)
  2. 明文传输: 传输过程中信息可以抓包直接获取,信息暴露、安全性差
  3. 不安全:
    通信使用明文传输、信息泄露
    不验证通信双方身份、有可能进入伪装网站
    无法证明报文完整性都导致不安全的问题

解决方式:可以用 HTTPS 的方式解决,也就是通过引入 SSL/TLS 层,使得更安全。

长连接

为了解决早期HTTP/1.0每次都要建立连接导致通信效率低的性能问题,因为HTTP基于TCP/IP协议
为了解决上述 TCP 连接问题,HTTP/1.1 提出了长连接的通信方式,也叫持久连接。这种方式的好处在于减少了 TCP 连接的重复建立和断开所造成的额外开销,减轻了服务器端的负载。

持久连接的特点是,只要任意一端没有明确提出断开连接,则保持 TCP 连接状态。如果某个 HTTP 长连接超过一定时间没有任何数据交互,服务端就会主动断开这个连接。

短连接和长连接对比

管道传输

因为HTTP/1.1 采用了长连接的方式,这使得管道(pipeline)网络传输成为了可能。

即可在同一个 TCP 连接里面,客户端可以发起多个请求,只要第一个请求发出去了,不必等其回来,就可以发第二个请求出去,可以减少整体的响应时间。
但是服务器必须按照接收请求的顺序发送对这些管道化请求的响应

如果服务端在处理 A 请求时耗时比较长,那么后续的请求的处理都会被阻塞住,这称为「队头堵塞」。

所以,HTTP/1.1 管道解决了请求的队头阻塞,但是没有解决响应的队头阻塞。

注意:实际上 HTTP/1.1 管道化技术不是默认开启,而且浏览器基本都没有支持,所以后面讨论HTTP/1.1 都是建立在没有使用管道化的前提。

HTTP/1.0 比较 HTTP/1.1

HTTP/1.1 相比 HTTP/1.0 提高了什么性能?

  1. 使用长连接的方式改善了 HTTP/1.0 短连接造成的性能开销。
  2. 支持管道(pipeline)网络传输,只要第一个请求发出去了,不必等其回来,就可以发第二个请求出去,可以减少整体的响应时间


HTTP协议层次结构图

现在主流浏览器大部分使用的都是HTTP/1.1协议,也有部分支持HTTP/2.0;绝大部分网站都升级为HTTPS更保证安全性

HTTP/2.0

详见该文章:深入理解HTTP/2.0
HTTP/2.0协议是基于HTTPS的,更加安全

相比与HTTP/1.1,HTTP/2.0增加如下几点的重大优化

头部压缩

HTTP2.0会压缩(Header)部分;如果同时多个请求其头部一样或相似,那么协议会消除重复部分。
利用HPAK算法:在客户端和服务器同时维护一张头信息表,所有字段都会存入这个表,生成一个索引号,就不用重复发送同样字段了,只发送索引号,减少数据量提高速度

二进制格式

HTTP/1.0和HTTP/1.1中,报文都是纯文本的格式简单易读;而在2.0中采用了二进制的格式
报头和数据体称为:帧(frame)-》头信息帧(Headers Frame)和数据帧(Data Frame)

文本形式信息保存为一个一个字符,占用空间多,每个字符对应比特位多,接受方还需要将报文转换为二进制,而直接用二进制减少了传输数据量,提高数据传输效率

1.0:
2.0:

数据以数据流(stream)的形式以字节单位发送,数据包可以不按顺序发送

在 HTTP/2 中每个请求或响应的所有数据包,称为一个数据流(Stream)。每个数据流都标记着一个独一无二的编号(Stream ID);
所有HTTP2.0通信都在一个TCP链接上完成,这个链接可以承载任意流量的双向数据流。每个数据流以消息的形式发送,而消息由一或多个帧组成。不同 Stream 的帧是可以乱序发送的(因此可以并发不同的 Stream ),因为每个帧的头部会携带 Stream ID 信息,所以接收端可以通过 Stream ID 有序组装成 HTTP 消息

客户端还可以指定数据流的优先级。优先级高的请求,服务器就先响应该请求

多路复用

HTTP2.0实现了真正的并行传输,它能够在一个TCP上进行任意数量的HTTP请求,由于其二进制分帧特性

HTTP/2 是可以在一个连接中并发多个请求或回应,而不用按照顺序一一对应。

移除了 HTTP/1.1 中的串行请求,不需要排队等待,彻底解决「队头阻塞」问题,降低了延迟,大幅度提高了连接的利用率。

服务端推送

HTTP/2 还在一定程度上改善了传统的「请求 - 应答」工作模式,服务端不再是被动地响应,可以主动向客户端发送消息、推送额外的资源

例如:

TCP导致队头阻塞

因为TCP面向字节流传输,而且保证传输可靠性和数据的完整性
只有TCP拿到完整连续的数据时,内核才会将数据从缓冲区交给HTTP应用,而只要前一个字节没有收到,HTTP就无法从内核缓冲区中得到数据,直到其到达,所以在此过程仍然会导致队头阻塞

图中发送方发送了很多个 packet,每个 packet 都有自己的序号,你可以认为是 TCP 的序列号,其中 packet 3 在网络中丢失了,即使 packet 4-6 被接收方收到后,由于内核中的 TCP 数据不是连续的,于是接收方的应用层就无法从内核中读取到,只有等到 packet 3 重传后,接收方的应用层才可以从内核中读取到数据,这就是 HTTP/2 的队头阻塞问题,是在 TCP 层面发生的。

因此如果出现丢包就会触发TCP的超时重传,这样后续缓冲队列中所有数据都得等丢了的重传

HTTP/3.0

(仍在学习中…后续完善)

基于Google的QUIC,HTTP3 背后的主要思想是放弃 TCP,转而使用基于 UDP 的 QUIC 协议。

为了解决HTTP/2.0中TCP造成的队头阻塞问题,HTTP/3.0直接放弃使用TCP,将传输层协议改成UDP;但是因为UDP是不可靠传输,所以这就需要QUIC实现可靠机制

QUIC 也是需要三次握手来建立连接的,主要目的是为了确定连接 ID。

可以学一下这篇文章:QUIC详解(用UDP实现可靠协议)

在 UDP 报文头部与 HTTP 消息之间,共有 3 层头部:

QUIC特点:

无队头阻塞

QUIC 协议也有类似 HTTP/2 Stream 与多路复用的概念,也是可以在同一条连接上并发传输多个 Stream,Stream 可以认为就是一条 HTTP 请求。

QUIC 有自己的一套机制可以保证传输的可靠性的。当某个流发生丢包时,只会阻塞这个流,其他流不会受到影响,因此不存在队头阻塞问题。这与 HTTP/2 不同,HTTP/2 只要某个流中的数据包丢失了,其他流也会因此受影响。

所以,QUIC 连接上的多个 Stream 之间并没有依赖,都是独立的,某个流发生丢包了,只会影响该流,其他流不受影响。

连接建立

HTTP/3 在传输数据前虽然需要 QUIC 协议握手,这个握手过程只需要 1 RTT,握手的目的是为确认双方的「连接 ID」,连接迁移就是基于连接 ID 实现的。

连接迁移

基于 TCP 传输协议的 HTTP 协议,由于是通过四元组(源 IP、源端口、目的 IP、目的端口)确定一条 TCP 连接,例如设备要连接wifi(IP地址改变)就必须要重新建立连接,而建立连接包含TCP三次握手和TSL四次握手,以及TCP慢启动所以会造成使用者卡顿的感觉

而QUIC通过连接ID标记自己,客户端和服务器可以各自选择一组 ID 来标记自己,因此即使移动设备的网络变化后,导致 IP 地址变化了,只要有上下文信息(比如连接 ID、TLS 密钥等),就可以“无缝”地复用原连接,消除重连的成本,没有丝毫卡顿感,达到了连接迁移的功能。

其实, QUIC 是一个在 UDP 之上的伪 TCP + TLS + HTTP/2 的多路复用的协议。






部分图片来源《图解HTTP》、《图解网络——小林coding》

有关详解HTTP协议版本(HTTP/1.0、1.1、2.0、3.0区别)的更多相关文章

  1. ruby - 如何模拟 Net::HTTP::Post? - 2

    是的,我知道最好使用webmock,但我想知道如何在RSpec中模拟此方法:defmethod_to_testurl=URI.parseurireq=Net::HTTP::Post.newurl.pathres=Net::HTTP.start(url.host,url.port)do|http|http.requestreq,foo:1endresend这是RSpec:let(:uri){'http://example.com'}specify'HTTPcall'dohttp=mock:httpNet::HTTP.stub!(:start).and_yieldhttphttp.shou

  2. ruby-on-rails - 项目升级后 Pow 不会更改 ruby​​ 版本 - 2

    我在我的Rails项目中使用Pow和powifygem。现在我尝试升级我的ruby​​版本(从1.9.3到2.0.0,我使用RVM)当我切换ruby​​版本、安装所有gem依赖项时,我通过运行railss并访问localhost:3000确保该应用程序正常运行以前,我通过使用pow访问http://my_app.dev来浏览我的应用程序。升级后,由于错误Bundler::RubyVersionMismatch:YourRubyversionis1.9.3,butyourGemfilespecified2.0.0,此url不起作用我尝试过的:重新创建pow应用程序重启pow服务器更新战俘

  3. ruby-on-rails - 在 ruby​​ .gemspec 文件中,如何指定依赖项的多个版本? - 2

    我正在尝试修改当前依赖于定义为activeresource的gem:s.add_dependency"activeresource","~>3.0"为了让gem与Rails4一起工作,我需要扩展依赖关系以与activeresource的版本3或4一起工作。我不想简单地添加以下内容,因为它可能会在以后引起问题:s.add_dependency"activeresource",">=3.0"有没有办法指定可接受版本的列表?~>3.0还是~>4.0? 最佳答案 根据thedocumentation,如果你想要3到4之间的所有版本,你可以这

  4. ruby - 触发器 ruby​​ 中 3 点范围运算符和 2 点范围运算符的区别 - 2

    请帮助我理解范围运算符...和..之间的区别,作为Ruby中使用的“触发器”。这是PragmaticProgrammersguidetoRuby中的一个示例:a=(11..20).collect{|i|(i%4==0)..(i%3==0)?i:nil}返回:[nil,12,nil,nil,nil,16,17,18,nil,20]还有:a=(11..20).collect{|i|(i%4==0)...(i%3==0)?i:nil}返回:[nil,12,13,14,15,16,17,18,nil,20] 最佳答案 触发器(又名f/f)是

  5. ruby-on-rails - `a ||= b` 和 `a = b if a.nil 之间的区别? - 2

    我正在检查一个Rails项目。在ERubyHTML模板页面上,我看到了这样几行:我不明白为什么不这样写:在这种情况下,||=和ifnil?有什么区别? 最佳答案 在这种特殊情况下没有区别,但可能是出于习惯。每当我看到nil?被使用时,它几乎总是使用不当。在Ruby中,很少有东西在逻辑上是假的,只有文字false和nil是。这意味着像if(!x.nil?)这样的代码几乎总是更好地表示为if(x)除非期望x可能是文字false。我会将其切换为||=false,因为它具有相同的结果,但这在很大程度上取决于偏好。唯一的缺点是赋值会在每次运行

  6. ruby-on-rails - 如果我将 ruby​​ 版本 2.5.1 与 rails 版本 2.3.18 一起使用会怎样? - 2

    如果我使用ruby​​版本2.5.1和Rails版本2.3.18会怎样?我有基于rails2.3.18和ruby​​1.9.2p320构建的rails应用程序,我只想升级ruby的版本,而不是rails,这可能吗?我必须面对哪些挑战? 最佳答案 GitHub维护apublicfork它有针对旧Rails版本的分支,有各种变化,它们一直在运行。有一段时间,他们在较新的Ruby版本上运行较旧的Rails版本,而不是最初支持的版本,因此您可能会发现一些关于需要向后移植的有用提示。不过,他们现在已经有几年没有使用2.3了,所以充其量只能让更

  7. ruby-on-rails - 获取 inf-ruby 以使用 ruby​​ 版本管理器 (rvm) - 2

    我安装了ruby​​版本管理器,并将RVM安装的ruby​​实现设置为默认值,这样'哪个ruby'显示'~/.rvm/ruby-1.8.6-p383/bin/ruby'但是当我在emacs中打开inf-ruby缓冲区时,它使用安装在/usr/bin中的ruby​​。有没有办法让emacs像shell一样尊重ruby​​的路径?谢谢! 最佳答案 我创建了一个emacs扩展来将rvm集成到emacs中。如果您有兴趣,可以在这里获取:http://github.com/senny/rvm.el

  8. ruby - 这两个 Ruby 类初始化定义有什么区别? - 2

    我正在阅读一本关于Ruby的书,作者在编写类初始化定义时使用的形式与他在本书前几节中使用的形式略有不同。它看起来像这样:classTicketattr_accessor:venue,:datedefinitialize(venue,date)self.venue=venueself.date=dateendend在本书的前几节中,它的定义如下:classTicketattr_accessor:venue,:datedefinitialize(venue,date)@venue=venue@date=dateendend在第一个示例中使用setter方法与在第二个示例中使用实例变量之间是

  9. ruby - Net::HTTP 获取源代码和状态 - 2

    我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur

  10. ruby-on-rails - 如何在 Ruby on Rails 中实现由 JSF 2.0 (Primefaces) 驱动的 UI 魔法 - 2

    按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭10年前。问题1)我想知道ruby​​onrails是否有功能类似于primefaces的gem。我问的原因是如果您使用primefaces(http://www.primefaces.org/showcase-labs/ui/home.jsf),开发人员无需担心javascript或jquery的东西。据我所知,JSF是一个规范,基于规范的各种可用实现,prim

随机推荐