前端面试最为经典的问题:输入URL到页面加载完成都发生了什么
1 前言
这个可以说是前端面试的最为经典的问题。它不但考察知识面的广度,而且考察深度。而且它层次性,无论是大牛还是小白都能做出针对性的回答。
Q&A:
Q:网络上已经由这么多关于这个问题的博客,你为什么还要再写?
A:我的小学语文老师经常说:好记性不如烂笔头。我的学习经验证明:如果一个复杂的问题,你不去整理或者实践,那么你不能指望:看过答案之后就掌握怎么解决这个问题。
另外:我对网络上关于这个问题的回答稍微做了整理。
2 正文
DNS解析
所谓DNS域名解析过程,就是将域名,例如www.google.com,解析为IP地址的过程。
后面会讲到的TCP协议和HTTP协议的通信过程中不能缺少IP地址。
好了,我们正式开始。当我们输入URL,DNS域名解析的过程就开始了,浏览器会先后访问下列地方:
浏览器缓存
浏览器会缓存DNS记录持续一定时间。有趣地是,操作系统并不告诉DNS记录有效的事件,所以浏览器缓存的这些记录有效一定的时间间隔:不同的浏览器时间间隔不同,大约在2-30分钟。
在chrome,通过这个地址:chrome://net-internals/#dns 访问chrome的DNS缓存
操作系统缓存
如果浏览器缓存没有包含需要的记录,浏览器用一个系统调用(Linux的说法)从操作系统获取它的缓存。
系统缓存主要存在/etc/hosts(Linux和Mac系统)中:
Win10系统可以用 ipconfig /displaydns命令(dos命令)获取DNS缓存(其他版本window未验证)。
路由器缓存
路由器也具有存储DNS缓存的功能
ISP DNS缓存
ISP也就是Internet Service Provider,翻译成中文就是互联网服务提供商,在天朝就是电信,联通,移动这些了。
ISP提供了本地DNS服务器,用于提供DNS缓存服务
递归搜索
下图是该过程的一个例子:
上图中的例子中,我们要访问www.wikipedia.org这个域名。
接下来,我们要访问根域名服务器198.41.0.4。(全球有13组根域名服务器。)。如果根域名服务器内不存在相应DNS记录,则会返回.org*域名服务器的地址:204.74.112.1。
浏览器得到回复后,会然后访问.org*域名服务器,如果该域名服务器没有相应的DNS记录,那么会返回wikipedia.org域名服务器的地址:207.142.131.234。
浏览器然后访问wikipedia.org域名服务器,如果之前一直没有得到DNS记录,那么这时候就应该得到了。
TCP三次握手
在发起HTTP请求之前,我们需要建立TCP连接,关于三次握手的理解和实践请看:这篇博文
HTTP请求
你能够确定Facebook的主页不能够通过缓存获取。所以,浏览器会发送HTTP request给Facebook的服务器。
服务器针对浏览器的HTTP request,会有相应的reponse。
当然,这里会涉及HTTP的知识,比如请求头,回复头,状态码等等。这里先不赘述。
浏览器解析网页
浏览器获取到HTTP response(一般是一个HTML文件),浏览器的内核开始解析HTML。在解析HTML的过程中,如果它需要请求其他资源,比如图片,比如CSS层叠样式表,比如JS文件,那么浏览器会继续发送HTTP请求。
在解析HTML的过程中,浏览器两个比较重要的事务。
一是生成DOM树,HTML代码中的一个标签就是一个DOM节点,DOM节点的嵌套关系组成了DOM树。
二是在DOM树基础上,根据DOM节点的集合属性(margin,padding,width,height等)生成render树。
注意,颜色,背景色等css属性并属于render树的范畴。之所以要将margin,padding,width这些集合属性与其他属性区分开来,需要根据所有DOM节点的这些属性计算各个DOM节点的位置。
如果任一DOM节点的集合属性发生变化,所有DOM节点的位置都要重新计算一遍,也就是说,要重新生成render。
生成render树的过程被称为reflow,译作回流。
浏览器会在render树的基础上继续渲染颜色、背景色、字体等样式,这个过程被称为repaint,译作重绘。
关于reflow和repaint可以看这里
上述过程可以用下图表示:
断开TCP连接
自HTTP1.1协议之后,HTTP就完全支持持久连接,也就是只要建立一次TCP连接,就可以不断发送HTTP请求。
如果浏览器一段时间内不发送HTTP请求,那么浏览器利用reset(RST)报文快速释放已经完成数据交互的TCP连接,以提高业务交互的效率。(为了并发处理HTTP请求,一般会有浏览器会有多个端口和服务器建立TCP连接。)最终会只剩下一个TCP连接,而且reset报文不需要服务器确认。
RST报文详见这篇博文
从上图中可以看到,序号为103-106的TCP报文分别是4个端口向服务器的80端口发送RST报文,释放TCP连接。
最终浏览器只剩下50276端口,50276端口在断开连接,需要不断向80端口发送keep-Alive报文,该报文的目的是告诉服务器:“我可能还要发送HTTP请求哦,不要断开连接哦。“
序号107-118的TCP报文就是keep-Alive报文以及服务器的确认报文,由于这期间一直没有HTTP请求,50276端口不再继续发送keep-Alive报文。
由于之后服务器的80端口一直没有收到keep-Alive报文(持续大约几分钟),服务器开始发送断开连接的请求。TCP的四次挥手正式开始。
序号119-122的TCP报文就是四次挥手的报文。下图对四次挥手报文进行了清晰的阐述,我就不赘述了。
另外:当客户端收到服务器的确认报文(seq = a +1)后,就断开连接,但是服务器要等到发出确认报文(seq = a + 1)之后再等上两个TCP报文周期后才断开连接。这是因为如果客户端没收到确认报文,那么会重发要求断开的报文(seq = b)。
值得注意的是:客户端也可以主动断开连接,但是我还不知道是在什么情况下。
TCP断开连接的内容就阐述完了。这也意味着这个经典问题的结束。
以上。