面试:请说说"从输入URL到页面加载完成的过程当中都发生了什么事情"

文章转载自:从输入URL到页面加载完成的过程当中都发生了什么事情html

基本过程

为何会想要了解从输入URL到页面加载完成的过程当中都发生了什么事情这个问题呢,由于课程参考资料的Web 建站技术中HTML、HTML五、XHTML、CSS、SQL、JavaScript、PHP、ASP.NET、Web Services 是什么中最高票答案中给出了下图所示的网站访问基本过程,张秋怡学姐的解答也十分易懂: 面试

再者这个问题可谓是常见的面试题之一,而这张图中只是给出了很是基本的一个先后端交互的过程,因为本身有基础,因此列出的相关概念也都基本理解了,因而就花些时间扩展一下数据库

跟我一块儿来学起来

  • 咱们在打开浏览器,而后在输入URL的时候有没有发现浏览器会给你一些你似曾相识且与你输入的内容相匹配的网址呢?其实咱们在浏览器中输入URL的时候,浏览器就会开始智能的匹配可能URL,浏览器会从历史记录,书签等地方,找到你已经输入的字符串可能对应的URL,而后给出智能提示segmentfault

  • 在输好URL后咱们会按下Enter键,浏览器会发起请求,若是URL是域名而不是IP地址,将进行域名解析,所谓域名解析是指什么呢?windows

    IP地址是网络上标识站点的数字地址,为了方便记忆,采用域名来代替IP地址标识站点地址,域名解析就是域名到IP地址的转换过程。后端

    域名解析按下面的步骤进行(部份内容涉及到计算机网络知识):浏览器

    • 咱们本地硬盘下有一个hosts(windows下路径为C:\Windows\System32\drivers\etc)文件,做用是将一些经常使用的网址域名与其对应的IP地址创建一个关联“数据库”。通常来讲,系统会首先自动从hosts文件中寻找对应的IP地址,若是有的话就直接使用hosts文件里面的IP地址,而后直接进行端口确认
    • 若是上一步没有找到,浏览器将调用解析程序,并成为DNS服务器的一个客户,把待解析的域名放在DNS请求报文中,以UDP用户数据报的方式发给本地DNS服务器
    • 若是本地DNS服务器查找到相应的域名的IP地址,就把对应的IP地址放在回答报文中返回
    • 若是上一步没有找到,即本地DNS服务器不知道被查询域名的IP地址,因为主机向本地DNS服务器的查询是递归查询,因此此时,本地DNS服务器就会以DNS客户的身份向其余根DNS服务器继续发出查询请求报文。本地DNS服务器向根DNS服务器的查询是迭代查询,当找到相应域名的IP地址后,就会把这个结果返回给最初发起查询请求的浏览器

    递归查询: 在该模式下DNS服务器接收到客户机请求,必须返回一个准确的查询结果给客户机。若是该DNS服务器本地没有存储被查询的DNS信息,那么该服务器会(替客户机)询问其余服务器,并将返回的查询结果再返回给客户机。缓存

    迭代查询: 在该模式下DNS服务器接收到客户机请求,若是该DNS服务器本地没有存储被查询的DNS信息,DNS服务器会向客户机提供其余可以解析查询请求的DNS服务器地址,让客户机再向这台DNS服务器提交请求,依次循环直到返回查询的结果为止。服务器

    • 通过上面的步骤后,浏览器已经得到输入域名的IP地址,能够进行下一步了。
  • 浏览器获得IP地址后,还要确认一下端口,默认端口是80端口,一个服务器可能会提供不一样的服务,这些服务经过端口来区分,能够指定端口号网络

  • 浏览器获得IP地址并确认端口后,会向目标服务器发起HTTP请求,HTTP请求是经过TCP链接来发送的(若是是HTTPS则须要先创建SSL链接,再是TCP链接,下面的讨论基于HTTP),具体以下

    • 浏览器会生成目标服务器的HTTP请求报文,请求报文通常包含请求方法、请求URI、协议版本、请求首部字段等内容,HTTP请求准备好后,HTTP请求报文从应用层传到传输层后会被分割为报文段,并会发起一条到达目标服务器的TCP链接,开始TCP三次握手,过程如图所示:

三次握手

通俗的能够理解为:

A主动向B打电话:嗨,能听到吗(SYN=1,seq=x),而后A就开始等待B的回答(SYN-SENT状态),此时A不知道B能不能听到

B听到A的话以后,能够确认它能听到A,可是它还要确认一下A能不能听到他本身的声音,因而B说:我能听到你的声音(ACK=1,ack=x+1),你能听到个人声音吗(SYN=1,seq=y),而后B开始等待A的恢复(SYN-RECD状态)

