从输入URL到页面展现,你想知道些什么?

整理下最近面试出现的频率比较高的一个问题,也就是:在浏览器中输入URL到整个页面显示在用户面前时这个过程当中到底发生了什么? 老生常谈的问题,可是,这个问题确实涉及很是多的东西,因而,再从新整理一遍:php

整体来讲共分红一下几个过程:

  • URL输入
  • DNS解析
  • TCP链接
  • 发送HTTP请求
  • 服务器处理请求
  • 服务器响应请求
  • 浏览器解析渲染页面
  • 链接结束

输入URL

URL中文名叫作统一资源定位符,统一资源定位符是对能够从互联网上获得的资源的位置和访问方法的一种简洁的表示,是互联网上标准资源的地址。互联网上的每一个文件都有一个惟一的URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它。html

主要组成部分:protocol :// hostname[:port] / path / [;parameters][?query]#fragment前端

  • protocol(协议)
  • hostname(主机名)
  • port(端口号)
  • path(路径)
  • parameters(参数)
  • query(查询)
  • fragment(信息片段)

当咱们开始在浏览器中输入网址的时候,浏览器其实就已经在智能的匹配可能得 url 了,他会从历史记录,书签等地方,找到已经输入的字符串可能对应的 url,而后给出智能提示,让你能够补全url地址。对于 google的chrome 的浏览器,他甚至会直接从缓存中把网页展现出来,就是说,你尚未按下 enter,页面就出来了。nginx

DNS解析

DNS解析的过程就是寻找哪台机器上有你须要资源的过程。当你在浏览器中输入一个地址时,例如www.baidu.com,其实不是百度网站真正意义上的地址。互联网上每一台计算机的惟一标识是它的IP地址,可是IP地址并不方便记忆。用户更喜欢用方便记忆的网址去寻找互联网上的其它计算机,也就是上面提到的百度的网址。因此互联网设计者须要在用户的方便性与可用性方面作一个权衡,这个权衡就是一个网址到IP地址的转换,这个过程就是DNS解析。它实际上充当了一个翻译的角色,实现了网址到IP地址的转换。网址到IP地址转换的过程是如何进行的?git

查找顺序: 浏览器缓存--> 操做系统缓存--> 本地host文件 --> 路由器缓存--> ISP DNS缓存 --> 顶级DNS服务器/根DNS服务器github

​ 一、浏览器缓存 :首先会向浏览器的缓存中读取上一次访问的记录,在chrome能够经过地址栏中输入chrome://net-internals/#dns查看缓存的当前状态 。面试

​ 二、操做系统缓存: 查找存储在系统运行内存中的缓存。在mac中能够经过下面的命令清除系统中的DNS缓存。chrome

dscacheutil -flushcache
复制代码

​ 三、 **hosts 文件:**查看本地硬盘的 hosts 文件,看看其中有没有和这个域名对应的规则,若是有的话就直接使用 hosts 文件里面的 ip 地址。数据库

​ 四、路由器缓存: 有些路由器也有DNS缓存的功能,访问过的域名会存在路由器上。segmentfault

​ 五、ISP DNS缓存:互联网服务提供商(如中国电信)也会提供DNS服务,好比比较著名的 114.114.114.114,在本地查找不到的状况下,就会向ISP进行查询,ISP会在当前服务器的缓存内查找是否有记录,若是有,则返回这个IP,若没有,则会开始向根域名服务器请求查询。

​ 六、顶级DNS服务器/根DNS服务器:根域名收到请求后,会判别这个域名(.com)是受权给哪台服务器管理,并返回这个顶级DNS服务器的IP。请求者收到这台顶级DNS的服务器IP后,会向该服务器发起查询,若是该服务器没法解析,该服务器就会返回下一级的DNS服务器IP(nicefilm.com),本机继续查找,直到服务器找到(www.nicefilm.com)的主机。

​ 六、最后,本地DNS服务器向域名的解析服务器发出请求,这时就能收到一个域名和IP地址对应关系,本地DNS服务器不只要把IP地址返回给用户电脑,还要把这个对应关系保存在缓存中,以备下次别的用户查询时,能够直接返回结果,加快网络访问。

