HTTP/HTTPS

HTTP 几个要点

URL

URL 中包含服务器域名、文件路径、收件人地址、用户名、密码等。URL 想表达的是

  • 所使用的协议: HTTP、FTP、FILE 等
  • 用户名密码可选
  • 所需访问或下载文件的路径

HTTP 协议定义了客户端和服务器之间交互的消息内容和步骤,即请求的信息包括了 请求啥 以及 你要进行什么操作

方法(要进行什么操作)

有 GET、POST、HEAD、OPTIONS、PUT、DELETE、TRACE、CONNECT 等

  • GET:在请求消息中表明使用 Get 方法,然后在URI 中表明文件名。服务器收到消息后,会把数据存放于相应消息中并返回给客户端

  • POST:当我们在购物填写地址信息或者填写问卷信息时,将内容填写到表格中,然后点击提交这个过程,实际上通常是采用 POST 方式。服务器收到请求数据后发送给 URI 指定的应用程序,然后服务端获取应用程序的执行结果,并在响应信息中返回给客户端。对于客户端而言,并不知道到底是不是想要的结果,所以服务端会在响应头中用一个状态码表示操作的结果是成功还是失败,比如 200 表示成功,404 可能为还没找到文件

请求报文

主要包含请求行、请求体、报文主体

请求体一般有如下类型:

  • Content-Encoding: 当消息体经过压缩等编码处理时,表示其编码格式
  • Content-Length: 表示消息体的长度
  • Content-Type: 消息体的数据类型
  • Expires: 消息体的有效期

头字段注意事项:

  • 字段名不区分大小写
  • 字段名不允许有空格,可以使用连字符“-”,但不能用下划线
  • 字段名后面紧接着“:”,不能有空格,“:” 后面的字段可有多个空格
  • 字段顺序无意义,不影响语义
  • 字段原则上不能重复,除非这个字段本身的语义允许,例如 Set-Cookie

HTTP 的 body 通常分为这几种类别:text、audio/video、application(文本或者二进制,交给上层应用处理)、image(图像文件),但是带宽一定,数据大了通常考虑使用压缩算法进行压缩,在 HTTP 中使用 Encoding-type 表示,常用的压缩方式有 gzip、deflate、br 等

问题

断点续传怎么操作?

  1. 查看服务器是否支持范围请求并记录文件大小
  2. 多个线程分别负责不同的 range
  3. 下载同时记录进度,即使因为网络等原因中断也没事, Range 请求剩余即可
HTTP 响应

响应的内容和请求消息的内容类似。不过响应的第一行内容为状态码,表示执行结果是否成功。

常见的状态码如下:

1XX:处于中间状态,还需后续处理

2XX:成功

  • 200: OK,常见的成功状态码
  • 204: No Content,同 200,不过响应头中没有 body 数据
  • 206: Partial Content,是 HTTP 分块下载或断点续传的基础,在客户端发送“范围请求”、要求获取资源的部分数据时出现,同 200 一样,不过 body 里的数据不是资源的全部,而是其中一部分。状态码 206 通常会伴随着头字段 "Content-Range" ,表示响应报文里数据的具体范围,供客户端确认,如: Content-Range: bytes 0-99/500,表示此次获取的共计 5000 个字节的前 100 个字节

3XX:重定向到其他资源

  • 301: Moved Permanently,永久重定向,即本地请求的资源不存在,使用新的 URI 再次访问

  • 302: Moved Temporarily,临时重定向,即所请求的资源暂时还在,但是需要用另一个 URI 访问

    301 与 302 通过字段 Location 中表明需要跳转的 URI,两者的不同在于一个是临时改变,一个是永久改变。例如,需要将网站全部升级为 HTTPS,这种永久性改变就需要配置永久的 301;有时晚上更新系统,系统暂时不可用,可配置 302 临时访问,此时不会做缓存优化,第二天还会访问原来的地址

  • 304 Not Modified,用于缓存控制。客户端在请求一个文件时,发现自己缓存的文件有 Last Modified,那么在请求中会包含 If Modified Since,这个时间就是缓存文件的 Last Modified。服务端只要判断这个时间与当前请求时的文件的修改时间就可以确定是返回 304 还是 200

