1. 问题
2. 解答
本文会首先简单回顾一下浏览器发送请求和服务器响应请求的过程,详见:HTTP 协议的基本概念:请求与响应 。
通过 HTTP 请求与响应的过程可以轻松解答这两个常见问题:一个涉及到了 Cache 流程;另外一个涉及到如何使用 Cookie 来进行状态管理。
I. HTTP 请求与响应概述
浏览器中的 HTTP 请求从发起到结束一共经历了如下 8 个阶段:构建请求、查找缓存、准备 IP 和端口、等待 TCP 队列、建立 TCP 连接、发起 HTTP 请求、服务器处理请求、服务器返回请求 和 断开连接。
1. HTTP 请求与响应流程图
II. 浏览器端发起 HTTP 请求流程
1. 构建请求
首先,浏览器构建请求行信息(如下所示),构建好后浏览器准备发起网络请求。
GET /index.html HTTP1.1
2. 查找缓存
在真正发起网络请求之前,浏览器会先在浏览器缓存(Cache)中查询是否有要请求的文件。其中,浏览器缓存是一种在本地保存资源副本,以供下次请求时直接使用的技术 。注意:Cookie 不是缓存,从严格意义上来讲,Cookie 只是 HTTP 的一个 头(Header)字段,然后遵守 HTTP 标准的 web 服务器和客户端(浏览器)都按照这个字段的标准来执行相应的行为,Cookie 一个常用的目的是来实现认证,而非缓存。
当浏览器发现请求的资源已经在浏览器缓存中存有副本,它会拦截请求,返回该资源的副本,并直接结束请求,而不会再去源服务器重新下载。这样做的好处有:
缓解服务器端压力,提升性能(获取资源的耗时更短了);
对于网站来说,缓存是实现快速资源加载的重要组成部分。
如果缓存查找失败,就会直接进入网络请求的过程。
3. 准备 IP 地址和端口
数据包都是通过 IP 地址传输给接收方的。由于 IP 地址是数字标识,比如极客时间网站的 IP 是 39.106.233.176
,难以记忆,但使用极客时间的域名 time.geekbang.org
,就好记多了,所以基于这个需求又出现了一个服务,负责把域名和 IP 地址做一一映射关系。这套域名映射为 IP 的系统就叫做 域名系统 ,简称 DNS(Domain Name System) 。
浏览器还提供了 DNS 数据缓存服务:如果某个域名已经解析过了,那么浏览器会缓存解析的结果,以供下次查询时直接使用,这样也会减少一次网络请求。
拿到 IP 之后,接下来就需要获取端口号了。通常情况下,如果 URL 没有特别指明端口号,那么 HTTP 协议默认是 80 端口。
4. 等待 TCP 队列
Chrome 有个机制,同一个域名同时最多只能建立 6 个 TCP 连接,如果在同一个域名下同时有 10 个请求发生,那么其中 4 个请求会进入排队等待状态,直至进行中的请求完成。如果当前请求数量少于 6,会直接进入下一步,建立 TCP 连接。
5. 建立 TCP 连接
排队等待结束之后,终于可以和服务器三次握手了,在 HTTP 工作开始之前,浏览器通过 TCP 与服务器建立连接。
6. 发送 HTTP 请求
一旦建立了 TCP 连接,浏览器就可以和服务器进行通信了。而 HTTP 中的数据正是在这个通信过程中传输的。
a. HTTP 请求数据格式
首先浏览器会向服务器发送 请求行,它包括了 请求方法、请求URL(Uniform Resource Identifier)和 HTTP 版本协议。
发送请求行,就是告诉服务器浏览器需要什么资源,最常用的请求方法是 Get。比如,直接在浏览器地址栏键入极客时间的域名 time.geekbang.org
,这就是告诉服务器要 Get 它的首页资源。
另外一个常用的请求方法是 POST,它用于发送一些数据给服务器,比如登录一个网站,就需要通过 POST 方法把用户信息发送给服务器。如果使用 POST 方法,那么浏览器还要准备数据给服务器,这里准备的数据是通过 请求体 来发送。
在浏览器发送请求行命令之后,还要以 请求头 形式发送其他一些信息,把浏览器的一些基础信息告诉服务器。比如包含了浏览器所使用的操作系统、浏览器内核等信息,以及当前请求的域名信息、浏览器端的 Cookie 信息,等等。
III. 服务器端返回 HTTP 响应流程
1. 返回响应
a. 服务器响应的数据格式
首先服务器会返回 响应行,包括协议版本和状态码。 但并不是所有的请求都可以被服务器处理的,那么一些无法处理或者处理出错的信息,怎么办呢?服务器会通过请求行的状态码来告诉浏览器它的处理结果,比如:
200 OK :请求已成功,请求所希望的响应头或数据体将随此响应返回。出现此状态码是表示正常状态。
302 Move temporarily :重定向,请求的资源临时从不同的 URI响应请求。
304 Not Modified :从缓存中读取数据,不从服务器重新获取数据。
403 Forbidden :服务器已经理解请求,但是拒绝执行它,一般在权限不够的时候常见。
404 Not Found :请求失败,请求所希望得到的资源未被在服务器上发现。
405 Method Not Allowed :请求行中指定的请求方法不能被用于请求相应的资源。
500 Internal Server Error :服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。
随后,正如浏览器会随同请求发送请求头一样,服务器也会随同响应向浏览器发送 响应头 。响应头包含了服务器自身的一些信息,比如服务器生成返回数据的时间、返回的数据类型(JSON、HTML、流媒体等类型),以及服务器要在客户端保存的 Cookie 等信息。
发送完响应头后,服务器就可以继续发送 响应体 的数据,通常,响应体就包含了 HTML 的实际内容。
2. 断开连接
通常情况下,一旦服务器向客户端返回了请求数据,它就要关闭 TCP 连接。
不过如果浏览器或者服务器在其头信息中加入了 Connection: Keep-Alive
,那么 TCP 连接在发送后将仍然保持打开状态,这样浏览器就可以继续通过同一个 TCP 连接发送请求。 保持 TCP 连接可以省去下次请求时需要建立连接的时间,提升资源加载速度。比如,一个 Web 页面中内嵌的图片就都来自同一个 Web 站点,如果初始化了一个持久连接,你就可以复用该连接,以请求其他资源,而不需要重新再建立新的 TCP 连接。
3. 重定向
到这里似乎请求流程快结束了,不过还有一种情况你需要了解下,比如当你在浏览器中打开 geekbang.org
后,你会发现最终打开的页面地址是 https://www.geekbang.org
。
这两个 URL 之所以不一样,是因为涉及到了一个 重定向操作。
a. 重定向的响应头和响应行
从图中可以看到,响应行返回的状态码是 301,状态 301 就是告诉浏览器,我需要重定向到另外一个网址,而需要重定向的网址正是包含在响应头的 Location 字段中,接下来,浏览器获取 Location 字段中的地址,并使用该地址重新导航,这就是一个完整重定向的执行流程。这也就解释了为什么输入的是 geekbang.org
,最终打开的却是 https://www.geekbang.org
了。
IV. 问题解答
1. 为什么很多站点第二次打开速度会很快?
如果第二次页面打开很快,主要原因是第一次加载页面过程中,缓存了一些耗时的数据。
那么,哪些数据会被缓存呢?从上面介绍的核心请求路径可以发现,DNS 缓存 和 页面资源缓存 这两块数据是会被浏览器缓存的。
其中,DNS 缓存比较简单,它主要就是在浏览器本地把对应的 IP 和 域名 关联起来,这里就不做过多分析了。
这里重点介绍浏览器资源缓存,下面是缓存处理的过程:
a. 缓存查找流程图
b. 存储缓存
服务器是通过什么方式让浏览器缓存数据的?从上图的第一次请求可以看出,当服务器返回 HTTP 响应头 给浏览器时,浏览器是 通过响应头中的 Cache-Control 字段来设置是否缓存该资源 。通常,我们还需要为这个资源设置一个缓存过期时长,而这个时长是通过 Cache-Control 中的 Max-age 参数来设置的,比如上图设置的缓存过期时间是 2000 秒。
Cache-Control:Max-age=2000
这也就意味着,在该缓存资源还未过期的情况下,如果再次请求该资源,会直接返回缓存中的资源给浏览器。
c. 更新缓存
但如果缓存过期了,浏览器则会继续发起网络请求,并且在 HTTP 请求头 中带上:
If-None-Match:"4f80f-13c-3a1xb12a"
服务器收到请求头后,会根据 If-None-Match 的值来判断请求的资源是否有更新。
如果没有更新,就返回 304 状态码,相当于服务器告诉浏览器:这个缓存可以继续使用,这次就不重复发送数据给你了。
如果资源有更新,服务器就直接返回最新资源给浏览器。
d. 总结
简要来说,很多网站第二次访问能够秒开,是因为这些网站把很多资源都缓存在了本地,浏览器缓存直接使用本地副本来回应请求,而不会产生真实的网络请求,从而节省了时间。同时,DNS 数据也被浏览器缓存了,这又省去了 DNS 查询环节。
2. 登录状态是如何保持的?
a. Cookie 的流程图
b. 存储 Cookie
用户打开登录页面,在登录框里输入用户名和密码,点击确定按钮。点击按钮会触发页面脚本生成用户登录信息,然后调用 POST 方法提交用户登录信息给服务器。
服务器接收到浏览器提交的信息之后,查询后台,验证用户登录信息是否正确,如果正确的话,会生成一段表示用户身份的字符串,并把该字符串写到 响应头 的 Set-Cookie 字段里,如下所示,然后把响应头发送给浏览器。
Set-Cookie: UID=3431uad;
浏览器在接收到服务器的响应头后,开始解析响应头,如果遇到响应头里含有 Set-Cookie 字段的情况,浏览器就会把这个字段信息保存到本地。比如把 UID=343luad 保存到本地。
c. 获得 Cookie
当用户再次访问时,浏览器会发起 HTTP 请求,但在发起请求之前,浏览器会读取之前保存的 Cookie 数据,并把数据写进 请求头 里的 Cookie 字段里(如下所示),然后浏览器再将请求头发送给服务器。
Cookie: UID=343luad;
服务器在收到 HTTP 请求头数据之后,就会查找请求头里面的 “Cookie” 字段信息,当查找到包含 UID=343luad 的信息时,服务器查询后台,并判断该用户是已登录状态,然后生成含有该用户信息的页面数据,并把生成的数据发送给浏览器。
浏览器在接收到该含有当前用户的页面数据后,就可以正确展示用户登录的状态信息了。
d. 总结
简单地说,如果服务器发送的响应头内有 Set-Cookie 的字段,那么浏览器就会将该字段的内容保存到本地。当下次客户端再往该服务器发送请求时,客户端会自动在请求头中加入 Cookie 值后再发送出去。服务器发现客户端发送过来的 Cookie 后,会去检查究竟是从哪一个客户端发来的连接请求,然后对比服务器上的记录,最后得到该用户的状态信息。
原文链接:https://qwert.blog.csdn.net/article/details/105641373