当浏览器输入一个url请求会经历什么?
- 浏览器查看缓存
- DNS域名解析
- 浏览器与目标服务器建立TCP连接
- 浏览器通过http协议发送请求
- 某些服务会做永久重定向响应
- 浏览器跟踪重定向地址
- 服务器处理请求,服务器发出一个HTTP响应
- 浏览器接收响应对象,释放TCP连接
- 浏览器解析HTML文档
- 浏览器布局渲染页面
分点解析
① 如果资源未缓存,或者不够新鲜,发起新请求
② 如果已缓存,检验是否足够新鲜,足够新鲜直接提供给客户端,否则与服务器进行验证。
PS:检验新鲜通常有两个HTTP头进行控制Expires和Cache-Control:
HTTP1.0提供Expires,值为一个绝对时间表示缓存新鲜日期
HTTP1.1增加了Cache-Control: max-age=,值为以秒为单位的最大新鲜时间
浏览器会把输入的域名解析成对应的IP,其过程如下:
1.查找浏览器缓存:因为浏览器一般会缓存DNS记录一段时间,不同浏览器的时间可能不一样,一般2-30分钟不等,浏览器去查找这些缓存,如果有缓存,直接返回IP,否则下一步。
2.查找系统缓存:浏览器缓存中找不到IP之后,浏览器会进行系统调用(windows中是gethostbyname),查找本机的hosts文件,如果找到,直接返回IP,否则下一步。
3.查找路由器缓存:如果1,2步都查询无果,则需要借助网络,路由器一般都有自己的DNS缓存,将前面的请求发给路由器,查找ISP 服务商缓存 DNS的服务器,如果查找到IP则直接返回,没有的话继续查找。
4.递归查询:如果以上步骤还找不到,则ISP的DNS服务器就会进行递归查询,所谓递归查询就是如果主机所询问的本地域名服务器不知道被查询域名的IP地址,那么本地域名服务器就以DNS客户的身份,向其他根域名服务器继续发出查询请求报文,而不是让该主机自己进行下一步查询。(本地域名服务器地址是通过DHPC协议获取地址,DHPC是负责分配IP地址的)
5.迭代查询:本地域名服务器采用迭代查询,它先向一个根域名服务器查询。本地域名服务器向根域名服务器的查询一般都是采用迭代查询。所谓迭代查询就是当根域名服务器收到本地域名服务器发出的查询请求报文后,告诉本地域名服务器下一步应该查询哪一个域名服务器,然后本地域名服务器自己进行后续的查询。(而不是替代本地域名服务器进行后续查询)。
建立TCP连接(三次握手)
刚开始客户端处于 Closed 的状态,服务端处于 Listen 状态。
进行三次握手:
第一次握手:客户端给服务端发一个 SYN 报文,并指明客户端的初始化*** ISN。此时客户端处于 SYN_SENT状态。
首部的同步位SYN=1,初始序号seq=x,SYN=1
第二次握手:服务器收到客户端的 SYN 报文之后,会以自己的 SYN 报文作为应答,并且也是指定了自己的初始化*** ISN(s)。同时会把客户端的 ISN + 1 作为ACK 的值,表示自己已经收到了客户端的 SYN,此时服务器处于 SYN_RCVD的状态。
在确认报文段中SYN=1,ACK=1,确认号ack=x+1,初始序号seq=y。
第三次握手:客户端收到 SYN 报文之后,会发送一个 ACK 报文,当然,也是一样把服务器的 ISN + 1 作为 ACK 的值,表示已经收到了服务端的 SYN 报文,此时客户端处于 ESTABLISHED 状态。服务器收到 ACK 报文之后,也处于 ESTABLISHED 状态,此时,双方已建立起了连接。
确认报文段ACK=1,确认号ack=y+1,序号seq=x+1。
SYN表同步 seq*** ACK:Acknowledgment Number响应字段,表应答
ISN随时间而变化,因此每个连接都将具有不同的ISN。
- http请求(Request)组成:请求行(起始行)(request line)、请求头部(header)、空行和请求数据四个部分组成。
第一部分:请求起始行,用来说明请求类型,要访问的资源及所使用的HTTP版本
第二部分:请求头部,紧接着请求行(即第一行)之后的部分,用来说明服务器要使用的附加信息
第三部分:空行,请求头部后面的空行是必须的
第四部分:请求数据也叫主体,可以添加任意的其他数据。
- HTTP请求方法
HTTP1.0定义了三种请求方法: GET, POST 和 HEAD方法。
HTTP1.1新增了五种请求方法:OPTIONS, PUT, DELETE, TRACE 和 CONNECT 方法。
GET 请求指定的页面信息,并返回实体主体。
HEAD 类似于get请求,只不过返回的响应中没有具体的内容,用于获取报头
POST 向指定资源提交数据进行处理请求(如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改。
PUT 从客户端向服务器传送内容取代指定的原文档的内容,即向指定资源位置上传其最新内容。
DELETE 请求服务器删除指定的页面。
CONNECT HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。
OPTIONS 允许客户端查看服务器的性能。
TRACE 回显服务器收到的请求,主要用于测试或诊断。
对于大型网站存在多个主机站点为了负载均衡或者导入流量,提高SEO(搜索引擎优化)排名,往往不会直接返回请求页面,而是重定向。返回的响应报文状态码就不是200 OK,而是301,302以3开头的重定向码。
重定向的作用:重定向是为了负载均衡或者导入流量,提高SEO排名。
利用一个前端服务器接受请求,然后负载到不同的主机上,可以大大提高站点的业务并发处理能力;
重定向也可将多个域名的访问,集中到一个站点;由于baidu.com,www.baidu.com会被搜索引擎认为是两个网站,照成每个的链接数都会减少从而降低排名,永久重定向会将两个地址关联起来,搜索引擎会认为是同一个网站,从而提高排名。
301代表永久性转移(Permanently Moved),301重定向是网页更改地址后对搜索引擎友好的最好方法,只要不是暂时搬移的情况,都建议使用301来做转址。
302代表暂时性转移(Temporarily Moved )。
浏览器在获取了重定向响应报文,在响应报文中Location项找到重定向地址,重新发送一个http请求,发送内容与之前的请求内容一致。
返回HTTP响应对象:服务器解析请求并处理请求à构建响应信息à发送HTTP响应对象,此时状态码为200 OK
返回状态码200 OK,表示服务器可以响应请求,返回报文,由于在报头中Content-type为“text/html”,浏览器以HTML形式呈现,而不是下载文件。
- HTTP响应对象(Response)
HTTP响应四个部分组成,分别是:状态行、消息报头、空行和响应正文。
第一部分:状态行,由HTTP协议版本号, 状态码, 状态消息 三部分组成。
第一行为状态行,(HTTP/1.1)表明HTTP版本为1.1版本,状态码为200,状态消息为(ok)
第二部分:消息报头,用来说明客户端要使用的一些附加信息
第二行和第三行为消息报头,
Date:生成响应的日期和时间;Content-Type:指定了MIME类型的HTML(text/html),编码类型是UTF-8
第三部分:空行,消息报头后面的空行是必须的
第四部分:响应正文,服务器返回给客户端的文本信息。
空行后面的html部分为响应正文。
- HTTP状态码
1xx:指示信息--表示请求已接收,继续处理
2xx:成功--表示请求已被成功接收、理解、接受
3xx:重定向--要完成请求必须进行更进一步的操作
4xx:客户端错误--请求有语法错误或请求无法实现
5xx:服务器端错误--服务器未能实现合法的请求
四次挥手终止TCP连接:
原因:由TCP的半关闭(half-close)造成的。所谓的半关闭,其实就是TCP提供了连接的一端在结束它的发送后还能接收来自另一端数据的能力。
刚开始双方都处于ESTABLISHED 状态,假如是客户端先发起关闭请求。四次挥手的过程如下:
- 第一次挥手:客户端发送连接释放报文段(FIN=1,序号seq=u)。
客户端处于 FIN_WAIT1 状态,并停止再发送数据,主动关闭TCP连接。
- 第二次挥手:服务端发出确认报文段(ACK=1,确认号ack=u+1,序号seq=v),此时服务端处于 CLOSE_WAIT 状态。
此时的TCP处于半关闭状态,客户端到服务端的连接释放。
客户端进入FIN_WAIT2 (终止等待2)状态,等待服务端发出的连接释放报文段。
- 第三次挥手:服务端也想断开连接,服务端发出连接释放报文段(FIN=1,ACK=1,序号seq=w,确认号ack=u+1)。此时服务端处于 LAST_ACK 的状态。
- 第四次挥手:客户端发出确认报文段(ACK=1,seq=u+1,ack=w+1),客户端处于 TIME_WAIT 状态。
此时TCP未释放掉,经过时间等待计时器设置的时间2MSL后,客户端进入CLOSED状态,
服务端收到 ACK 报文之后,就处于关闭连接了,处于 CLOSED 状态。
收到一个FIN只意味着在这一方向上没有数据流动。客户端执行主动关闭并进入TIME_WAIT是正常的,服务端通常执行被动关闭,不会进入TIME_WAIT状态。
为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?
浏览器接收服务器响应结果,如有压缩则首先解压处理,接着就是页面解析渲染
解析渲染该过程主要分为以下步骤:
当浏览器获得一个html文件时,会“自上而下”加载,并在加载过程中进行解析渲染。
解析:
-
构建DOM树:浏览器会将HTML解析成一个DOM树,DOM 树的构建过程是一个深度遍历过程:当前节点的所有子节点都构建好后才会去构建当前节点的下一个兄弟节点。
-
DOM树构建过程中:首先开启下载线程,对所有的资源进行优先级排序下载(注意,这里仅仅是下载)。同时主线程会对文档进行解析:
- 遇到 script 标签时,首先阻塞后续内容的解析,同时检查该script是否已经下载下来,如果已下载,便执行代码。
- 遇到 link 标签时,不会阻塞后续内容的解析(比如 DOM 构建),检查 link 资源是否已下载,如果已下载,则构建 cssom。
- 遇到 DOM 标签时,执行 DOM 构建,将该 DOM 元素添加到DOM树中。
-
DOM树构建过程中:首先开启下载线程,对所有的资源进行优先级排序下载(注意,这里仅仅是下载)。同时主线程会对文档进行解析:
2. 构建CSSOM树:将CSS解析成 CSS Rule Tree(CSSOM树) 。
3. 构建渲染树:根据DOM树和CSSOM来构造 Rendering Tree。注意:Rendering Tree 渲染树并不等同于 DOM 树,因为一些像 Header 或 display:none 的东西就没必要放在渲染树中了。
4. 渲染树布局:有了Render Tree,浏览器已经能知道网页中有哪些节点、各个节点的CSS定义以及他们的从属关系。下一步操作称之为Layout,顾名思义就是计算出每个节点在屏幕中的位置。
5. 渲染树绘制:再下一步就是绘制,即遍历render树,并使用UI后端层绘制每个节点。
重点:
上述过程是逐步完成的,为了更好的用户体验,渲染引擎将会尽可能早的将内容呈现到屏幕上,并不会等到所有的html都解析完成之后再去构建和布局render树。它是解析完一部分内容就显示一部分内容,同时,可能还在通过网络下载其余内容。(发送http请求下载)
两种DOM元素:样式(link、style)与脚本文件(script) 的解析
附加
|
|
TCP协议 |
UDP协议 |
|
连接 |
有连接的 |
无连接的 |
|
数据交付 |
可靠交付,保证数据无差错,不丢失,不重复,且按时序到达,提供超时重传来保证可靠性 |
努力交付,不可靠,不保证按序到达,甚至不保证到达,也不保证按序送到。 |
|
资源需求 |
所需资源多 |
资源要求少 |
|
传输数据形式 |
面向字节流的服务 |
面向报文的服务 |
|
连接形式 |
一对一的连接 |
一对一,多对多,一对多的通信 |
|
网络影响 |
网络拥堵影响发送端发送速率,但是有流量控制和拥塞控制 |
网络拥堵不会影响发送端的发送速率 |
|
应用场景 |
可靠但传输速度慢,响应慢:登录、打电话 |
不可靠但传输速度快,快速响应:发消息、视频 |
答: 这是因为服务端在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。而关闭连接时,当收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,己方也未必全部数据都发送给对方了,所以己方可以立即close,也可以发送一些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接,因此,己方ACK和FIN一般都会分开发送。
SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server回复确认包,并等待Client的确认,由于源地址是不存在的,因此,Server需要不断重发直至超时,这些伪造的SYN包将产时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络堵塞甚至系统瘫痪。SYN攻击时一种典型的DDOS攻击,
检测SYN攻击的方式:即当Server上有大量半连接状态且源IP地址是随机的,则可以断定遭到SYN攻击了,使用如下命令可以让之现行:
#netstat -nap | grep SYN_RECV
答:第一,为保证客户端发送的最有一个ACK报文段能够到达服务端。ACK报文段可能丢失,因而使处在LAST-ACK状态的服务端收不到对已发送的FIN和ACK 报文段的确认。服务端会超时重传这个FIN和ACK报文段,而客户端就能在2MSL时间内收到这个重传的ACK+FIN报文段。接着客户端重传一次确认。
第二,防止已失效的连接请求报文段出现在本连接中。客户端在发送完最有一个ACK报文段后,再经过2MSL,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。
答:所有的CSS都是阻塞渲染的(CSSOM是展示任何东西的必需品,在CSS没处理好之前所有东西都不会展示)
CSSOM 在加载一个新页面时必须重新构建(意味着即使你的CSS文件被缓存了,也并不意味着这个已经构建好了的CSSOM可以应用到每一个页面)
1. css加载不会阻塞DOM树的解析
2. css加载会阻塞DOM树的渲染
3. css加载会阻塞后面js语句的执行
Ps:CSS选择器的读取顺序是从右向左。
概念
(1)Reflow(回流):浏览器要花时间去渲染,当它发现了某个部分发生了变化影响了布局,那就需要倒回去重新渲染。
(2)Repaint(重绘):如果只是改变了某个元素的背景颜色,文字颜色等,不影响元素周围或内部布局的属性,将只会引起浏览器的repaint,重画某一部分。
Reflow要比Repaint更花费时间,也就更影响性能。所以在写代码的时候,要尽量避免过多的Reflow。
回流的原因
(1)页面初始化的时候;
(2)操作DOM时;
(3)某些元素的尺寸变了;
(4)如果 CSS 的属性发生变化了。
减少回流与重绘
(1)不要一条一条地修改 DOM 的样式。与其这样,还不如预先定义好 css 的 class,然后修改 DOM 的 className。
(2)不要把 DOM 结点的属性值放在一个循环里当成循环里的变量。
(3)为动画的 HTML 元件使用 fixed 或 absoult 的 position,那么修改他们的 CSS 是不会 reflow 的。
(4)千万不要使用 table 布局。因为可能很小的一个小改动会造成整个 table 的重新布局。
浏览器采用自上而下的方式解析,在遇到这两种元素时都会阻塞浏览器的解析,直到外部资源加载并解析或执行完毕后才会继续向下解析html。对于样式与脚本的先后顺序同样也会影响到浏览器的解析过程,究其原因主要在于:script脚本执行过程中可能会修改html界面(如document.write函数);DOM节点的CSS样式会影响js的执行结果。
- link标签的加载,阻塞后续script(脚本)执行,直到外部样式加载并解析完毕。
- link标签不阻塞后续外部文件(link、script)加载,但会阻塞外部脚本的执行。
- 外部脚本与外部样式是并行加载,但直到外部样式加载完毕,外部脚本才能执行
- 如果后续外部脚本含有async属性(IE下为defer),则外部样式不会阻塞该脚本的加载与执行
- 添加async属性之后,script加载的外部文件成为了异步加载,这时相当于它于原本的html解析过程同步进行。所以他不会被任何加载过程阻塞,只会在自己加载完成之后执行。但是,异步执行的影响就是,它如要读取dom节点,很可能会失败,因为它的加载和html解析过程没有了先后顺序。另外,如果它要输出动态的dom节点,就无法保证节点的位置,因为它添加的节点,是在html已解析的节点下顺序添加的。
<script src="http://udacity-crp.herokuapp.com/time.js?rtt=1&a" async></script>
- 对于动态创建的link标签不会阻塞其后动态创建的script的加载与执行,不管script标签是否具有async属性,但对于其他非动态创建的script,以上三条结论仍适用
async:
- 异步加载,并告诉浏览器立即下载文件,下载完成后立即执行,表示当前脚本不必等待其他脚本,也不必阻塞文档呈现。
- 只对外部脚本文件有效。
- 多个async属性的script标签其下载和执行也是异步的,不能确保彼此的先后顺序
- 不能保证异步脚本按照它们在页面中出现的顺序执行,可能会影响页面构造
-
async会在load事件之前执行,但并不能确保与DOMContentLoaded的执行先后顺序
defer:
- 推迟加载:告诉浏览器立即下载脚本,但延迟到文档完全被解析和显示之后再执行,延迟脚本总是按照指定它们的顺序执行
- 只对外部脚本文件有效。
- 多个defer属性的script标签会按顺序执行
- 在执行时不会影响页面的构造
-
defer脚本会在DOMContentLoaded和load事件之前执行
- <head>元素内:
- 在文档的 <head> 元素中包含所有 JavaScript 文件,意味着必须等到全部 JavaScript 代码都被下载、解析和执行完成以后,才能开始呈现页面的内容(浏览器在遇到 <body> 标签时才开始呈现内容)。
- 对于那些需要很多 JavaScript 代码的页面来说,这无疑会导致浏览器在呈现页面时出现明显的延迟,而延迟期间的浏览器窗口中将是一片空白。
- <body> 元素中页面内容的后面:
- 在解析包含的 JavaScript 代码之前,页面的内容将完全呈现在浏览器中。而用户也会因为浏览器窗口显示空白页面的时间缩短而感到打开页面的速度加快了
DOMContentLoaded:文档解析完毕,页面重新渲染。当页面引用的所有 js 同步代码执行完毕,触发 DOMContentLoaded 事件。无需等待样式表、图像和子框架的完成加载。
Load:仅用于检测一个完全加载的页面,html 文档中的图片资源,js 代码中有异步加载的 css、js 、图片资源都加载完毕之后,load 事件触发。
- async:异步执行脚本,值:async(只适用于外部脚本,是html5新增属性);
- charset:脚本中使用的字符编码(只适用于外部脚本);
- defer:当页面已完成解析后,执行脚本,值:defer(只适用于外部脚本)。
- src:外部脚本的地址;
- type:脚本的MIME类型;
- language:不赞成使用。规定脚本语言。请使用 type 属性代替它。
- xml:space:规定是否保留代码中的空白(html5不支持)。
1)请求(客户端->服务端[request])
GET(请求的方式) /newcoder/hello.html(请求的目标资源) HTTP/1.1(请求采用的协议和版本号)
Accept: */* (客户端能接收的资源类型)
Accept-Language: en-us (客户端接收的语言类型)
Connection: Keep-Alive (维护客户端和服务端的连接关系)
Host: localhost:8080 (连接的目标主机和端口号)
Referer: http://localhost/links.asp (告诉服务器我来自于哪里)
User-Agent: Mozilla/4.0 (客户端浏览器版本号的名字)
Accept-Encoding: gzip, deflate (客户端能接收的压缩数据的类型)
If-Modified-Since: Tue, 11 Jul 2000 18:23:51 GMT (缓存时间)
Cookie (客户端暂存服务端的信息)
Date: Tue, 11 Jul 2000 18:23:51 GMT (客户端请求服务端的时间)
2)响应(服务端->客户端[response])
HTTP/1.1(响应采用的协议和版本号) 200(状态码) OK(描述信息)
Location: http://www.baidu.com (服务端需要客户端访问的页面路径)
Server:apache tomcat (服务端的Web服务器名)
Content-Encoding: gzip (服务端能够发送压缩编码类型)
Content-Length: 80 (服务端发送的压缩数据的长度)
Content-Language: zh-cn (服务端发送的语言类型)
Content-Type: text/html; charset=GB2312 (服务端发送的类型及采用的编码方式)
Last-Modified: Tue, 11 Jul 2000 18:23:51 GMT (服务端对该资源最后修改的时间)
Refresh: 1;url=http://www.it315.org (服务端要求客户端1秒钟后,刷新,然后访问指定的页面路径)
Content-Disposition: attachment; filename=aaa.zip (服务端要求客户端以下载文件的方式打开该文件)
Transfer-Encoding: chunked (分块传递数据到客户端)
Set-Cookie:SS=Q0=5Lb_nQ; path=/search (服务端发送到客户端的暂存数据)
Expires: -1//3种 (服务端禁止客户端缓存页面数据)
Cache-Control: no-cache (服务端禁止客户端缓存页面数据)
Pragma: no-cache (服务端禁止客户端缓存页面数据)
Connection: close(1.0)/(1.1)Keep-Alive (维护客户端和服务端的连接关系是否保持)
Date: Tue, 11 Jul 2000 18:23:51 GMT (服务端响应客户端的时间)
在服务器响应客户端的时候,带上Access-Control-Allow-Origin头信息,解决跨域的一种方法。