4XX: 请求报文有误,服务器无法处理

  • 400 Bad Request,通用错误码,表示请求报文有错误。但这个错误过于笼统,不知道客户端是哪里的错误,所以实际应用中通常会返回含有明确含义的状态码
  • 403 Forbidden:服务器禁止访问资源,原因比如涉及敏感词汇、法律禁止等
  • 404 Not Found:想要的资源在本地未找到从而无法提供给客户端。或者服务器无法回应且未知原因
  • 405 Method Not Allowed:获取资源的方法好几种,例如不允许 POST 时使用了 POST
  • 406 Not Acceptable: 服务端无法满足客户端请求的条件,例如请求需要中文但只有英文
  • 408 Request Timeout:请求超时,服务器等待了过长的时间
  • 429 Too Many Requests: 客户端发送了太多的请求,通常是服务器的限连策略

5XX:服务端错误

  • 500 Internal Server Error: 与 400 类似,属于一个通用的错误码,但是服务器到底是什么错误我们不得而知
  • 502 Bad Gateway:通常是服务器作为网关或者代理时返回的错误码,表示服务器工作正常,访问后段服务器发生了错误
  • 503 Service Unavailable:表示服务器当前忙,暂时无法响应服务。503 是一个临时的状态,在响应报文中的 Retry-After 字段,指示客户端可以在多久以后再次尝试发送请求

响应信息返回后显示在屏幕中,如果为纯文字,到此就结束了。如果有图片、视频、音频等信息,浏览器会从响应信息中的文字搜索相应的标签,如果有图片等其他信息,则再次请求服务器,按照相应的文件名向服务器发送请求并显示在刚才预留的空间中。