下面这张图很完美的解释了这一过程:

optional title

上述图片是查找www.google.com的IP地址过程。首先在本地域名服务器中查询IP地址,若是没有找到的状况下,本地域名服务器会向根域名服务器发送一个请求,若是根域名服务器也不存在该域名时,本地域名会向com顶级域名服务器发送一个请求,依次类推下去。直到最后本地域名服务器获得google的IP地址并把它缓存到本地,供下次查询使用。从上述过程当中,能够看出网址的解析是一个从右向左的过程: com -> google.com -> www.google.com。可是你是否发现少了点什么,根域名服务器的解析过程呢?事实上,真正的网址是www.google.com.,并非我多打了一个.,这个.对应的就是根域名服务器,默认状况下全部的网址的最后一位都是.,既然是默认状况下,为了方便用户,一般都会省略,浏览器在请求DNS的时候会自动加上,全部网址真正的解析过程为: . -> .com -> google.com. -> www.google.com.。

补充:

  1. 什么是DNS?

      DNS(Domain Name System,域名系统),因特网上做为域名和IP地址相互映射的一个分布式数据库,可以使用户更方便的访问互联网,而不用去记住可以被机器直接读取的IP数串。经过主机名,最终获得该主机名对应的IP地址的过程叫作域名解析(或主机名解析)。

      通俗的讲,咱们更习惯于记住一个网站的名字,好比www.baidu.com,而不是记住它的ip地址,好比:167.23.10.2。而计算机更擅长记住网站的ip地址,而不是像www.baidu.com等连接。由于,DNS就至关于一个电话本,好比你要找www.baidu.com这个域名,那我翻一翻个人电话本,我就知道,哦,它的电话(ip)是167.23.10.2。

  2. DNS查询的两种方式:递归查询和迭代查询

    2.一、递归解析

    ​ 当局部DNS服务器本身不能回答客户机的DNS查询时,它就须要向其余DNS服务器进行查询。此时有两种方式,如图所示的是递归方式。局部DNS服务器本身负责向其余DNS服务器进行查询,通常是先向该域名的根域服务器查询,再由根域名服务器一级级向下查询。最后获得的查询结果返回给局部DNS服务器,再由局部DNS服务器返回给客户端。

    img

    2.二、迭代解析

      当局部DNS服务器本身不能回答客户机的DNS查询时,也能够经过迭代查询的方式进行解析,如图所示。局部DNS服务器不是本身向其余DNS服务器进行查询,而是把能解析该域名的其余DNS服务器的IP地址返回给客户端DNS程序,客户端DNS程序再继续向这些DNS服务器进行查询,直到获得查询结果为止。也就是说,迭代解析只是帮你找到相关的服务器而已,而不会帮你去查。好比说:baidu.com的服务器ip地址在192.168.4.5这里,你本身去查吧,本人比较忙,只能帮你到这里了。

    img

  3. DNS域名称空间的组织方式

    咱们在前面有说到根DNS服务器,域DNS服务器,这些都是DNS域名称空间的组织方式。按其功能命名空间中用来描述 DNS 域名称的五个类别的介绍详见下表中,以及与每一个名称类型的示例

    img

  4. DNS优化

    了解了DNS的过程,能够为咱们带来哪些?上文中请求到google的IP地址时,经历了8个步骤,这个过程当中存在多个请求(同时存在UDP和TCP请求,为何有两种请求方式,请自行查找)。若是每次都通过这么多步骤,是否太耗时间?如何减小该过程的步骤呢?那就是DNS缓存。

    4.1 DNS缓存

    DNS存在着多级缓存,从离浏览器的距离排序的话,有如下几种: 浏览器缓存,系统缓存,路由器缓存,IPS服务器缓存,根域名服务器缓存,顶级域名服务器缓存,主域名服务器缓存。

    • 在你的chrome浏览器中输入:chrome://dns/,你能够看到chrome浏览器的DNS缓存。
    • 系统缓存主要存在/etc/hosts(Linux系统)中:

    DNS系统缓存

    4.2 DNS负载均衡

      当一个网站有足够多的用户的时候,假如每次请求的资源都位于同一台机器上面,那么这台机器随时可能会蹦掉。处理办法就是用DNS负载均衡技术,它的原理是在DNS服务器中为同一个主机名配置多个IP地址,在应答DNS查询时,DNS服务器对每一个查询将以DNS文件中主机记录的IP地址按顺序返回不一样的解析结果,将客户端的访问引导到不一样的机器上去,使得不一样的客户端访问不一样的服务器,从而达到负载均衡的目的。例如能够根据每台机器的负载量,该机器离用户地理位置的距离等等。