A听到B的话以后,A能够确认两件事,一是B能听到它说话,二是它也能听到B说话,A已经能够随时说话和倾听了(ESTABLISHED状态)。可是此时的B还在等待中,并不知道A能不能听到,因此此时A须要再回复B说:我能够听到你的声音(ACK=1,ack=y+1),开始愉快的聊天吧~(seq=x+1),B听到这句话后便也能够随时说话和倾听了(ESTABLISHED状态)

以后两我的就能够balabalabala....

  • HTTP请求的请求报文是直接附在第三次握手的消息中
  • 穿插补充小知识,为何是三次握手,而不是两次四次?

有一种观点是三次握手是基于TCP协议的可靠性(Reliability)要求,这是确认双发都能进行收发的最小次数,两次确认不了,四次多余。可是并无彻底意义上的可靠,不论握手多少次都只能代表握手的时候是可靠的,不能保证后面数据传输时一直可靠,由于信道是不可靠的,固然三次握手至少能够代表它曾经可靠,这是两次握手没法完成的,而四次甚至更屡次握手仅仅是提升“它曾经可靠”这个结论的可信程度。因此这个握手也只是确保可靠的一个基本须要,TCP协议的可靠性(注意区分完整性integrity)更多的是由校验和、定时器超时重传、确认机制.

在《计算机网络》一书中也有讲过这个问题,给出的解释是:三次握手是为了防止失效的链接请求报文段被服务端接收,从而产生错误。具体例子以下所述:

  • client发出的一个链接请求报文段并无丢失,而是在某个网络结点长时间的滞留了,以至延误到链接释放之后的某个时间才到达server。原本这是一个早已失效的报文段。但server收到此失效的链接请求报文段后,就误认为是client再次发出的一个新的链接请求。因而就向client发出确认报文段,赞成创建链接。
  • 假设不采用**“三次握手”**,那么只要server发出确认,新的链接就创建了。可是因为如今client并无发出创建链接的请求,所以不会理睬server的确认,也不会向server发送数据。而server却觉得新的链接已经创建,并一直等待client发来数据。这样,server的不少资源就白白浪费掉了。
  • 采用**“三次握手”**的办法能够防止上述现象发生。例如刚才那种状况,client不会向server的确认发出确认。server因为收不到确认,就知道client并无要求创建链接
  • 链接创建以后,开始进行数据传输,虽然浏览器知道目标服务器的IP和端口,可是数据总不可能飞过去吧?HTTP请求报文段会从传输层传到网络层,在网络层被封装成IP数据包,网络层规定了经过怎样的路径(所谓的传输路线)到达目标服务器,并把数据包传送给对方。
  • 网络层封装好的IP数据包会进一步传到下一层---》数据链路层,而后会再次被封装到MAC数据帧结构中,因为IP地址间的通讯依赖于MAC地址(网卡所属的固定地址),因此MAC数据帧结构中会有通过ARP协议解析后的MAC地址(不必定是目标服务器的MAC地址,由于实际上通讯的双方在同一局域网(LAN)内的状况是不多的,通常都会通过路由中转)。
  • 数据链路层的MAC数据帧再向下传,便会到达物理层,这里要注意物理层考虑的是怎样才能在链接各类计算机的传输媒体上传输数据比特流,而不是指具体的传输媒体。 物理层须要确保原始的数据可在各类物理媒体上传输,它规定了传输媒体的机械特性、电气特性、功能特性、过程特性:

常见的传输媒体有双绞线、电缆、光缆、无线信道等,物理层的任务就是要让数据在这些传输媒体上都能能进行传输

  • 经过MAC地址匹配,数据经过传输媒体到达目标服务器的物理层,物理层接收数据比特流而后向上传送到服务器的数据链路层,在数据链路层MAC数据帧将进行封装的逆操做,还原成IP数据包以后向上传送到网络层,网络层也进行封装的逆操做还原成HTTP请求报文段(分割后的一小段一小段的),而后这些报文段向上传到传输层,在传输层按原来的序号从新组装成完整的HTTP请求报文,再向上传到应用层,应用层的HTTP协议便会开始对请求进行处理
  • 这个处理多是直接返回静态的资源,也可能通过PHP、JAVA等语言进行处理等,等处理完成后,会返回一个HTTP响应,它生成一个HTTP响应报文,与HTTP请求报文结构相似,而后这个响应报文会“走过”请求报文来时的路到达浏览器
  • 浏览器接收HTTP响应,而后有可能释放TCP链接,也有可能从新使用这个TCP链接发送新的请求(持久链接),此处了解一下TCP链接的释放,不一样于TCP链接创建的三次握手,TCP链接的释放是四次挥手,客户端和服务器端均可以发起关闭请求,也存在二者同时发起关闭请求的状况,图中为客户端A主动发起关闭请求:

一样通俗的解释一波:

A对B要传的文件已经传完了,因而他对B说:我要传的文件已经传完了,我要准备下线了(seq=u,FIN=1)。而后A就等待B的回复(FIN-WAIT-1状态)

B看到A的消息后,回复A说:知道了,可是我还有文件给你(ACK=1,ack=u+1,seq=v)。B进入等他文件传完的状态(CLOSE-WAIT状态)。A收到B的回复以后,下线不了了,因而继续等待着B的文件传完(FIN-WAIT-2状态)

几分钟后,B的文件传完了,此时他对A说:个人文件传完了,我也要下线了(seq=w,FIN=1,ACK=1,ack=u+1),而后B等待A的回复来确认真的能够下线了(LAST-ACK状态)

A收到B的回复后,便对A说:好的,那你下线吧(ACK=1,seq=u+1,ack=w+1)。此时A会等待一段时间(2MSL,TIME-WAIT状态),B收到后就直接下线了(CLOSE状态),而后2MSL时间到了以后,A也下线(CLOSE状态)

  • 扩展知识小课堂:

    • 为何服务器B在接到A的断开请求时不当即赞成断开?

    当服务器B收到断开链接的请求时,服务器可能仍然有数据未发送完毕,因此服务器先发送确认信号,等全部数据发送完毕后再赞成断开

    • 为何是四次挥手,而不是像创建链接同样的三次?

    由于TCP链接是全双工模式,服务器B收到A的断开请求时,仅仅代表A没有东西传给服务器B了,但此时服务器B可能向A的传输还没结束,因此服务器B要先给A一个确认收到A的断开请求的ACK报文,而后继续向A把信息传完,等传完以后服务器B再向A发送断开请求的报文段,等A收到并回复ACK报文后再释放链接。

    也就是说对于A来讲他要发送请求给B并等待B确认,对于B来讲也要发送请求给A并等待A确认,二者都通过这两个过程才能彻底释放TCP链接,而非单方面的释放。 创建链接只须要创建,没有数据的影响,而释放链接还要考虑数据是否传输完,因此创建链接的时候B确认收到A的创建请求与B发送创建请求这一步能够合成一步成为TCP创建链接的第二次握手,而释放链接时却必须分开。

    • 最后一次握手后A为何要等2MSL

    首先解释一下MSL,MSL是指最长报文段寿命,RFC793建议为两分钟,但实际上可据实际状况而定,也就是说一个报文段最久可存在的时间是MSL

    1. 这是为了保证A发送的最后一个ACK报文可以到达服务器B,若是这个ACK报文丢失了,服务器B没有收到,B会超时重传第三次握手的FIN+ACK报文给A,这个时候处于等待的A就能够收到这个重传的FIN+ACK报文,并再次发送ACK报文给服务器B,而且从新启动2MSL计时器,最终结果是A和B都正常进入CLOSE状态。若是A发完ACK报文后就直接释放了A-->B的链接,那么A就收不到B重传的FIN+ACK报文,也不能从新发送ACK`报文,那么B就没法按正常步骤释放B-->A的链接
    2. 防止“已失效的链接请求报文”出如今下一个新的链接中,由于一个报文段的寿命是MSL,因此A在发送完最后一个ACK报文段以后,再通过时间2MSL,本链接持续的时间内所产生的全部报文段都将在网络中消失,这样这些旧的报文段便不会出如今下一个新的链接中
    • 浏览器以后会检查HTTP的响应状态,主要经过响应码来判断

    1xx: 表示通知信息的,好比请求收到了或正在处理

    2xx:表示成功,操做被成功接收并处理

    3xx:表示重定向,通常完成请求还必须采起进一步的行动

    4xx:表示客户端的差错

    5xx:表示服务器的差错

  • 若是响应可缓存,浏览器将把响应存入缓存

  • 浏览器根据HTTP报头信息解码响应,决定如何处理这些响应,并展示响应,以响应为一个HTML为例 浏览器开始自上而下,自左而右的加载HTML文档,最开始会遇到 <!DOCTYPE>声明,而后根据<!DOCTYPE>声明浏览器就知道该用哪一种规范来解析这个文档

  • 再继续边加载边解析,边生成DOM树,加载过程当中遇到外部CSS文件,浏览器便会另外发出一个请求,来获取CSS文件(过程和上面说的同样),获取CSS后会生成CSS Rule树。DOM树和CSS Rule树生成Render树,页面能够开始边加载边渲染了

    • 渲染树和DOM树的关系:那些不可见的DOM元素(如<head>…</head>,display=none的元素)不会被插入渲染树中;还有像一些节点是绝对定位或浮动,这些节点会在文本流以外,所以他们会在渲染树和DOM树的不一样位置,渲染树标识出真实的位置,并用一个占位结构标识出他们原来的位置,而DOM树上是他们原来的位置
    • 渲染包含"布局"(layout)和"绘制"(paint)这两个步骤,所谓"布局"是指给出每一个DOM节点在浏览器窗口中的准确位置,"绘制"是指遍历Render树将布局好的DOM节点绘制在屏幕上。

  • 浏览器继续加载渲染,若是遇到<script>标签,浏览器会当即执行(暂不考虑defer及async属性),此时会出现页面阻塞,不只要等待文档中JS文件下载加载完毕,还要等待JS解析执行完毕,才能够恢复HTML文档的加载解析。
    • 这是浏览器为了防止出现JS修改DOM树,须要从新构建DOM树的状况,DOM树改变浏览器须要回过头来从新渲染这部分代码,因此浏览器但愿经过阻塞其余内容的下载和呈现,来避免出现更多的没必要要的Reflow(称为回流或者重排)
    • 若是<script>放在的<head>中,则<body>标签没法被加载,那么页面天然就没法渲染了,所以这将致使在该JS代码彻底执行完以前,页面都是一片空白,用户体验很是很差,通常我看到长时间的空白页面,我都很是想直接关闭它。所以会推荐将全部<script>标签尽量放到<body>标签的底部,以尽可能减小对整个页面下载的影响,此时虽然还会存在一个脚本阻塞另外一个脚本的问题,可是用户体验比上面的好不少,由于用户看到了大部份内容,而不是空白
    • defer属性至关于告诉浏览器当即下载,延迟执行。它使得加载后续文档元素的过程将和JS文件的加载并行进行(异步),可是JS文件的执行要在整个页面解析完成以后,DOMContentLoaded事件触发以前完成,执行顺序为出现的前后顺序。(高程中指出现实中不必定会按照顺序执行,也不必定会在DOMContentLoaded事件触发以前完成,所以最好只包含一个延迟脚本,这多是与浏览器的实现有关,具体什么状况下会出现我还不知道???)
    • async属性至关于告诉浏览器当即下载执行,而且页面的加载渲染不须要等待该脚本加载和执行,它们二者会异步进行。标记为async的脚本不会按照它们出现的前后顺序执行,而是谁先下载完了谁就先执行,它们必定会在页面的load事件触发以前执行,但可能会在DOMContentLoaded事件触发以前或以后执行。基于前面所说的一点缘由,异步脚本最好不要修改DOM,若是由多个异步脚本,它们之间最好没有依赖关系
  • 浏览器继续加载渲染,若是遇到图片资源,浏览器也会另外发出一个请求,来获取图片资源,这是异步请求,因此不会等到图片下载完,而是继续渲染后面的HTML文档。
  • 等到服务器返回图片文件,若是先前并无为这个图片设定宽高,那么因为图片占用了必定面积,影响了后面段落的排布,浏览器会进行Reflow
  • 而后而后终于和</html>碰面了,这次的页面加载渲染过程完成,浏览器也是很累了,而后会当即触发DOMContentLoaded事件,该事件是在造成完整的DOM树以后就会触发,而不会理会图像、JS文件、CSS文件或其余资源是否已经下载完毕
  • 当页面彻底加载后,也就是全部图像、JS文件、CSS文件等外部资源都加载完成后会触发load事件
  • 用户在页面上进行交互时,可能会致使页面进行Repaint或Reflow
    • Repaint: 若是只是改变了某个元素的背景颜色,文字颜色等,不影响元素周围或内部布局的属性,将只会引发浏览器的Repaint,重绘某一部分
    • Reflow: 若是某个部分发生了的变化影响了布局,那浏览器就须要倒回去从新渲染,每次Reflow必然会致使Repaint

尾声

原本只是想了解了解,结果一入深似海,看似简单的操做背后藏着数不清的小动做,文中也只是涉及了一部分,还有不少相关的过程没有涉及到,可是能力有限,仍是慢慢来,暂时就先告一段落,文中若有错误还请指正哦~

相关文章
相关标签/搜索