整个 HTTP 响应的简化版如图
![![v2-0b02fa4a73a8072eb03cdf78270235e1_1440w.png](https://upload-images.jianshu.io/upload_images/1322408-f59ab102ed8d089b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ](https://upload-images.jianshu.io/upload_images/1322408-0e7d75257274ec96.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

Cookie 与 Session

HTTP 是无状态的、无记忆的,Cookie 机制的出现让其有记忆功能。

例如登陆淘宝时,看到一个商品点进去,进行了页面跳转/刷新,按照 HTTP 的无状态协议岂不是又要登陆一次?为了解决类似问题,Cookie 技术通过在请求和响应报文中写入 Cookie 信息来控制客户端的状态。 Cookie 会根据从服务端发送的响应报文里 Set-Cookie 的首部字段信息,通知客户端保存 Cookie,当下次客户端再往该服务器发送请求时,会自动在请求报文中加入 Cookie 值发送出去。

Session 管理以及 Cookie 状态管理

  1. 客户端把用户 ID 和 密码等登陆信息放入报文实体部分,通常以 POST 方法把请求发送给服务器,此时会使用 HTTPS 通信来进行 HTML 表单画面的显示和用户输入数据的发送
  2. 服务器会发送用以识别用户的 Session ID。通过验证客户端发送过来的登陆信息进行身份验证,然后把用户的认证状态与 Session ID 绑定后记录在服务端。向客户端返回响应时,会在首部字段 Set-Cookie 内写入 Session ID
  3. 客户端接收到从服务端发来的 Session ID 后,会将其作为 Cookie 保存在本地,下次向服务器发送请求时,浏览器会自动发送 Cookie,所以 Session ID 也随之发送到服务器,服务端可通过验证接收到的 Session ID 识别用户和其认证状态。
![v2-07ededff1d415c1fa2db3fd89378eda0_1440w.jpg](https://upload-images.jianshu.io/upload_images/1322408-ad31811c72a9cdb6.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

代理

代理作为中间位置,相对于请求方为服务端,相对于后端服务端为请求方。代理常见的功能为负载均衡。代理需要区分正向代理与反向代理。两者的区别在于代理的对象不一样:正向代理代理的对象是客户端,反向代理代理的对象是服务端

正向代理

正向代理隐藏了真实的请求客户端,服务端不知道真实的客户端是谁,客户端请求的服务都被代理服务器代理来请求。例如某些科学上网工具。浏览器访问 google 时,被 block,于是可以在国外搭建一台代理服务器,让代理帮我去请求 google,代理把请求返回的相应结果再返回给我

反向代理

反向代理隐藏了真实的服务端。当我们请求 www.baidu.com 的时候,就像拨打 10010 一样,背后可能有成千上万台服务器为我们服务,但具体是哪一台,你不知道,只需要知道反向代理服务器是谁就好了。www.baidu.com 就是我们的反向代理服务器。反向代理服务器会帮我们把请求转发到真实的服务器那里去。

TCP 三次握手和四次挥手

TCP 工作在全双工模式,它可以同时进行双向数据传输。

三次握手

三次握手其实是建立一个 TCP 连接时,需要客户端和服务端总共发送 3 个包。进行三次握手的作用是为了确认双方的接收能力和发送能力是否正常、指定自己的初始化序列号为后面的可靠性传送作准备。实质上就是连接服务器指定端口、建立 TCP 连接,并同步连接双方的序列号和确认号,交换 TCP 窗口大小

刚开始客户端处于 closed 状态,服务端处于 Listen 状态。进行三次握手

  1. 第一次握手:客户端给服务端发送一个 SYN 报文,并指明客户端的初始化序列号ISN。

    首部的同步位 SYN=1,初始序号 seq=x,SYN=1 的报文段不能携带数据,但要消耗掉一个序号

  2. 第二次握手:服务器收到客户端的 SYN 报文后,会以自己的 SYN 报文作为应答,并且也指定了自己的初始化序列号 ISN。同时会把客户端的 ISN+1 作为 ACK 的值,表示自己已经收到了客户端的 SYN。

    在确认报文中,SYN=1,ACK=1,确认号ack=x+1,初始序列号seq=y

  3. 第三次握手:客户端收到 SYN 报文后,会发送一个 ACK 报文,当然也是一样把服务器的 ISN+1 作为 ACK 值,表示已经收到了服务端的 SYN 报文。

    确认报文段 ACK=1,确认号ack=y+1,序列号seq=x+1(初始位 seq=x,第二个报文段所以要+1),ACK 报文段可以携带数据,不懈怠数据则不消耗序号

在 socket 编程中,客户端执行 connect 时,将触发三次握手

为什么需要三次握手,两次不行吗?

三次握手的目的:

  • 第一次握手:客户端发送网络包,服务端收到了。这样服务端得出结论:客户端的发送能力、服务端的接收能力是正常的
  • 第二次握手:服务端发包,客户端收到了。这样客户端得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的。不过此时服务端并不能确认客户端的接收能力是否正常
  • 第三次握手:客户端发包,服务端收到了。这样服务端得出结论:客户端的接收能力正常

因此,需要三次握手才能确认双方的接收、发送能力是否正常。

试想,如果两次握手,则会出现下面这种情况:

如果客户端发出连接请求,但因连接请求报文丢失而未收到确认,于是客户端再重传一次连接请求。后来收到了确认,建立了连接。数据传输完毕就释放了连接。客户端共发出了两个连接请求报文段,第一个丢失,第二个到达了服务端。如果第一个丢失的报文段只是在某些网络节点长时间滞留了,延误到了连接释放以后的某个时间才到达服务端,此时服务端误认为客户端又发出一次新的连接请求,于是就向客户端发出确认报文段,同意建立连接。不采用三次握手,只要服务端发出确认,就建立新的连接了,此时客户端忽略服务端发来的确认,也不发送数据,则服务端一直在等待客户端发送数据,浪费资源

序列号和确认号的作用

TCP 协议工作在 OSI 的传输层,是一种可靠的面向连接的数据流协议,TCP 之所以可靠,是因为它保证了传送数据包的顺序,顺序是用序列号来保证的。响应包内也包括一个序列号,表示接收方准备好这个序列号的包。在 TCP 传送一个数据包时,它会把这个数据包放入重发队列中,同时启动计时器,如果收到了关于这个包的确认信息,便将此数据包从队列中删除,如果在计时器超时的时候仍然没有收到确认信息,则需要重新发送该数据包。另外,TCP 通过数据分段中的序列号来保证所有传输的数据可以按照正常的顺序进行重组,从而保证数据传输的完整。

什么是半连接队列

服务器第一次收到客户端的 SYN 之后,此时双方还没有完全建立起连接,服务器会把这种状态下的请求连接放在一个队列里,这个队列就是半连接队列。当建立起连接后,就会将连接放到全连接队列里。

补充一点关于 SYN-ACK 重传次数的问题:服务器发送完 SYN_ACK 包,如果未收到客户确认包,服务器进行首次重传,等待一段时间仍未收到客户端确认包,会进行第二次重传;如果重传次数超过系统规定的最大重传次数,系统将该连接信息从半连接队列中删除。注意,每次重传等待时间不一定相同,一般按照指数增常,如1s,2s,4s,8s……

ISN(Initial Sequence Number) 是固定的吗?

当一端为建立连接而发送它的 SYN 时,它为连接选择一个初始序号。ISN 随时间而变化,因此每个连接都将具有不同的 ISN。ISN 可以看作是一个32比特的计数器,每 4ms 加 1。这样选择序号的目的在于防止在网络中被延迟的分组在以后又被传送,从而导致某个连接的一方对它做错误的解释。

三次握手的其中一个重要功能是客户端和服务端交换 ISN,以便让对方知道接下来接收数据时如何按序列号组装数据。如果 ISN 是固定的,攻击者很容易猜出后续的确认号。

三次握手过程中可以携带数据吗?

第三次握手的时候可以。第一次、第二次不可以。

如果第一次握手可以携带数据的话,如果有人要恶意攻击服务器,那他没次都在第一次握手中的 SYN 报文中放入大量数据。因为攻击者不理服务器接收、发送能力是否正常,疯狂重复发 SYN 报文,这会让服务器花费很多时间、内存空间来接收这些报文。

第三次的话,对于客户端来说,它已经建立起连接了,并且也知道服务器的接收、发送能力是正常的,所以可以携带数据

SYN 攻击是什么?

SYN 攻击是客户端在短时间内伪造大量不存在的 IP 地址,并向 Server 不断地发送 SYN 包,服务端则回复确认包,并等待客户端确认,由于源地址不存在,因此 Server 需要不断地重发至超时。这些伪造的 SYN 包将长时间占用半连接队列,导致正常的 SYN 请求因为队列满而被丢弃,从而引起网络拥塞甚至系统瘫痪

常见的防御 SYN 攻击的方法有如下几种:

  • 缩短超时(SYN Timeout)时间
  • 增加最大半连接数
  • 过滤网关防护
  • SYN cookies 技术
四次挥手

建立一个连接需要三次握手,而终止一个连接要经过四次挥手。这由 TCP 的半关闭造成的。所谓的半关闭,其实就是 TCP 提供了连接的一段在结束它的发送后还能接收来自另一端数据的能力。

TCP 连接的拆除需要发送四个包,因此称为四次挥手,客户端或服务端均可主动发起挥手动作。

假如时客户端先发起关闭请求,四次挥手过程如下:

  1. 第一次挥手:客户端发送一个 FIN 报文,报文中会指定一个序列号。即客户端处于发出“连接释放报文段(FIN=1,序号seq=u)”,并停止再发送数据,主动关闭 TCP 连接,等待服务端的确认
  2. 第二次挥手:服务端收到 FIN 之后,会发送 ACK 报文,且把客户端的序列号值 +1 作为 ACK 报文的序列号值,表明已经收到客户端的报文了。即服务端收到连接释放报文段后即发出确认报文段(ACK=1,确认号ack=u+1,序号seq=v)。此时的 TCP 处于半关闭状态,客户端到服务端的连接释放。客户端收到服务端的确认后,开始等待服务端发出的来哪释放报文段。
  3. 第三次挥手:如果服务端也想断开连接了,和客户端的第一次挥手一样,发送 FIN 报文,且指定一个序列号。此时服务端没有要向客户端发出的数据,服务端发出的连接释放报文段(FIN=1,ACK=1,序号seq=w,确认号ack=u+1),服务端进入最后确认状态,等待客户端的确认
  4. 第四次挥手:客户端收到 FIN 之后,一样发送一个 ACK 报文作为应答,且把服务端的序列号值 +1 作为自己ACK 报文的序列号值。需要过一阵子以确保服务端收到自己的 ACK 报文之后才会进入 CLOSED 状态,服务端收到 ACK 报文之后,就关闭连接了。即客户端收到服务端的连接释放报文段后,对此发出确认报文段(ACK=1,seq=u+1,ack=w+1),此时 TCP 未释放掉,需要经过时间等待计时器设置的时间 2MSL 后,客户端才关闭。

收到一个 FIN 只意味着在这一方向上没有数据流动。客户端执行主动关闭时正常的,服务端通常执行被动关闭。

在 socket 编程中,任何一方执行 close() 操作即可产生挥手操作

挥手为什么需要四次

当服务端收到 FIN 报文时,很可能不会立即关闭 Socket,所以只能先回复 ACK 报文,告诉客户端:“你发送的 FIN 报文我收到了”。只有等服务端所有的报文发送完毕,才会发送 FIN 报文

2MSL 等待状态

TIME_WAIT 状态也称为 2MSL 等待状态。每个具体 TCP 实现必须选择一个报文段最大生存时间 MSL,它是任何报文段被丢弃前在网络内的最长时间。当 TCP 执行一个主动关闭,并发回最后一个 ACK,该连接必须在 TIME_WAIT 状态停留 2 倍的 MSL。这样可以让 TCP 再次发送最后的 ACK 以防这个 ACK 丢失(另一端超时并重发最后的 FIN)

HTTPS

HTTPS 在传输层和应用层中间加了一层 TLS。

HTTPS 请求建立连接过程

注意:

  1. client Hello 阶段,客户端向服务端提供以下信息

    • 支持的协议版本,比如 TLS 1.0 版本
    • 一个客户端生成的随机数,稍后用于生成“对话密钥”
    • 支持的加密算法,比如 RSA 公钥加密
    • 支持的压缩算法
  2. Server Hello 阶段。服务端收到客户端请求后,向客户端发出回应。服务器回应包含以下内容:

    • 确认使用的加密通信协议版本,比如 TLS 1.0 版本,如果浏览器与服务器支持的版本不一致,服务器关闭加密通信
    • 一个服务器生成的随机数,稍后用于生成对话密钥
    • 确认使用的加密方法,比如 RSA 公钥加密
    • 服务器证书

    除了上面这些信息,如果服务器需要确认客户端的身份,就会再包含一项请求,要求客户端提供“客户端证书”。比如金融机构往往只允许认证客户联入自己的网络,就会向正式客户提供 USB 密钥,里面就包含了一张客户端证书

  3. 浏览器收到服务器返回的证书,会验证证书的有效性,主要步骤如下:

    • 验证证书的有效期

    • 验证证书域名与浏览器地址中域名是否匹配

    • 验证证书吊销状态

    • 验证证书颁发机构

      如果上述步骤没能全部通过,就会显示警告

  4. 如果检查通过,客户端就会从证书中取出服务器的公钥,然后向服务器发送下面三项信息

    • 一个随机数。该随机数用服务器公钥加密,防止被窃听
    • 编码改变通知,表示随后的信息都将用双方商定的加密方法和密钥发送
    • 客户端握手结束通知,表示客户端的握手阶段已经结束。这一项同时也是前面发送的所有内容的 hash 值,用来供服务器校验
  5. 上面的第一项的随机数,是整个握手阶段出现的第三个随机数,又称“pre-master key”。此时客户端和服务器同时有了三个随机数,接着双方就用事先商定的加密方法,各自生成本次会话所用的同一把“会话密钥”。此外,如果前一步,服务器要求客户端证书,客户端会在这一步发送证书及相关信息

  6. 服务器的最后回应。服务器收到客户端的第三个随机数 pre-master key 后,计算生成本次会话所用的会话密钥,然后向客户端发送下面信息:

    • 编码改变通知,表示随后的信息将用双方商定的加密方法和密钥发送
    • 服务器握手结束通知,表示服务器的握手阶段已经结束。这一项同时也是前面发送的所有内容的 hash 值,用来供客户端校验

不同版本的 HTTP

0x01 HTTP/0.9

当时网络资源匮乏,0.9 版本采用纯文本格式,且设置为只读,是能使用 GET 的方式从服务器获得 HTML 文档,响应后关闭。响应中只包含了文档本身,响应内容无响应头、无错误码、无状态码。请求过程如下

GET /Mysite.html

<HTML>
Hello world
</HTML>
HTTP/1.0
0x02 HTTP/1.0

随着时代的进步,仅仅文本的传输无法满足需求,更多情况需要采图文的方式。 HTTP/1.0 在 1996 年诞生,增加了如下几个方面:

  • 之前只有 GET 方法,现在增加 POST、HEAD 方法
  • 加入协议版本号、同时增加文件处理类型
  • 加入 HTTP Header,让 HTTP 处理请求更加灵活
  • 增加响应状态码,标记出错的原因
  • 提供国际化(不同语言)支持

典型的请求过程

GET /image.html HTTP/1.0
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64)

200 OK
Date: Tue, 17 Nov 2020 09:15:31 GMT
Content-Type: text/html
<HTML> 
一个包含图片的页面
  <IMG SRC="/image.gif">
</HTML>
0x03 HTTP/1.1

1999 年,HTTP/1.1 发布成为标准,增加了如下几个方面:

  • 继续增加了 PUT 方法
  • 允许持久链接:随着文件越来越大,图片等信息越来越复杂,如果每一次上传下载文件都需要建立连接、断开连接的过程将增加大量的开销,为此提出了“持久连接”。也就是一次 TCP 连接可以具有多个 HTTP 请求。当然长连接可以选择的,如果考虑关闭,只需要使用 Connection:close 关闭即可
  • 强制要求 Host 头:大文件可切分为小文件发送

HTTP/1.1 有如下几个缺点:

  • 自带慢启动:慢启动从 0 到 1循循渐进。就像轿车启动后缓慢调节到适合的速度。缺点是:一个页面有静态数据,很多小文件在加载过程中就会直接发起请求,这样导致太多的请求都会经历慢启动过程,花费时间太多
  • 多条 TCP 连接宽带竞争:带宽固定,多条 TCP 连接同时发起竞争带宽资源,由于各个 TCP 连接之间没有通信机制,也无法得知哪些资源优先级更高,从而导致想快速下载的资源反而延迟下载
  • 头部阻塞:在 HTTP/1.1 中,虽然大家共用了一条 TCP 通道,但是第一个请求没有结束,第二请求就可能阻塞等待,即不能同时发送接收数据。如果一个网页有很多数据文件,如果能够同时发出请求,让部分数据文件能够得到响应并预处理就能大大利用了宽带和 cpu 资源。
0x04 HTTP/2.0

HTTP/2.0 是一个二进制协议,基于“帧”的结构设计,改进了很多 HTTP/1.1 痛点问题:

  • 多路复用的流
  • 头部压缩
  • 资源优先级和依赖设置
  • 服务器推送
  • 流量控制
  • 重置消息

HTTP/2.0 多路复用原理解析

什么是多路复用

我们用“HTTP 消息”来表示一个请求-响应的过程,那么 HTTP/1.1 中的消息是“管道串行化”:只有等一个消息完成后才能进行下一条消息;而 HTTP/2.0 中多个消息交织在一起,显著提高了“通信”的效率。这就是多路复用:在一个 HTTP 的连接上,多路 “HTTP 消息”同时工作

为什么 HTTP/1.1 不能实现“多路复用”

HTTP/2.0 是基于二进制“帧”的协议,HTTP/1.1 是基于“文本分割”解析的协议

看一个 HTTP/1.1 简单的 GET 请求例子

GET / HTTP/1.1
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding:gzip, deflate, br
Accept-Language:zh-CN,zh;q=0.9,en;q=0.8
Cache-Control:max-age=0
Connection:keep-alive
Host:www.imooc.com
Referer:https://www.imooc.com/
Upgrade-Insecure-Requests:1
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36

以上就是 HTTP/1.1 发送请求消息的文本格式:以换行符分割每一条 key:value 的内容,解析这种数据时,服务端需要不断的读入字节,知道遇到分隔符(这里指换行符,代码中可能使用 /n 或 /r/n 表示)。这种方式有如下问题:

  • 一次只能处理一个请求或响应,因为这种以分隔符分割消息的数据,在完成之前不能停止解析
  • 解析这种数据无法预知需要多少内存,这回带给服务端很大的压力,因为不知道要把一行要解析的内容读到多大的“缓冲区”中,在保证解析效率和速度的前提下:内存该如何分配?
HTTP/2.0 帧结构设计和多路复用实现

HTTP/2.0 设计是基于“二进制帧”进行设计的,它实现了一个目的:一切可预知、一起可控

帧是一个数据单元,实现了对消息的封装,下面是一个 HTTP/2.0 的帧结构

  • Length:3字节,表示帧负载的长度
名称 长度 描述
Length 3 字节 表示帧负载的长度,默认最大帧大小 2^14
Type 1 字节 当前帧的类型,后面会介绍
Flags 1 字节 具体帧的标识
R 1 位 保留位,不要设置,否则可能带来严重后果
Stream Identifier 31 位 每个流的唯一 ID
Frame Payload 长度可变 真实帧的内容,长度是在 Length 字段中设置的

如果使用 HTTP/1.1 的话,你需要发送完上一个请求,才能发送下一个;由于 HTTP/2.0 是分帧的,请求和响应可以交错甚至可以复用。为了能够发送不同的数据信息,通过帧数据传递不同的内容, HTTP/2 中定义了 10 中不同类型的帧,在上面表格的 Type 字段中可对“帧”类型进行设置。下面是 HTTP/2 的帧类型

名称 ID 描述
DATA 0x0 传输流的核心内容
HEADERS 0x01 包含 HTTP 首部和可选的优先级参数
PRIORITY 0x02 指示或者更改流的优先级和依赖
RST_STREAM 0x03 允许一端停止流(通常是由于错误导致的)
SETTINGS 0x04 协商连接级参数
PUSH_PROMISE 0x05 提示客户端,服务端要推送东西
PING 0x06 测试连接可用性和往返时研(RTT)
GOAWAY 0x07 告诉另外一端,当前段已结束
WINDOW_UPDATE 0x08 协商一端要接收多少字节(用于流量控制)
CONTINUATION 0x09 用以拓展 HEADER 数据块
流 steam

流,即一个完整的请求-响应数据交互过程。HTTP/2 连接上独立的、双向的帧序列交换。流 ID(帧首部的 6~9 字节)用来标识帧所属的流。

连接、流、帧的关系
  1. 一个连接同时被多个流复用
  2. 一个流代表一次完整的请求/响应过程,包含多个帧
  3. 一个消息被拆分封装成多个帧进行传输。消息指 HTTP 请求或响应。一个消息至少由 HEADERS 帧组成,并且可以另外包含 CONTINUATION 和 DATA 帧,以及其他的 HEADERS 帧。
多路复用的好处
  1. 减少服务端连接压力,减少内存占用,提升连接吞吐量
  2. 连接数的减少改善了网络拥塞状况,慢启动时间减少,拥塞和丢包恢复速度更快
  3. 避免连接频繁创建和关闭(三次连接、四次挥手)
HTTP/2.0 其他特性
流量控制

不同于 HTTP/1.1,只要客户端可以处理,服务端就会尽可能快的发送数据,HTTP/2.0 提供了客户端调整传输速度的能力(服务端也可以)。 WINDOW_UPDATE 帧用来完成这件事。每个帧告诉对方,发送方想要接收多少字节,它将发送一个 WINDOW_UPDATE 帧以指示其更新后的处理字节能力

设置资源优先级和依赖关系

流的一个重要特性是可以设置优先级和资源数据的依赖关系。HTTP/2.0 通过流的依赖可以实现这些功能。通过 HEADERS 帧和 PRIORITY 帧,客户端可以明确的告诉服务端它需要什么,这是通过声明依赖关系和权重实现的。

  • 依赖关系为客户端提供了一种能力,通过指明某些对象对另外一些对象的依赖,告知服务器哪些资源应该被有线传输。
  • 权重让客户端告诉服务器如何确定具有共同依赖关系对象的优先级
服务端推送

当页面还没有开始请求具体的资源时,服务端已经把一些资源(例如 css 和 js)推送到客户端了。当浏览器要渲染页面时,资源已经在缓存中了。服务端推送是通过 PUSH_PROMISE 帧实现的。

参考

  1. 40张图揭秘:「键入网址到底发生了什么」
  2. 炸裂!万字长文拿下HTTP 我在字节跳动等你!
  3. SSL/TLS协议运行机制的概述
  4. HTTP/2协议“多路复用”实现原理
  5. 面试官,不要再问我三次握手和四次挥手
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270