创建TCP链接

  拿到域名对应的IP地址以后,浏览器会以一个随机端口(1024<端口<65535)向服务器的WEB程序(经常使用的有httpd,nginx等)80端口发起TCP的链接请求。这个链接请求到达服务器端后(这中间经过各类路由设备,局域网内除外),进入到网卡,而后是进入到内核的TCP/IP协议栈(用于识别该链接请求,解封包,一层一层的剥开),还有可能要通过Netfilter防火墙(属于内核的模块)的过滤,最终到达WEB程序,最终创建了TCP/IP的链接。

TCP链接如图所示:

img

三次握手以创建TCP链接

第一次握手:创建链接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。

第二次握手服务器收到syn包,必须确认客户的SYN(ack=j+1),同时本身也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;

第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP链接成功)状态,完成三次握手。

补充:

为什须要三次握手?

​ 《计算机网络》第四版中讲“三次握手”的目的是“为了防止已失效的链接请求报文段忽然又传送到了服务端,于是产生错误”,书中的例子是这样的,“已失效的链接请求报文段”的产生在这样一种状况下:client发出的第一个链接请求报文段并无丢失,而是在某个网络结点长时间的滞留了,以至延误到链接释放之后的某个时间才到达server。原本这是一个早已失效的报文段。但server收到此失效的链接请求报文段后,就误认为是client再次发出的一个新的链接请求。因而就向client发出确认报文段,赞成创建链接。假设不采用“三次握手”,那么只要server发出确认,新的链接就创建了。因为如今client并无发出创建链接的请求,所以不会理睬server的确认,也不会向server发送数据。但server却觉得新的运输链接已经创建,并一直等待client发来数据。这样,server的不少资源就白白浪费掉了。采用“三次握手”的办法能够防止上述现象发生。例如刚才那种状况,client不会向server的确认发出确认。server因为收不到确认,就知道client并无要求创建链接。”。主要目的防止server端一直等待,浪费资源。

发送HTTP请求

创建了TCP链接以后,发起一个http请求。一个典型的 http request header 通常须要包括请求的方法,例如 GET 或者 POST 等,不经常使用的还有 PUT 和 DELETE 、HEAD、OPTION以及 TRACE 方法,通常的浏览器只能发起 GET 或者 POST 请求。   

客户端向服务器发起http请求的时候,会有一些请求信息,请求信息包含三个部分:

  • 请求方法URI协议/版本
  • 请求头(Request Header)
  • 请求正文:

下面是一个完整的HTTP请求例子:

GET/sample.jspHTTP/1.1
Accept:image/gif.image/jpeg,*/*
Accept-Language:zh-cn
Connection:Keep-Alive
Host:localhost
User-Agent:Mozila/4.0(compatible;MSIE5.01;Window NT5.0)
Accept-Encoding:gzip,deflate

username=jinqiao&password=1234
复制代码

注意:最后一个请求头以后是一个空行,发送回车符和换行符,通知服务器如下再也不有请求头。

(1)请求的第一行是“方法URL议/版本”:GET/sample.jsp HTTP/1.1 (2)请求头(Request Header)    请求头包含许多有关的客户端环境和请求正文的有用信息。例如,请求头能够声明浏览器所用的语言,请求正文的长度等。

Accept:image/gif.image/jpeg.*/*
Accept-Language:zh-cn
Connection:Keep-Alive
Host:localhost
User-Agent:Mozila/4.0(compatible:MSIE5.01:Windows NT5.0)
Accept-Encoding:gzip,deflate.
复制代码

