浏览器是如何与服务器进行通信的?

TCP 连接包括了建立连接、传输数据与断开连接三个阶段,而这一次我们要介绍的 HTTP 协议正是建立在 TCP 连接基础上。

HTTP 协议是一种允许浏览器向服务器获取资源的协议,同时也是我们前端开发 Web 的基础。HTTP 协议通常由浏览器发起请求,用于向服务器获取不同类型的文件,比如前端常用的 HTML 文件,CSS文件。而 HTTP 协议本身也是浏览器使用最广泛的协议。

在我们使用浏览器的时候,通常会碰到下面两种情况:

  • 当我们第一次访问某个网站的时候,可能打开的速度特别慢,但是再次访问的时候速度明显变快。
  • 当我们登录过一个网站之后,下次再访问这个网站的时候,就已经处于登录状态了。

这一背后的原理都需要了解 HTTP 协议的请求过程。这也正是我们要讲解的内容。

HTTP 请求流程大概氛围两部分:第一部分是浏览器向服务器发起 HTTP 请求,第二部分则是服务器进行响应。我们接下来将从这两部分进行讲解。

HTTP 与 TCP

前面已经提到了 HTTP 协议建立在 TCP 连接的基础之上,那么我们在讲解 HTTP 请求之前,首先需要再深入了解一下 HTTP 协议与 TCP 之间的关系。

浏览器使用 HTTP 协议作为应用层,同时使用 TCP/IP 协议作为传输层协议,将它发送到网络上。

因此在 HTTP 实际开始工作之前,浏览器需要通过 TCP 协议与服务器建立连接,也就是说 HTTP 的内容本质上是通过 TCP 的传输数据阶段来实现的。

建立 TCP 连接的第一步,就是需要知道目的地址的 IP 地址和端口号。

我们一般不直接使用 IP 地址作为网址进行访问,而是通过部署在 IP 地址上的域名来访问。因此我们首先需要利用的就是我们的域名地址,来获取目的地址。

域名与 IP 地址实际上是一一映射的关系,而构建这套映射关系的系统,就叫做域名系统,简称为 DNS Domain Name System。

因此我们在拿到网址 URL 之后,我们就可以通过 DNS 和域名来获得目的地址的 IP 地址。

至于端口号,如果在访问地址的时候没有直接指明端口号,那 么HTTP 协议默认的端口号就是 80。

于是到现在我们就明白了HTTP请求与TCP连接之间的关系大致如下。

  1. HTTP 是应用层协议,需要通过网络层协议 TCP 建立网络连接。
  2. TCP 建立网络连接需要获得目的地址的 IP 地址和端口号。
  3. 我们通过 DNS 和域名可以获得目的地址的 IP 地址。

浏览器发起请求

我们假设在浏览器地址框种输入地址:yucohny.github.io/index.html。那么接下来我们尝试分析浏览器将完成的动作。

构建请求

首先,浏览器需要构建请求行信息,随后,浏览器再准备发起网络请求:

GET /index.html HTTP1.1
复制代码

查找缓存

在正式发起网络请求之前,浏览器都会首先在浏览器的缓存当中查询是否有要请求的文件。

浏览器缓存本身是一种在本地保存资源的副本,以供下次请求时直接使用的一种技术。

当浏览器发现我们想要请求的资源已经存在于缓存当中时,就会拦截请求,直接返回该资源的副本,同时结束请求。这样做有几个明显的好处:

  • 能够缓解服务器端的压力,提升性能。
  • 缓存是实现快速资源下载的重要组成部分。

如果在缓存当中没有找到我们想要请求的资源,那么就会进入正式的网络请求过程。

准备 IP 地址和端口

在前面介绍 HTTP 和 TCP 之间关系的时候,我们已经介绍了如何获取IP地址和端口号:

我们通过 DNS 查找到目的地址的 IP 地址;同时查看 HTTP 协议,本身是否指定了端口号,如果没有指定端口号,那么默认则是 80 端口。

