从输入URL到页面展示发生了什么(详细版)

334 阅读10分钟

最简单的浏览器请求过程如下: 客户端地址栏中输入内容,浏览器从地址栏中输入的内容获得服务器的IP地址和端口号。浏览器用TCP的三次握手和四次回收与服务器建立连接,建立连接以后浏览器向服务器发送拼好的报文,服务器收到拼接好的报文以后,也会给浏览器返回拼接好的报文内容,浏览器接受服务器返回的响应报文,浏览器解析好报文渲染到页面。

上述前提是我们在浏览器中输入的是IP地址,但是大多数情况下我们在浏览器中输入的是域名,输入域名的具体步骤如下:

第一步:用户输入

当用户在浏览器的地址栏里面输入内容时,浏览器会对输入的内容进行判断,是搜索内容还是请求的URL

  • 如果是搜索内容,浏览器会使用默认的搜索引擎合成带有搜索关键字的URL
  • 如果是符合规则的URL,浏览器会根据规则给内容加上URL协议,从而生成完整的URL

第二步:DNS查询阶段

浏览器是如何知道我们输入的域名对应的IP地址是什么呢?这就依靠我们的DNS这个大功臣。DNS主要是通过一系列的域名解析服务器,试图把域名转换成对于的TCP/IP协议中的IP地址。

DNS服务器特性:高可用、高并发、分布式。

DNS服务器作用:因特网上作为域名和IP地址相互映射的一个分布式数据库,能够使用户更方便的访问互联网,而不用去记住能够被机器直接读取的IP数串。

DNS查询模式有两种:递归查询迭代查询

  • 一般客户机和服务器之间的查询方式属于递归查询。
  • DNS服务器之间的查询方式属于迭代查询。

DNS(Domain Name System)是“域名系统”的英文缩写。

DNS的域名空间结构: (图片来源:极客时间趣谈网络协议)

客户机和服务器之间的查询方式属于递归查询。

解析流程:

0)客户端输入URL后,首先在本地计算机的缓存中查找。如果在本地无法获得查询信息,则将查询请求发给DNS服务器。本地计算机的缓存中查找查找步骤如下

1)电脑客户端会发出一个DNS请求比如https://juejin.cn/ ,并且发给本地的域名服务器(本地DNS)。本地DNS一般分为两种,第一,通过DHCP配置的本地DNS由你的网络服务商(ISP)提供。第二,电信、移动等设备自动分配的,通常在你的网络服务商的某个机房。

2)本地DNS收到客户端的请求,本地DNS这台服务器缓存了一张域名和IP地址映射的数据库。如果能找到https://juejin.cn/会直接返回IP地址,否则会去问根DNS。

3)根DNS是最高层次的不直接用于域名解析,根DNS收到本地DNS的请求发现是.cn,会将下一级域名服务器地址给本地DNS

4)本地DNS转向下一级域名服务器,如此递归直到找到目标域名的DNS服务器。最后将对应的IP地址告诉本地DNS。

5)本地DNS服务器再将对应的IP地址告诉客户端。

6)客户端和目标地址建立连接。

思考

我们在进行DNS域名解析的过程中,DNS的解析可能会给出CDN服务器的地址而不是目标服务器的实际地址,因为CDN会缓存网站的大部分资源,减轻源服务器的压力。所以有的HTTP请求就不需要发到源服务器,DNS服务器就可以给我们返回数据了。

但是通过后台服务器动态生成的页面属于动态资源,CDN服务器没办法缓存的,HTTP请求只能从源服务器获取。

源服务器(也就是目标服务器)外表表现的就是一个IP地址,针对高并发,目标网站一般是多个服务器组成的比较稳定的集群。

第三步:建立TCP连接

当完成域名到IP地址的解析,地址栏中输入的域名也就相当于在地址栏中输入了IP地址,浏览器用TCP的三次握手与服务器建连接。

如果请求是HTTPS还需要建立TLS连接。

问题:

  • 为什么要三次,而不是两次呢?

    答:因为可靠,比如A要发起一个连接,可能发出去请求包就丢了,可能超时了,A无法确认结果就还会再发,所以B收到请求以后,B知道A要与他进行连接,但是B愿意不愿意连接必须也要告诉A,但是B告诉A的话也是消息发出去,但是不知道能否达到A,可能A已经挂了,所以A也需要向B发送应答之应答,此刻A出去的消息虽然也是有去无回,但是在这么下去就没有结束了。只要保证双方的消息都有去有回就行了。当然三次握手不仅仅是为了双方建立连接,还要沟通TCP的序号问题。

  • 开始的时候,请求方和接收方都是close状态。服务端主动监听某个端口,处于listen状态。

  • 客户端发送请求连接报文,连接SYN,之后处于SYN_sent状态。

  • 服务端收到请求后,返回SYN,之后处于SYN_rcvd状态。

  • 客户端收到服务器发送的SYN和ACK之后,发送SYN和ACK,之后处于established状态。

  • 服务端收到YN和ACK以后,处于established状态。

