参考一 https://juejin.im/post/5bf23afa6fb9a049be5d1494
参考二 https://github.com/sunyongjian/blog/issues/34
参考三 https://blog.csdn.net/weixin_44460333/article/details/89369316
参考四 https://www.cnblogs.com/Andya/p/7272462.html
参考五 https://juejin.im/post/5bbaa549e51d450e827b6b13
参考六 https://xianyulaodi.github.io/2017/03/22/老生常谈-从输入url到页面展示到底发生了什么
URL
DNS
将域名解析成IP地址
。域名只是与IP地址
的一个映射。域名解析的过程实际是将域名还原为IP地址
的过程(如果输入的是IP地址,此步骤省略)IP地址
加端口 ,浏览器发起http请求
tcp连接
(建立过程:三次握手)tcp
的三次握手建立好连接后,便向服务器发送HTTP请求
,请求数据包HTTP请求
,根据请求信息去查找资源,返回响应信息HTTP响应
TCP连接
。因为http
是基于tcp
连接的,而tcp
则是通过ip 地址
去识别访问的。DNS 解析
就是域名转化成ip 地址
的过程。如果url
里不包含端口号,则会使用该协议的默认端口号。HTTP协议
默认80端口,HTTPS协议
默认443端口。
目前大部分的应用层连接都是 HTTP 协议
,而 HTTP
是 TCP/IP
承载的,所以这一步先假定是建立的TCP 连接
。
SYN
同步标识,通常用来建立连接。在“三次握手”的前两次出现。
ACK
确认标识,接收端确认接收到数据。
FIN
结束标识。表示双方数据发送完成,跟 SYN 类似,属于行为标识。
LISTEN
- 侦听来自远方TCP端口
的连接请求;SYN-SENT
-在发送连接请求后等待匹配的连接请求;SYN-RECEIVED
- 在收到和发送一个连接请求后等待对连接请求的确认;ESTABLISHED
- 代表一个打开的连接,数据可以传送给用户;FIN-WAIT-1
- 等待远程TCP
的连接中断请求,或先前的连接中断请求的确认;FIN-WAIT-2
- 从远程TCP
等待连接中断请求;CLOSE-WAIT
- 等待从本地用户发来的连接中断请求;CLOSING
-等待远程TCP
对连接中断的确认;LAST-ACK
- 等待原来发向远程TCP
的连接中断请求的确认;TIME-WAIT
-等待足够的时间以确保远程TCP
接收到连接中断请求的确认;CLOSED
- 没有任何连接状态;最初客户端和服务端的TCP进程
都处于CLOSED
关闭状态,客户端主动打开连接,而服务端被动打开连接。(A、B关闭状态CLOSED
——B收听状态LISTEN
——A同步已发送状态SYN-SENT
——B同步收到状态SYN-RCVD
——A、B连接已建立状态ESTABLISHED
)
三次握手协议指的是在发送数据的准备阶段,服务器端和客户端之间需要进行三次交互:
syn包(syn=j)
到服务器,并进入SYN_SENT
状态,等待服务器确认;syn包
,必须确认客户的SYN(ack=j+1)
,同时自己也发送一个SYN包(syn=k)
,即SYN+ACK
包,此时服务器进入SYN_RECV
状态;SYN+ACK
包,向服务器发送确认包ACK(ack=k+1)
,此包发送完毕,客户端和服务器进入ESTABLISHED
(TCP连接成功)状态,完成三次握手。
起初A和B都处于CLOSED状态
——B创建TCB
,处于LISTEN状态
,等待A请求——A创建TCB
,发送连接请求(SYN=1,seq=x)
,进入SYN-SENT
状态——B收到连接请求,向A发送确认(SYN=ACK=1
,确认号ack=x+1
,初始序号seq=y
),进入SYN-RCVD
状态——A收到B的确认后,给B发出确认(ACK=1,ack=y+1,seq=x+1
),A进入ESTABLISHED
状态——B收到A的确认后,进入ESTABLISHED
状态。
TCB
传输控制块Transmission Control Block
:存储每一个连接中的重要信息,如TCP
连接表,到发送和接收缓存的指针,到重传队列的指针,当前的发送和接收序号。
第一次握手:客户端发送网络包,服务端收到了。这样服务端就能得出结论:客户端的发送能力、服务端的接收能力是正常的。
第二次握手:服务端发包,客户端收到了。这样客户端就能得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的。不过此时服务器并不能确认客户端的接收能力是否正常。
第三次握手:客户端发包,服务端收到了。这样服务端就能得出结论:客户端的接收、发送能力正常,服务器自己的发送、接收能力也正常。
主要为了防止已失效的连接请求报文段突然又传送到了B,因而产生错误。
如A发出连接请求,但因连接请求报文丢失而未收到确认,于是A再重传一次连接请求。后来收到了确认,建立了连接。数据传输完毕后,就释放了连接。
A共发出了两个连接请求报文段,其中第一个丢失,第二个到达了B,但是第一个丢失的报文段只是在某些网络结点长时间滞留了,延误到连接释放以后的某个时间才到达B,此时B误认为A又发出一次新的连接请求,于是就向A发出确认报文段,同意建立连接,不采用三次握手,只要B发出确认,就建立新的连接了,此时A不理睬B的确认且不发送数据,则B一致等待A发送数据,浪费资源。
很多人可能会认为三次握手都不能携带数据,其实第三次握手的时候,是可以携带数据的。也就是说,第一次、第二次握手不可以携带数据,而第三次握手是可以携带数据的。
对于第三次的话,此时客户端已经处于 established 状态,也就是说,对于客户端来说,他已经建立起连接了,并且也已经知道服务器的接收、发送能力是正常的了,所以能携带数据页没啥毛病。
现在的页面为了优化请求的耗时,默认都会开启持久连接(keep-alive),那么一个TCP连接确切关闭的时机,是这个tab标签页关闭的时候。这个关闭的过程就是著名的四次挥手。关闭是一个全双工的过程,发包的顺序的不一定的。一般来说是客户端主动发起的关闭。
Client
发送一个FIN
,用来关闭Client
到Server
的数据传送,Client
进入FIN_WAIT_1
状态。Server
收到FIN
后,发送一个ACK
给Client
,确认序号为收到序号+1
(与SYN
相同,一个FIN
占用一个序号),Server
进入CLOSE_WAIT
状态。Server
发送一个FIN
,用来关闭Server
到Client
的数据传送,Server
进入LAST_ACK
状态。Client
收到FIN
后,Client
进入TIME_WAIT
状态,接着发送一个ACK
给Server
,确认序号为收到序号+1
,Server
进入CLOSED
状态,完成四次挥手。
起初A和B处于ESTABLISHED状态
——A发出连接释放报文段并处于FIN-WAIT-1状态
——B发出确认报文段且进入CLOSE-WAIT状态
——A收到确认后,进入FIN-WAIT-2状态
,等待B的连接释放报文段——B没有要向A发出的数据,B发出连接释放报文段且进入LAST-ACK状态
——A发出确认报文段且进入TIME-WAIT状态
——B收到确认报文段后进入CLOSED状态
——A经过等待计时器时间2MSL
后,进入CLOSED状态
。
因为当Server端
收到Client端
的SYN连接请求报文
后,可以直接发送SYN+ACK报文
。其中ACK报文
是用来应答的,SYN报文
是用来同步的。
但是关闭连接时,当Server端
收到FIN报文
时,很可能并不会立即关闭SOCKET
,所以只能先回复一个ACK报文
,告诉Client端
,“你发的FIN报文
我收到了”。只有等到我Server端
所有的报文都发送完了,我才能发送FIN报文
,因此不能一起发送。故需要四步握手。
MSL :最大报文段生存时间(Maximum Segment Lifetime
)
为什么客户端发送ACK
之后不直接关闭,而是要等一阵子才关闭。这其中的原因就是,要确保服务器是否已经收到了我们的ACK
报文,如果没有收到的话,服务器会重新发FIN
报文给客户端,客户端再次收到FIN
报文之后,就知道之前的ACK
报文丢失了,然后再次发送ACK
报文。
至于TIME_WAIT
持续的时间至少是一个报文的来回时间。一般会设置一个计时,如果过了这个计时没有再次收到FIN
报文,则代表对方成功就是ACK
报文,此时处于 CLOSED
状态。
正是由于这个ACK报文段
有可能丢失,使得处于LAST-ACK状态
的B收不到对已发送的FIN+ACK报文段
的确认,B超时重传FIN+ACK报文段
,而A能在2MSL
时间内收到这个重传的FIN+ACK报文段
,接着A重传一次确认,重新启动2MSL
计时器,最后A和B都进入到CLOSED状态
,若A在TIME-WAIT状态
不等待一段时间,而是发送完ACK报文段
后立即释放连接,则无法收到B重传的FIN+ACK报文段
,所以不会再发送一次确认报文段,则B无法正常进入到CLOSED状态
。
1.处理的过程大致是把请求的信息解析出来
2.根据解析出来的信息去请求资源
3.找到资源后,则构建响应信息,主要包括状态码,响应头,响应报文三个部分。
4.把构建的响应发送给客户端(Web服务器
首先传送一些HTTP头信息
,然后传送具体内容(即HTTP体信息
),HTTP头信息
和HTTP体信息
之间用一个空行分开)
1xx
:信息性状态码,表示服务器已接收了客户端请求,客户端可继续发送请求。
2xx
:成功状态码,表示服务器已成功接收到请求并进行处理。
3xx
: 重定向状态码,表示服务器要求客户端重定向。
4xx
:客户端错误状态码,表示客户端的请求有非法内容。
5xx
:服务器错误状态码,表示服务器未能正常处理客户端的请求而出现意外错误。
301
和302
状态码都表示重定向,就是说浏览器在拿到服务器返回的这个状态码后会自动跳转到一个新的URL地址
,这是它们的共同点。
他们的不同在于,301
表示旧地址A
的资源已经被永久地移除了(这个资源不可访问了),搜索引擎在抓取新内容的同时也将旧的网址交换为重定向之后的网址;
302
表示旧地址A
的资源还在(仍然可以访问),这个重定向只是临时地从旧地址A
跳转到地址B
,搜索引擎会抓取新的内容而保存旧的网址。
当一个网站或者网页24—48小时内
临时移动到一个新的位置,这时候就要进行302
跳转,而使用301
跳转的场景就是之前的网站因为某种原因需要移除掉,然后要到新的地址访问,是永久性的。
(1)某些注册了多个域名的网站,需要通过重定向让访问这些域名的用户自动跳转到主站点等。
(2)网页被移到一个新地址,这种情况下,如果不做重定向,则用户收藏夹或搜索引擎数据库中旧地址只能让访问客户得到一个404页面错误信息,访问流量白白丧失
(3)跟搜索引擎排名有关。如果一个页面有两个地址,就像http://www.yy.com/
和http://yy.com/
,搜索引擎会认为它们是两个网站,结果造成每个搜索链接都减少从而降低排名。
HTML 标记
并构建DOM 树
,通过HTML解析器
解析HTML文档
,构建一个DOM Tree
。CSS 标记
并构建CSSOM 树
,通过CSS解析器
解析HTML
中存在的CSS
,构建Style Rules
。DOM
与 CSSOM
合并成一个渲染树(Render Tree)
。Render Tree
构建完毕,进入到布局阶段(layout/reflow)
,将会为每个阶段分配一个应出现在屏幕上的确切坐标。浏览器在解析html
文件时,会"自上而下"加载,并在加载过程中进行解析渲染。在解析过程中,如果遇到请求外部资源时,如图片、外链的CSS
、iconfont
等,请求过程是异步的,并不会影响html文档
进行加载。
浏览器是一个边解析边渲染的过程。首先浏览器解析HTML
文件构建DOM树
,然后解析CSS
文件构建渲染树,等到渲染树构建完成后,浏览器开始布局渲染树并将其绘制到屏幕上。
DOM节点
中的各个元素都是以盒模型的形式存在,这些都需要浏览器去计算其位置和大小等,这个过程称为relow
;当盒模型的位置,大小以及其他属性,如颜色,字体,等确定下来之后,浏览器便开始绘制内容,这个过程称为repain
。
页面在首次加载时必然会经历reflow
和repain
。reflow
和repain
过程是非常消耗性能的,尤其是在移动设备上,它会破坏用户体验,有时会造成页面卡顿。所以我们应该尽可能少的减少reflow
和repain
。
在浏览器显示HTML
时,它会注意到需要获取其他地址内容的标签。这时,浏览器会发送一个获取请求来重新获得这些文件。请求过程是异步的,并不会影响HTML
文档进行加载,但是当文档加载过程中遇到JS
文件,HTML
文档会挂起渲染过程,不仅要等到文档中JS文件
加载完毕还要等待解析执行完毕,才会继续HTML
的渲染过程。
这些文件的请求都要经历一个和HTML
读取类似的过程。所以浏览器会在DNS
中查找这些域名,发送请求,重定向等等…
静态文件会允许浏览器对其进行缓存。有的文件可能会不需要与服务器通讯,而从缓存中直接读取。
如果在解析的过程中遇到script
的时候,文档的解析将会停止下来,立即解析执行脚本,如果脚本是外部的,则会等待请求完成并解析执行。html
文档会挂起渲染(加载解析渲染同步)的线程,不仅要等待文档中js文件加载完毕,还要等待解析执行完毕,才可以恢复html
文档的渲染线程。
因为JS
有可能会修改DOM
,最为经典的document.write
,这意味着,在JS
执行完成前,后续所有资源的下载可能是没有必要的,这是js
阻塞后续资源下载的根本原因。
CSS
文件的加载不影响JS
文件的加载,但是却影响JS
文件的执行。JS
代码执行前浏览器必须保证CSS
文件已经下载并加载完毕。
为了不阻塞页面地呈现,一般会把script
脚本放在文档的最后。