等待 TCP 队列

在一切都准备好了之后,我们仍然不能直接建立 TCP 连接,这是因为 chrome 谷歌浏览器存在一个机制:对于同一个域名,我们最多只能同时建立 6 个 TCP 连接,因此如果在同一个域名下有大于 6 个 TCP 连接同时发生,那么后面的请求则会进入排队等待状态,直到有多的空位来连接。

建立 TCP 连接

这一部分就是浏览器,通过TCP协议与服务器已经成功建立连接。

发送 HTTP 请求

在成功建立 TCP 连接之后,浏览器就可以和服务器进行通信。HTTP 中想要发送的数据,就是在这个通讯过程中传输的。

浏览器请求信息至服务器,本质上请求信息包含了三个部分:请求行、请求头与请求体。

对于请求行而言,则包括了请求方法,请求 URL 和 HTTP 协议版本三个部分,也就是我们前面所提到的构建请求。

请求头包括了一些浏览器的基础信息,比如浏览器所使用的操作系统、浏览器内核,以及浏览器端的 Cookie 等信息。

而请求体则是我们真正想要发送的数据的内容。

至此,浏览器就已经成功向服务器发送了请求。

服务器处理请求

接下来复习将会根据浏览器的请求信息来准备响应的内容。

返回请求

一旦服务器处理结束,就可以返回信息至浏览器。我们可以通过 curl 命令查看响应信息。

我们在本地命令行输入:

curl -i "https://yucohny.github.io/index.html"
复制代码

精简后的返回信息如下:

HTTP/1.1 200 OK
​
Connection: keep-alive
Content-Length: 405
Server: GitHub.com
Content-Type: text/html; charset=utf-8
permissions-policy: interest-cohort=()
Last-Modified: Sat, 26 Mar 2022 14:46:30 GMT
Access-Control-Allow-Origin: *
Strict-Transport-Security: max-age=31556952
ETag: "623f2746-195"
expires: Sun, 27 Mar 2022 07:32:24 GMT
Cache-Control: max-age=600
x-proxy-cache: MISS
X-GitHub-Request-Id: 7CAA:49EE:2FEA23:336FBC:624010B0
Accept-Ranges: bytes
Date: Sun, 27 Mar 2022 07:22:24 GMT
Via: 1.1 varnish
Age: 0
X-Served-By: cache-icn1450078-ICN
X-Cache: MISS
X-Cache-Hits: 0
X-Timer: S1648365744.209898,VS0,VE183
Vary: Accept-Encoding
X-Fastly-Request-ID: c05cfa310b4d32d60f2c76f6c1d4f38a08916b56
​
<!doctype html>
<html lang="en">
<head></head>
<body>
​
</body>
</html>
复制代码

我使用空行将请求信息划分为了三个部分,分别对应了响应行、响应头与响应体。不难看出这三个部分本质上对应了我们请求信息的三个部分。

响应行包括了 HTTP 协议版本与状态码。

常用的状态码有许多。其中状态码 200 表示处理成功;状态码 404 表示没有找到对应页面。

响应头则包括了服务器自身的一些信息,比如服务器生成返回数据的时间、返回的数据类型,以及服务器要在客户端保存的 cookie 的信息。

而响应体则包含了 HTML 的实际内容。

断开连接

通常情况下,一旦服务器向客户端返回了响应信息,那么就需要关闭 TCP 连接,但是如果浏览器与服务器都在他们的请求信息或者返回信息中加入了以下信息

Connection: Keep-Alive
复制代码

那么 TCP 连接在服务器返回信息后,仍然保持打开状态,这样浏览器就可以继续通过同一个 TCP 连接发送请求。保持 TCP 连接打开可以省去下次需要建立连接的时间提升资源下载的速度。

猜你喜欢

转载自juejin.im/post/7079678785221558302
今日推荐