至此,客户端和服务端都做到了一发一收成功了。建立连接至此已经成功了。

目前大多数建立的TCP连接都是默认开启 Keep-Alive的,这样建立好的TCP连接可以再多次请求中复用,而不需要一次又一次的握手重新建立连接。

其实就是浏览器访问服务端之后, 一个http请求的底层是tcp连接, tcp连接要经过三次握手之后,开始传输数据, 而且因为http设置了keep-alive,所以单次http请求完成之后这条tcp连接并不会断开, 而是可以让下一次http请求直接使用.当然keep-alive肯定也有timeout, 超时关闭.

第四步:发送请求

建立连接后,浏览器就要发送HTTP的请求。浏览器构建数据包(包含请求行,请求头,请求正文,并把该域名相关Cookie等数据附加到请求头),然后向服务器发送请求消息。 请求分为三部分,

  • 第一部是起始行,描述请求或者响应的基本信息
  • 第二部分是头部字段集合
  • 第三部分才是真正的请求实体,数据的传输。

第一部分和第二部分经常被合称为请求头响应头

浏览器发送get请求的时候,HTTP报文经常是只有header没有body。HTTP协议对header的大小没有限制,头部太大会影响网络的运行效率,所以web浏览器不允许过大的请求头。

1、请求行(起始行)

GET / HTTP/1.1 这就是一个简单的请求行,他描述的是客户端想要如何操作服务端的资源。

浏览器请求方法是GET,请求目标是服务器的根目录,告诉服务器我用的协议版本好是1.1,服务器也需要用1.1版本回复客户端。

2、状态行(响应行)

HTTP/1.1 200 OK这是服务端返回给浏览器的状态行,告诉浏览器报文使用的协议版本号是1.1,状态码是200,一切OK。

HTTP/1.1 404 Not Found,告诉浏览器报文使用的协议版本号是1.1,状态码是404,表示服务器接受到了请求,但是没有找到对应的资源。

3、头部字段

请求头和响应头的头部字段结构基本相同。唯一区别就是起始行不同。头部字段的格式都是以key-value的形式,中间用冒号隔开,最后用换行符换行。HTTP头部字段比较灵活,可以扩展。除了可以使用标准的已有头外,我们也可以自定义头部字段。

第五步:接受响应

服务器接收到消息后根据请求信息构建响应数据(包括响应行,响应头,响应正文),然后发送回网络进程。 网络进程接收到响应数据后进行解析。 如果发现响应行的返回的状态码为301,302,说明服务器要我们去找别人要数据,找谁呢?找响应头中的Location字段要,Location的内容是需要重定向的地址url。获取到这个url一切重新来过。 如果返回的状态码为200,说明服务器返回了数据。

第六步:渲染页面

浏览器进程发出“提交文档”(文档是响应体数据)消息给渲染进程,渲染进程接收到消息后会和网络进程建立传输数据的通道,网络进程将“文档”传输给渲染进程。

1、构建DOM树

浏览器无法直接理解HTML、CSS、JS。所以需要将HTML转换成浏览器能够理解的结构-DOM树。

2、样式计算

我们构建好DOM树以后,DOM节点的样式还不知道,这时候我们就需要进行样式计算了。

2.1 把CSS转换成浏览器能够理解的结构

当渲染引擎接收到CSS文本时,会执行转换操作,把CSS文本转换成浏览器可以理解的结构-styleSheets。

2.2 转换样式表中的属性值,使其标准化

比如转换em为px、red转换成rab(255,0,0),blod解析为700等等计算渲染引擎容易理解的,标准化的计算值。

2.3 计算出DOM树中每个节点的具体样式

这里会涉及到CSS继承规则和层叠规则

3、布局阶段

3.1 创建布局树

为了构建布局树,浏览器大体上完成了下面这些工作:

  • 遍历 DOM 树中的所有可见节点,并把这些节点加到布局树中;
  • 而不可见的节点会被布局树忽略掉,如 head 标签下面的全部内容,再比如 body.p.span 这个元素,因为它的属性包含 dispaly:none,所以这个元素也没有被包进布局树。

3.2 布局计算

现在我们有了布局树,接下来就会计算布局节点的坐标信息了。

总结:在 HTML 页面内容被提交给渲染引擎之后,渲染引擎首先将 HTML 解析为浏览器可以理解的 DOM;然后根据 CSS 样式表,计算出 DOM 树所有节点的样式;接着又计算每个元素的几何坐标位置,并将这些信息保存在布局树中。