从输入 URL 到页面呈现之间发生了什么?
DNS
解析(将域名解析为IP
地址)- 发起
TCP
连接(三次握手) - 发送
HTTP
请求 - 服务器处理请求并返回
HTTP
报文 - 浏览器解析渲染页面
- 断开连接(四次挥手)
1. DNS 解析
浏览器输入域名后,会通过DNS服务器查找该域名对应的 IP
地址,然后通过 IP
地址来访问服务器资源
在此插入一个 CDN
的知识,它是利用 DNS
重定向方式,一个 CDN
域名会存在多个服务器, DNS
服务器会返回一个与当前用户距离最近的服务器 IP
地址,以使访问能够更快。
2. TCP 三次握手
第一次:由浏览器发起,告诉服务器要发起请求了
第二次:由服务器发起,告诉浏览器我准备好了
第三次:由浏览器发起,我收到了,准备发起请求了
第一次:客户端发送syn包(Seq=x)到服务器,并进入SYN_SEND状态,等待服务器确认;
第二次:服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(Seq=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
3. 发送 HTTP 请求
TCP
三次握手结束后,开始发送 HTTP
请求
4. 服务器处理请求并返回 HTTP 报文
5. 浏览器解析渲染页面
- 解析
HTML
,构建DOM
树 - 解析
CSS
,生成CSS
规则树 - 合并
DOM
与CSS
规则,生成render
树 - 布局
render
树(Layout/Reflow
回流),负责各元素尺寸、位置的计算 - 绘制
render
树(paint
重绘),绘制页面像素信息
注意:
- 构建
DOM
树是一个渐进过程,为达到更好的用户体验,渲染引擎会尽快将内容显示在屏幕上,它不必等到整个HTML
文档解析完成之后才开始构建render
树和布局。 Render
树不必等到DOM
树和CSS
样式表构建完毕后才开始构建,这三个过程在实际进行的时候并不是完全独立的,而是会有交叉,会一边加载,一边解析,以及一边渲染。CSS
的解析是从右往左逆向解析的,嵌套标签越多,解析越慢。- 原生
JS
或JQ
操作DOM
时,浏览器会从构建DOM
树开始从头到尾执行一遍流程。在一次操作中,我需要更新 10 个DOM
节点,浏览器收到第一个DOM
请求后并不知道还有 9 次更新操作,因此会马上执行流程,最终执行10 次。例如,第一次计算完,紧接着下一个DOM
更新请求,这个节点的坐标值就变了,前一次计算为无用功。计算DOM
节点坐标值等都是白白浪费的性能。
6. 断开连接
数据传输完毕,需要断开 TCP
连接,此时将发起 TCP
四次挥手
第一次:由浏览器发起,发送给服务器,请求报文已发送完,请准备关闭连接
第二次:由服务器发起,发送给浏览器,请求报文已接收完,我准备关闭连接
第三次:由服务器发起,发送给浏览器,响应报文已发送完,你准备关闭连接
第四次:由浏览器发起,发送给服务器,响应报文已接收完,我准备关闭连接,你也关闭吧
第一次挥手:
客户端发送一个FIN,用来关闭客户端到服务器的数据传送,也就是客户端告诉服务器:我已经不 会再给你发数据了(当然,在fin包之前发送出去的数据,如果没有收到对应的ack确认报文,客户端依然会重发这些数据),但是,此时客户端还可 以接受数据。 FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。 TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。第二次挥手:
服务器收到FIN包后,发送一个ACK给对方并且带上自己的序列号seq,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号)。此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。 此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。第三次挥手:
服务器发送一个FIN,用来关闭服务器到客户端的数据传送,也就是告诉客户端,我的数据也发送完了,不会再给你发数据了。由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。第四次挥手:
主动关闭方收到FIN后,发送一个ACK给被动关闭方,确认序号为收到序号+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。 服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。 至此,完成四次挥手。
为什么是三次握手?
三次握手中,client
和 server
都有一个发 syn
和收 ack
的过程, 双方都是发后能收, 表明通信则准备工作OK。
这是由 TCP 的自身特点可靠传输决定的。客户端和服务端要进行可靠传输,那么就需要确认双方的接收和发送能力。第一次握手可以确认客服端的发送能力,第二次握手,确认了服务端的发送能力和接收能力,所以第三次握手才可以确认客户端的接收能力。不然容易出现丢包的现象。