从输入网址回车到显示都发生了什么

191 9 技术 2021-12-22

这是一个经典的问题,这道题目能直观考察出对计算机网络、浏览器渲染等知识的理解

一、域名解析

我们知道互联网终端的门牌号是IP,域名只是IP的一个马甲,比起IP长串的数字更方便我们去记忆,所以当我们按下回车的时刻首先会查找DNS将域名转成IP,一般DNS都会进行缓存,所以是按照一个顺序查找缓存:

  1. 查找浏览器缓存
  2. 查找系统host文件
  3. 查找路由器缓存
  4. 查找更靠近用户的本地DNS服务器
  5. 查找DNS根服务器

最终获取到这个域名对应的IP

二、发起并建立TCP连接

为实现数据的可靠传输,TCP要在应用进程间建立传输连接。它是在两个传输用户之间建立一种逻辑联系,使得通信双方都确认对方为自己的传输连接端点。客户端和服务端需要经过“三次握手”后建立TCP连接

三次握手的过程为:

  1. 客户端发送的第一个数据包是一个连接请求报文段,包含一个SYN=1(同步位), seq=x(客户端序列号)。TCP规定,SYN=1的报文段不能携带数据。当客户端发送了这个报文之后,进入SYN-SENT(同步已发送)状态。

  2. 服务端己收到并返回SYN=1(表示没有变化),seq=y(变成了服务端的序列号),新增ACK=1,ack=x+1(客户端序列号+1)。然后服务端进入了SYN-RCVD(同步收到)状态。

  3. 客户端收到服务端报文后,还需要发送一个确认报文ACK=1,seq=x+1,ack=y+1。这里没有了SYN这个字段,所以这个报文可以携带数据。发送完毕后进入ESTABLISHED状态,这时候TCP连接建立。

参考下图:

20190122094332584

为什么非要三次才能建立呢?看着图片很好理解,就是客户端和服务器都进行了一次发送和接收,保证了双边都有能力进行发送和接收

上一个很有趣的例子,你和某个人隔着一道墙只闻其声不见其人,你想要确认他的身份并打招呼(建立TCP连接)。

  • 于是你大声问:你是张三吗?
  • 对方听到后回答:是啊,你是谁?
  • 你听到后说:我是李四啊!

这是他听完就知道我们是认识的,可以开始聊天了。思考一下如果上面的对话中某一方突然不作声了,那么肯定就不能愉快的相认了,分析下上面的对话,双方都说了话并且听到了对方的话,这就是三次握手

三、发送http请求

浏览器向服务器通过建立的TCP连接发送http GET请求

四、服务器响应并返回html

服务器返回html,浏览器开始解析,将html里需要的静态资源(如js/css/png)一并进行请求

五、断开TCP连接

请求完毕后则需要断开TCP连接,经过“四次挥手”后双边断开连接

当前处于ESTABLISHED(已建立连接)状态。客户端发出连接释放请求。包含内容Fin=1,seq=u(这个u是这个数据包之前一个数据包的序列号+1),客户端进入FIN-WAIT-1(终止等待1)状态。

服务端接收到发出确认,包含内容ack=u+1(确认号),seq=v(序列号,这个v是服务端上一个发送的数据包的序列号+1), ACK=1。然后服务端进入CLOSE-WAIT(关闭等待)。这个时候连接已结束。但TCP是全双工通信,在服务端可能还有数据没完全发送给客户端,所以服务端到客户端仍未结束。但客户端已不能再发送数据了,如果服务端还有数据发送,客户端仍要接收。

客户端收到服务端的确认之后,进入FIN-2(终止等待2)状态,等待服务端发送服务端发器的连接释放数据包。如果这时候服务端还有数据包要发送都要接收。没有数据包之后服务端发送连接释放数据包含FIN=1,ACK=1,seq=w(因为在服务端回复客户端的连接请求(数据包的序列号是v), ack=u+1(确认号和上次回复客户端的请求释放连接的确认号一样)。接着服务端进入LAST-ACK(最后确认状态),等待客户端的确认。

客户端收到服务端的连接释放数据包之后,发出一个确认数据包ACK=1,seq=u+1,ack = w+1。然后客户端进入TIME-WAIT(时间等待)状态。等待某个时间后进入CLOSED状态。

参考下图

20190122105243815

那么为什么要进行“四次挥手”才能断开TCP连接呢?接着上个例子,你和张三聊够想离开了

  • 你大声喊:张三我走啦,你还有什么要说吗?
  • 他回答:记得下次来蹲坑自己记得带纸巾!
  • 你回应说:好的,再见啦!
  • 他回答:好,下次见!

如果上面的对面在任意节点中断了,那么双方的对话都不能算真正结束,可能对方还有话要说。

六、浏览器渲染

这里是重中之重,涉及到浏览器引擎的运作,浏览器渲染顺序如下

  1. 生成DOM树和CSS规则树:自上而下解析HTML生成DOM树,遇到CSS则不阻塞情况下异步并行解析生成CSS规则树。
  2. 生成渲染树:将DOM树与CSS规则树合并在一起生成渲染树。
  3. 布局:有了渲染树,浏览器已经能知道网页中有哪些节点、各个节点的CSS定义以及他们的从属关系。下一步操作称之为Layout,顾名思义就是计算出每个节点在屏幕中的位置。
  4. 绘制:将渲染树每个节点绘制到屏幕,这时候就能看到页面出现在浏览器中。

这部分涉及很多知识点,挑几个重点的讲下:

阻塞渲染的处理:平常使用的<script>标签在解析的时候会阻塞渲染,而且加载后立刻被执行。由于自上而下解析的原因,如果是放在header标签里则会导致DOM树被阻塞,并且这时候还未生成元素节点,JS代码里操作DOM的地方会获取不到而报错,所以推荐<script>标签放在body标签里的最后面,并合并JS以增加阻塞次数,或者动态加载JS避免阻塞渲染。

回流与重绘:在我们进行渲染过程中,肯定不是一次就结束的,例如异步加载到的CSS会改变渲CSS规则树、JS会改变DOM树,这时候浏览器会不断重复进行绘制,这就牵涉到回流与重绘了。

  • 回流(Reflow):浏览器要花时间去渲染,当它发现了某个部分发生了变化影响了布局,那就需要倒回去重新渲染,也就是重回第三步布局然后绘制。
  • 重绘(Repaint):如果只是改变了某个元素的背景颜色,文字颜色等,不影响元素周围或内部布局的属性,将只会引起浏览器的repaint,重画某一部分,也就是直接去到第四步绘制。

它们之间的关系是回流必将引起重绘,而重绘不一定会引起回流

由于回流一定会引起重绘,代价昂贵,比较消耗资源,所以要学会去优化去减少消耗。浏览器已经做过优化,现代浏览器会维护一个关于渲染的队列,把所有会引起回流重绘的操作放入队列,等到有一定数量和间隔时间就批量执行,让多次变成一次。

写的时候也需要尽量减少回流与重绘,列举一些可优化的点:

  • 避免循环中去操作DOM,应存在变量中一次性插入或修改。
  • 避免直接修改多个CSS属性,应使用样式class。
  • 减少改变回流的属性变动,如top/left/padding/margin/width/height,使用transform替代。或者设置position: fixed使其脱离文档流后再操作。
  • 缓存获取元素的偏移量属性,获取一个元素的scrollTop、scrollLeft、scrollWidth、offsetTop、offsetLeft、offsetWidth、offsetHeight之类的属性,浏览器为了保证值的正确也会回流取得最新的值。
© 2020 peal.cc 粤ICP备2020133024号