博客原文地址: https://finget.github.io/view...
浏览器是多进程的,主要分为:javascript
这里咱们只考虑输入的是一个URL 结构字符串,若是是非 URL 结构的字符串,则会用浏览器默认的搜索引擎搜索该字符串。css
URL 主要由 协议
、主机
、端口
、路径
、查询参数
、锚点
6部分组成!html
输入URL后,浏览器会解析出协议、主机、端口、路径等信息,并构造一个HTTP请求。前端
expires
和cache-control
判断是否命中(包括是否过时)强缓存策略,若是命中,直接从缓存获取资源,并不会发送请求。若是没有命中,则进入下一步。last-modified
和etag
判断是否命中协商缓存,若是命中,直接从缓存获取资源。若是没有命中,则进入下一步。因为安全隐患,会使用 HSTS 强制客户端使用 HTTPS 访问页面。详见:你所不知道的 HSTS。
当你的网站均采用 HTTPS,并符合它的安全规范,就能够申请加入 HSTS 列表,以后用户不加 HTTPS 协议再去访问你的网站,浏览器都会定向到 HTTPS。不管匹配到没有,都要开始 DNS 查询工做了。java
强制缓存就是向浏览器缓存查找该请求结果,并根据该结果的缓存规则来决定是否使用该缓存结果的过程。强缓存又分为两种Expires
和Cache-Control
nginx
请求头:git
字段名称 | 说明 |
---|---|
no-cache | 告知(代理)服务器不直接使用缓存,要求向原服务器发起请求 |
no-store | 全部内容都不会被保存到缓存或Internet临时文件中 |
max-age=delta-seconds | 告知服务器客户端但愿接收一个存在时间不大于delta-secconds秒的资源 |
max-stale[=delta-seconds] | 告知(代理)服务器客户端愿意接收一个超过缓存时间的资源,如有定义delta-seconds则为delta-seconds秒,若没有则为任意超出时间 |
min-fresh=delta-seconds | 告知(代理)服务器客户端但愿接收一个在小于delta-seconds秒内被更新过的资源 |
no-transform | 告知(代理)服务器客户端但愿获取实体数据没有被转换(好比压缩)过的资源 |
noly-if-cached | 告知(代理)服务器客户端但愿获取缓存的内容(如有),而不用向原服务器发去请求 |
cache-extension | 自定义扩展值,若服务器不识别该值将被忽略掉 |
响应头:github
字段名称 | 说明 |
---|---|
public | 代表任何状况下都得缓存该资源(即便是须要HTTP认证的资源) |
Private=[field-name] | 代表返回报文中所有或部分(若指定了field-name则为field-name的字段数据)仅开放给某些用户(服务器指定的share-user,如代理服务器)作缓存使用,其余用户则不能缓存这些数据 |
no-cache | 不直接使用缓存,要求向服务器发起(新鲜度校验)请求 |
no-store | 因此内容都不会被保存到缓存或Internet临时文件中 |
no-transform | 告知客户端缓存文件时不得对实体数据作任何改变 |
noly-if-cached | 告知(代理)服务器客户端但愿获取缓存的内容(如有),而不用向原服务器发去请求 |
must-revalidate | 当前资源必定是向原方法服务器发去验证请求的,如请求是吧会返回504(而非代理服务器上的缓存) |
proxy-revalidate | 与must-revalidate相似,但仅能应用于共享缓存(如代理) |
max-age=delta-seconds | 告知客户端该资源在delta-seconds秒内是新鲜的,无需向服务器发请求 |
s-maxage=delta-seconds | 同max-age,但仅能应用于共享缓存(如代理) |
cache-extension | 自定义扩展值,若服务器不识别该值将被忽略掉 |
示例:web
// server.js const http = require('http') const fs = require('fs') http.createServer(function (request, response) { console.log('request come', request.url) if (request.url === '/') { const html = fs.readFileSync('test.html', 'utf8') response.writeHead(200, { 'Content-Type': 'text/html' }) response.end(html) } if (request.url === '/script.js') { response.writeHead(200, { 'Content-Type': 'text/javascript', 'Cache-Control': 'max-age=20,public' // 缓存20s 多个值用逗号分开 }) response.end('console.log("script loaded")') } }).listen(8888) console.log('server listening on 8888')
// test.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> </body> <script src="/script.js"></script> </html>
协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程。面试
在浏览器第一次给服务器发送请求后,服务器会在响应头中加上这个字段。
浏览器接收到后,若是再次请求,会在请求头中携带If-Modified-Since
字段,这个字段的值也就是服务器传来的最后修改时间。
服务器拿到请求头中的If-Modified-Since
的字段后,其实会和这个服务器中该资源的最后修改时间Last-Modified
对比,询问服务器在该日期后资源是否有更新,有更新的话就会将新的资源发送回来。
可是若是在本地打开缓存文件,就会形成 Last-Modified
被修改,因此在 HTTP / 1.1
出现了 ETag
。
ETag
是服务器根据当前文件的内容,给文件生成的惟一标识,只要里面的内容有改动,这个值就会变。服务器经过响应头把这个值给浏览器。
浏览器接收到ETag的值,会在下次请求时,将这个值做为If-None-Match
这个字段的内容,并放到请求头中,而后发给服务器。
若是两种方式都支持的话,服务器会优先考虑ETag
Service Worker
是运行在浏览器背后的独立线程,通常能够用来实现缓存功能。使用 Service Worker
的话,传输协议必须为 HTTPS
。由于 Service Worker
中涉及到请求拦截,因此必须使用 HTTPS
协议来保障安全。Service Worker
的缓存与浏览器其余内建的缓存机制不一样,它可让咱们自由控制缓存哪些文件、如何匹配缓存、如何读取缓存,而且缓存是持续性的。
Service Worker
实现缓存功能通常分为三个步骤:首先须要先注册 Service Worker
,而后监听到 install
事件之后就能够缓存须要的文件,那么在下次用户访问的时候就能够经过拦截请求的方式查询是否存在缓存,存在缓存的话就能够直接读取缓存文件,不然就去请求数据。
当 Service Worker
没有命中缓存的时候,咱们须要去调用 fetch
函数获取数据。也就是说,若是咱们没有在 Service Worker
命中缓存的话,会根据缓存查找优先级去查找数据。可是无论咱们是从 Memory Cache
中仍是从网络请求中获取的数据,浏览器都会显示咱们是从 Service Worker
中获取的内容。
Memory Cache
也就是内存中的缓存,主要包含的是当前中页面中已经抓取到的资源,例如页面上已经下载的样式、脚本、图片等。读取内存中的数据确定比磁盘快,内存缓存虽然读取高效,但是缓存持续性很短,会随着进程的释放而释放。 一旦咱们关闭Tab页面,内存中的缓存也就被释放了。
那么既然内存缓存这么高效,咱们是否是能让数据都存放在内存中呢?
这是不可能的。计算机中的内存必定比硬盘容量小得多,操做系统须要精打细算内存的使用,因此能让咱们使用的内存必然很少。
须要注意的事情是,内存缓存在缓存资源时并不关心返回资源的HTTP缓存头Cache-Control
是什么值,同时资源的匹配也并不是仅仅是对URL作匹配,还可能会对Content-Type
,CORS等其余特征作校验。
Disk Cache
也就是存储在硬盘中的缓存,读取速度慢点,可是什么都能存储到磁盘中,比之 Memory Cache
胜在容量和存储时效性上。
Push Cache
(推送缓存)是 HTTP/2
中的内容,当以上三种缓存都没有命中时,它才会被使用。它只在会话(Session)中存在,一旦会话结束就被释放,而且缓存时间也很短暂,在Chrome浏览器中只有5分钟左右,同时它也并不是严格执行HTTP头中的缓存指令。
Edge
和 Safari
浏览器支持相对比较差no-cache
和 no-store
的资源Push Cache
就被释放HTTP/2
的链接,也就可使用同一个Push Cache
。这主要仍是依赖浏览器的实现而定,出于对性能的考虑,有的浏览器会对相同域名但不一样的tab标签使用同一个HTTP链接。Push Cache
中的缓存只能被使用一次在发起http请求以前,浏览器首先要作去得到咱们想访问网页的IP地址,浏览器会发送一个UDP的包给DNS域名解析服务器。
咱们的浏览器、操做系统、路由器都会缓存一些URL对应的IP地址,统称为DNS高速缓存。这是为了加快DNS解析速度,使得没必要每次都到根域名服务器中去查询。
迭代查询的方式就是,局部的DNS服务器并不会本身向其余服务器进行查询,而是把可以解析该域名的服务器IP地址返回给客户端,客户端会不断的向这些服务器进行查询,直到查询到了位置,迭代的话只会帮你找到相关的服务器,而后说我如今比较忙,你本身去找吧。
DNS还有负载均衡的做用,如今不少网站都有多个服务器,当一个网站访问量过大的时候,若是全部请求都请求在同一个服务器上,可能服务器就会崩掉,这时候就用到了DNS负载均衡技术,当一个网站有多个服务器地址时,在应答DNS查询的时候,DNS服务器会对每一个查询返回不一样的解析结果,也就是返回不一样的IP地址,从而把访问引导到不一样的服务器上去,来达到负载均衡的目的。例如能够根据每台机器的负载量,或者该机器距离用户的地理位置距离等等条件。
TCP(Transmission Control Protocol)传输控制协议。
TCP/IP协议将应用层、表示层、会话层合并为应用层,物理层和数据链路层合并为网络接口层。
TCP/IP协议不只仅指的是TCP和IP两个协议,⽽是指的⼀个由FTP,SMTP,TCP,UDP,IP,ARP等等协议构成的协议集合。
客服端和服务端在进行http请求和返回的工程中,须要建立一个TCP connection
(由客户端发起),http
不存在链接这个概念,它只有请求和响应。请求和响应都是数据包,它们之间的传输通道就是TCP connection
。
位码即tcp标志位,有6种标示:
第一次握手:主机A发送位码为SYN=1
,随机产生Seq number=1234567
的数据包到服务器,主机B由SYN=1
知道,A要求创建联机;(第一次握手,由浏览器发起,告诉服务器我要发送请求了)
第二次握手:主机B收到请求后要确认联机信息,向A发送ack number=(主机A的seq+1)
,SUN=1,ACK=1234567 + 1
,随机产生Seq=7654321
的包;(第二次握手,由服务器发起,告诉浏览器我准备接受了,你赶忙发送吧)
第三次握手:主机A收到后检查ack number
是否正确,即第一次发送的seq number+1
,以及位码SYN
是否为1,若正确,主机A会再发送ack number=(主机B的seq+1)
,ack=7654321 + 1
,主机B收到后确认Seq
值与ACK=7654321+ 1
则链接创建成功;(第三次握手,由浏览器发送,告诉服务器,我立刻就发了,准备接受吧)
老是要问:为何须要三次握手,两次不行吗?其实这是由TCP的自身特色 可靠传输决定的。客户端和服务端要进行可靠传输,那么就须要 确认双方的接收
和发送
能力。第一次握手能够确认客服端的发送能力
,第二次握手,服务端SYN=1,Seq=Y
就确认了发送能力
,ACK=X+1
就确认了接收能力
,因此第三次握手才能够确认客户端的接收能力
。否则容易出现丢包的现象。
试想若是是用两次握手,则会出现下面这种状况:
如客户端发出链接请求,但因链接请求报文丢失而未收到确认,因而客户端再重传一次链接请求。后来收到了确认,创建了链接。数据传输完毕后,就释放了链接,客户端共发出了两个链接请求报文段,其中第一个丢失,第二个到达了服务端,可是第一个丢失的报文段只是在某些网络结点长时间滞留了,延误到链接释放之后的某个时间才到达服务端,此时服务端误认为客户端又发出一次新的链接请求,因而就向客户端发出确认报文段,赞成创建链接,不采用三次握手,只要服务端发出确认,就创建新的链接了,此时客户端忽略服务端发来的确认,也不发送数据,则服务端一致等待客户端发送数据,浪费资源。
服务器第一次收到客户端的 SYN 以后,就会处于 SYN_RCVD 状态,此时双方尚未彻底创建其链接,服务器会把此种状态下请求链接放在一个队列里,咱们把这种队列称之为半链接队列。
固然还有一个全链接队列,就是已经完成三次握手,创建起链接的就会放在全链接队列中。若是队列满了就有可能会出现丢包现象。
这里在补充一点关于SYN-ACK 重传次数的问题:
服务器发送完SYN-ACK包,若是未收到客户确认包,服务器进行首次重传,等待一段时间仍未收到客户确认包,进行第二次重传。若是重传次数超过系统规定的最大重传次数,系统将该链接信息从半链接队列中删除。
注意,每次重传等待的时间不必定相同,通常会是指数增加,例如间隔时间为 1s,2s,4s,8s…
当一端为创建链接而发送它的SYN时,它为链接选择一个初始序号。ISN随时间而变化,所以每一个链接都将具备不一样的ISN。ISN能够看做是一个32比特的计数器,每4ms加1 。这样选择序号的目的在于防止在网络中被延迟的分组在之后又被传送,而致使某个链接的一方对它作错误的解释。
三次握手的其中一个重要功能是客户端和服务端交换 ISN(Initial Sequence Number),以便让对方知道接下来接收数据的时候如何按序列号组装数据。若是 ISN 是固定的,攻击者很容易猜出后续的确认号,所以 ISN 是动态生成的。
其实第三次握手的时候,是能够携带数据的。可是,第一次、第二次握手不能够携带数据。
为何这样呢?你们能够想一个问题,假如第一次握手能够携带数据的话,若是有人要恶意攻击服务器,那他每次都在第一次握手中的 SYN 报文中放入大量的数据。由于攻击者根本就不理服务器的接收、发送能力是否正常,而后疯狂着重复发 SYN 报文的话,这会让服务器花费不少时间、内存空间来接收这些报文。
也就是说,第一次握手不能够放数据,其中一个简单的缘由就是会让服务器更加容易受到攻击了。而对于第三次的话,此时客户端已经处于 ESTABLISHED 状态。对于客户端来讲,他已经创建起链接了,而且也已经知道服务器的接收、发送能力是正常的了,因此能携带数据也没啥毛病。
服务器端的资源分配是在二次握手时分配的,而客户端的资源是在完成三次握手时分配的,因此服务器容易受到SYN洪泛攻击。SYN攻击就是Client在短期内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server则回复确认包,并等待Client确认,因为源地址不存在,所以Server须要不断重发直至超时,这些伪造的SYN包将长时间占用未链接队列,致使正常的SYN请求由于队列满而被丢弃,从而引发网络拥塞甚至系统瘫痪。SYN 攻击是一种典型的 DoS/DDoS 攻击。
检测 SYN 攻击很是的方便,当你在服务器上看到大量的半链接状态时,特别是源IP地址是随机的,基本上能够判定这是一次SYN攻击。在 Linux/Unix 上可使用系统自带的 netstats 命令来检测 SYN 攻击。
netstat -n -p TCP | grep SYN_RECV
常见的防护 SYN 攻击的方法有以下几种:
HTTP/0.9
HTTP/1.0
status code
和 header
HTTP/1.1
GET
, HEAD
, POST
, PUT
,DELETE
, TRACE
, OPTIONS
HTTP2
HTTP3
HTTP3 的主要改进在传输层上。传输层不会再有我前面提到的那些繁重的 TCP 链接了。如今,一切都会走 UDP。
如今 HTTP3 最快!
请求报文:
响应报文:
- TCP是底层通信协议,定义的是数据传输和链接方式的规范;
关于HTTP的东西还有不少,我在最后放了张 大图。
在HTTP的基础上再加一层TLS(传输层安全性协议)或者SSL(安全套接层),就构成了HTTPS协议。
HTTPS 默认工做在 TCP 协议443端口,它的工做流程通常如如下方式:
Client Hello
消息,其中携带客户端支持的协议版本、加密算法、压缩算法以及客户端生成的随机数;服务端收到客户端支持的协议版本、加密算法等信息后;
Server Hello
消息,并携带选择特定的协议版本、加密方法、会话 ID 以及服务端生成的随机数;Certificate
消息,即服务端的证书链,其中包含证书支持的域名、发行方和有效期等信息;Server Key Exchange
消息,传递公钥以及签名等信息;Certificate Request
,验证客户端的证书;Server Hello Done
消息,通知服务端已经发送了所有的相关信息;客户端收到服务端的协议版本、加密方法、会话 ID 以及证书等信息后,验证服务端的证书;
Client Key Exchange
消息,包含使用服务端公钥加密后的随机字符串,即预主密钥(Pre Master Secret
);Change Cipher Spec
消息,通知服务端后面的数据段会加密传输;Finished
消息,其中包含加密后的握手信息;服务端收到 Change Cipher Spec
和 Finished
消息后;
Change Cipher Spec
消息,通知客户端后面的数据段会加密传输;Finished
消息,验证客户端的 Finished
消息并完成 TLS 握手;TLS 握手的关键在于利用通讯双发生成的随机字符串和服务端的证书公钥生成一个双方通过协商后的对称密钥,这样通讯双方就可使用这个对称密钥在后续的数据传输中加密消息数据,防止中间人的监听和攻击,保证通信安全。
HTTPS链接 须要7次握手,3次TCP + 4次TSL。
每台服务器上都会安装处理请求的应用——Web Server。常见的Web Server 产品有 apache
、nginx
、IIS
或 Lighttpd
等。
HTTP请求通常能够分为两类,静态资源 和 动态资源。
请求访问静态资源,这个就直接根据url地址去服务器里找就行了。
请求动态资源的话,就须要web server把不一样请求,委托给服务器上处理相应请求的程序进行处理(例如 CGI 脚本,JSP 脚本,servlets,ASP 脚本,服务器端 JavaScript,或者一些其它的服务器端技术等),而后返回后台程序处理产生的结果做为响应,发送到客户端。
服务器在处理请求的时候主要有三种方式:
字节 → 字符 → 令牌 → 节点 → 对象模型。
<!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width,initial-scale=1"> <link href="style.css" rel="stylesheet"> <title>Critical Path</title> </head> <body> <p>Hello <span>web performance</span> students!</p> <div></div> </body> </html>
body { font-size: 16px } p { font-weight: bold } span { color: red } p span { display: none } img { float: right }
渲染流程:
<div class="position_">position</div> <div class="box_3d">3d变换</div> <div class="will-change">will-change</div> <div class="transform"></div> <iframe src="https://www.baidu.com"></iframe> div {width: 100px;height: 100px;} .position_ {background: pink;position: fixed;z-index: 20;} .box_3d {background: red;transform: translate3d(100px,30px,10px);} .will-change {background: #f12312;will-change: transform;} .transform {background: #302912;transform: skew(30deg, 20deg);}
在 chrome 上查看 Layers.
若是没有打开Layers,按下图打开:
知道图层的存在,咱们能够手动打开一个图层,经过添加
transform: translateZ(0)
这样回流和重绘的代价就小了,效率就会大大提升。可是不要滥用这个属性,不然会大大增长内存消耗。—— 开启GPU加速。
当页面中元素样式的改变并不影响它在文档流中的位置时(例如:color、background-color、visibility等),浏览器会将新样式赋予给元素并从新绘制它,这个过程称为重绘。
当Render Tree中部分或所有元素的尺寸、结构、或某些属性发生改变时,浏览器从新渲染部分或所有文档的过程称为回流。
回流必将引发重绘,而重绘不必定会引发回流。
引发回流:
引发回流的属性和方法:
由于当服务端收到客户端的SYN链接请求报文后,能够直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。可是关闭链接时,当服务端收到FIN报文时,极可能并不会当即关闭SOCKET,因此只能先回复一个ACK报文,告诉客户端,“你发的FIN报文我收到了”。只有等到我服务端全部的报文都发送完了,我才能发送FIN报文,所以不能一块儿发送。故须要四次挥手。
客户端收到服务端的链接释放报文段后,对此发出确认报文段(ACK=1,seq=u+1,ack=w+1),客户端进入TIME_WAIT(时间等待)状态。此时TCP未释放掉,须要通过时间等待计时器设置的时间2MSL后,客户端才进入CLOSED状态。若是不等待,客户端直接跑路,当服务端还有不少数据包要给客户端发,且还在路上的时候,若客户端的端口此时恰好被新的应用占用,那么就接收到了无用数据包,形成数据包混乱。
理论上,四个报文都发送完毕,就能够直接进入CLOSE状态了,可是可能网络是不可靠的,有可能最后一个ACK丢失。因此TIME_WAIT状态就是用来重发可能丢失的ACK报文。
1 个 MSL 确保四次挥手中主动关闭方最后的 ACK 报文最终能达到对端;
1 个 MSL 确保对端没有收到 ACK 重传的 FIN 报文能够到达。
若是想要高清大图或者Xmind文件的话,能够私信lian x
在这里对前辈大佬表示敬意,查找了不少资料,若有遗漏,还请见谅。文中若是有误,还望及时指出,感谢!
5 分钟看懂 HTTP3
一文带你了解HTTPS
从URL输入到页面展示到底发生什么?
从URL输入到页面展示到底发生什么?
在浏览器上请求一个URL的所有过程
前端经典面试题: 从输入URL到页面加载发生了什么?
浏览器缓存看这一篇就够了
从输入URL到浏览器显示页面的流程
在浏览器输入 URL 回车以后发生了什么(超详细版)
TCP和Http的区别!我都搞懂了,你就别迷糊了!
为何 HTTPS 须要 7 次握手以及 9 倍时延
渲染树构建、布局及绘制
浏览器渲染详细过程:重绘、重排和 composite 只是冰山一角
浏览器渲染机制和 Reflow(回流、重排)和 Repaint(重绘)
问我Chrome浏览器的渲染原理(6000字长文)
浅谈浏览器的图层与重绘重排(详细),以及如何用于性能优化
HTTP 笔记 1:Web 基础及简单的 HTTP 协议
图解HTTP-21张图把HTTP安排得明明白白
HTTP3
一文带你了解HTTPS
浏览器工做原理与实践