(3)请求正文 请求头和请求正文之间是一个空行,这个行很是重要,它表示请求头已经结束,接下来的是请求正文。请求正文中能够包含客户提交的查询字符串信息:

username=jinqiao&password=1234
复制代码

服务器永久重定向

​ 服务器给浏览器响应一个301永久重定向响应,这样浏览器就会访问http://www.google.com/ 而非http://google.com/。   

​ 为何服务器必定要重定向而不是直接发送用户想看的网页内容呢?其中一个缘由跟搜索引擎排名有关。若是一个页面有两个地址,就像http://www.yy.com/http://yy.com/,搜索引擎会认为它们是两个网站,结果形成每一个搜索连接都减小从而下降排名。而搜索引擎知道301永久重定向是什么意思,这样就会把访问带www的和不带www的地址归到同一个网站排名下。还有就是用不一样的地址会形成缓存友好性变差,当一个页面有好几个名字时,它可能会在缓存里出现好几回。

补充:

一、301和302的区别:

  301和302状态码都表示重定向,就是说浏览器在拿到服务器返回的这个状态码后会自动跳转到一个新的URL地址,这个地址能够从响应的Location首部中获取(用户看到的效果就是他输入的地址A瞬间变成了另外一个地址B)——这是它们的共同点。   他们的不一样在于。301表示旧地址A的资源已经被永久地移除了(这个资源不可访问了),搜索引擎在抓取新内容的同时也将旧的网址交换为重定向以后的网址;   302表示旧地址A的资源还在(仍然能够访问),这个重定向只是临时地从旧地址A跳转到地址B,搜索引擎会抓取新的内容而保存旧的网址。 SEO302好于301

二、重定向缘由:

(1)网站调整(如改变网页目录结构); (2)网页被移到一个新地址; (3)网页扩展名改变(如应用须要把.php改为.Html或.shtml)。 这种状况下,若是不作重定向,则用户收藏夹或搜索引擎数据库中旧地址只能让访问客户获得一个404页面错误信息,访问流量白白丧失;再者某些注册了多个域名的网站,也须要经过重定向让访问这些域名的用户自动跳转到主站点等。

三、何时进行301或者302跳转呢?

当一个网站或者网页24—48小时内临时移动到一个新的位置,这时候就要进行302跳转,而使用301跳转的场景就是以前的网站由于某种缘由须要移除掉,而后要到新的地址访问,是永久性的。 清晰明确而言:使用301跳转的大概场景以下:

  • 域名到期不想续费(或者发现了更适合网站的域名),想换个域名。
  • 在搜索引擎的搜索结果中出现了不带www的域名,而带www的域名却没有收录,这个时候能够用301重定向来告诉搜索引擎咱们目标的域名是哪个。
  • 空间服务器不稳定,换空间的时候。

服务器处理请求

​ 通过前面的重重步骤,咱们终于将咱们的http请求发送到了服务器这里,其实前面的重定向已是到达服务器了,那么,服务器是如何处理咱们的请求的呢?   

​ 后端从在固定的端口接收到TCP报文开始,它会对TCP链接进行处理,对HTTP协议进行解析,并按照报文格式进一步封装成HTTP Request对象,供上层使用。   

​ 一些大一点的网站会将你的请求到反向代理服务器中,由于当网站访问量很是大,网站愈来愈慢,一台服务器已经不够用了。因而将同一个应用部署在多台服务器上,将大量用户的请求分配给多台机器处理。此时,客户端不是直接经过HTTP协议访问某网站应用服务器,而是先请求到Nginx,Nginx再请求应用服务器,而后将结果返回给客户端,这里Nginx的做用是反向代理服务器。同时也带来了一个好处,其中一台服务器万一挂了,只要还有其余服务器正常运行,就不会影响用户使用。

如图所示:

img

补充:

一、什么是反向代理?

客户端原本能够直接经过HTTP协议访问某网站应用服务器,网站管理员能够在中间加上一个Nginx,客户端请求Nginx,Nginx请求应用服务器,而后将结果返回给客户端,此时Nginx就是反向代理服务器。

img

服务器返回一个 HTTP 响应 

通过前面的6个步骤,服务器收到了咱们的请求,也处理咱们的请求,到这一步,它会把它的处理结果返回,也就是返回一个HTPP响应。 HTTP响应与HTTP请求类似,HTTP响应也由3个部分构成,分别是:

  • 状态行
  • 响应头(Response Header)
  • 空行
  • 响应正文
HTTP/1.1 200 OK
Date: Sat, 31 Dec 2005 23:59:59 GMT
Content-Type: text/html;charset=ISO-8859-1
Content-Length: 122

<html>
<head>
<title>http</title>
</head>
<body>
<!-- body goes here -->
</body>
</html>

复制代码

状态行: 状态行由协议版本、数字形式的状态代码、及相应的状态描述,各元素之间以空格分隔。 格式: HTTP-Version Status-Code Reason-Phrase CRLF 例如: HTTP/1.1 200 OK \r\n | -协议版本:是用http1.0仍是其余版本 | -状态描述:状态描述给出了关于状态代码的简短的文字描述。好比状态代码为200时的描述为 ok | -态代码:状态代码由三位数字组成,第一个数字定义了响应的类别,且有五种可能取值。以下

1xx:信息性状态码,表示服务器已接收了客户端请求,客户端可继续发送请求。

  • 100 Continue
  • 101 Switching Protocols

2xx:成功状态码,表示服务器已成功接收到请求并进行处理。

  • 200 OK 表示客户端请求成功
  • 204 No Content 成功,但不返回任何实体的主体部分
  • 206 Partial Content 成功执行了一个范围(Range)请求

3xx: 重定向状态码,表示服务器要求客户端重定向。

  • 301 Moved Permanently 永久性重定向,响应报文的Location首部应该有该资源的新URL
  • 302 Found 临时性重定向,响应报文的Location首部给出的URL用来临时定位资源
  • 303 See Other 请求的资源存在着另外一个URI,客户端应使用GET方法定向获取请求的资源
  • 304 Not Modified 服务器内容没有更新,能够直接读取浏览器缓存
  • 307 Temporary Redirect 临时重定向。与302 Found含义同样。302禁止POST变换为GET,但实际使用时并不必定,307则更多浏览器可能会遵循这一标准,但也依赖于浏览器具体实现

4xx:客户端错误状态码,表示客户端的请求有非法内容。

  • 400 Bad Request 表示客户端请求有语法错误,不能被服务器所理解
  • 401 Unauthonzed 表示请求未经受权,该状态代码必须与 WWW-Authenticate 报头域一块儿使用
  • 403 Forbidden 表示服务器收到请求,可是拒绝提供服务,一般会在响应正文中给出不提供服务的缘由
  • 404 Not Found 请求的资源不存在,例如,输入了错误的URL

5xx:服务器错误状态码,表示服务器未能正常处理客户端的请求而出现意外错误。

  • 500 Internel Server Error 表示服务器发生不可预期的错误,致使没法完成客户端的请求
  • 503 Service Unavailable 表示服务器当前不可以处理客户端的请求,在一段时间以后,服务器可能会恢复正常

响应头:   

​ 响应头部:由关键字/值对组成,每行一对,关键字和值用英文冒号”:”分隔,典型的响应头有:

img

响应正文

包含着咱们须要的一些具体信息,好比cookie,html,image,后端返回的请求数据等等。这里须要注意,响应正文和响应头之间有一行空格,表示响应头的信息到空格为止,下图是fiddler抓到的请求正文,红色框中的:响应正文:

img

浏览器显示 HTML

浏览器在收到HTML,CSS,JS文件后,它是如何把页面呈现到屏幕上的?下图对应的就是WebKit渲染的过程。

构建dom树 -> 构建render树 -> 布局render树 -> 绘制render树

1527843133578

渲染过程:

​ 浏览器是一个边解析边渲染的过程。首先浏览器解析HTML文件构建DOM树,而后解析CSS文件构建渲染树,等到渲染树构建完成后,浏览器开始布局渲染树并将其绘制到屏幕上。这个过程比较复杂,涉及到两个概念: reflow(回流)和repain(重绘)。DOM节点中的各个元素都是以盒模型的形式存在,这些都须要浏览器去计算其位置和大小等,这个过程称为relow;当盒模型的位置,大小以及其余属性,如颜色,字体,等肯定下来以后,浏览器便开始绘制内容,这个过程称为repain。页面在首次加载时必然会经历reflow和repain。reflow和repain过程是很是消耗性能的,尤为是在移动设备上,它会破坏用户体验,有时会形成页面卡顿。因此咱们应该尽量少的减小reflow和repain。

​ 当文档加载过程当中遇到js文件,html文档会挂起渲染(加载解析渲染同步)的线程,不只要等待文档中js文件加载完毕,还要等待解析执行完毕,才能够恢复html文档的渲染线程。由于JS有可能会修改DOM,最为经典的document.write,这意味着,在JS执行完成前,后续全部资源的下载多是没有必要的,这是js阻塞后续资源下载的根本缘由。因此我明平时的代码中,js是放在html文档末尾的。

​ JS的解析是由浏览器中的JS解析引擎完成的。JS是单线程运行,也就是说,在同一个时间内只能作一件事,全部的任务都须要排队,前一个任务结束,后一个任务才能开始。可是又存在某些任务比较耗时,如IO读写等,因此须要一种机制能够先执行排在后面的任务,这就是:同步任务(synchronous)和异步任务(asynchronous)。

​ JS的执行机制就能够看作是一个主线程加上一个任务队列(task queue)。同步任务就是放在主线程上执行的任务,异步任务是放在任务队列中的任务。全部的同步任务在主线程上执行,造成一个执行栈;异步任务有了运行结果就会在任务队列中放置一个事件;脚本运行时先依次运行执行栈,而后会从任务队列里提取事件,运行任务队列中的任务,这个过程是不断重复的,因此又叫作事件循环(Event loop)。

img

链接结束

​ 如今的页面为了优化请求的耗时,默认都会开启持久链接(keep-alive),那么一个TCP链接确切关闭的时机,是这个tab标签页关闭的时候。这个关闭的过程就是著名的四次挥手。关闭是一个全双工的过程,发包的顺序的不必定的。通常来讲是客户端主动发起的关闭,过程以下。

对于一个已经创建的链接,TCP使用改进的三次握手来释放链接(使用一个带有FIN附加标记的报文段)。TCP关闭链接的步骤以下:

第一步,当主机A的应用程序通知TCP数据已经发送完毕时,TCP向主机B发送一个带有FIN附加标记的报文段(FIN表示英文finish)。

第二步,主机B收到这个FIN报文段以后,并不当即用FIN报文段回复主机A,而是先向主机A发送一个确认序号ACK,同时通知本身相应的应用程序:对方要求关闭链接(先发送ACK的目的是为了防止在这段时间内,对方重传FIN报文段)。

第三步,主机B的应用程序告诉TCP:我要完全的关闭链接,TCP向主机A送一个FIN报文段。

第四步,主机A收到这个FIN报文段后,向主机B发送一个ACK表示链接完全释放。

补充:

为何链接的时候是三次握手,关闭的时候倒是四次握手?

答:由于当Server端收到Client端的SYN链接请求报文后,能够直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。可是关闭链接时,当Server端收到FIN报文时,极可能并不会当即关闭SOCKET,因此只能先回复一个ACK报文,告诉Client端,"你发的FIN报文我收到了"。只有等到我Server端全部的报文都发送完了,我才能发送FIN报文,所以不能一块儿发送。故须要四步握手。

总结

​ 至此,整篇文章暂时到这里,文章内容大部分为参考网上信息,还有不少细节点须要去整理概括,若有不足,但愿多多指出!

参考文献:

老生常谈-从输入url到页面展现到底发生了什么

前端经典面试题: 从输入URL到页面加载发生了什么?

从输入url到页面展示发生了什么?