1.DNS解析

平时在浏览器中输入的域名并非是网址或资源真实所在的服务器地址或资源,域名只是与ip地址的一个映射。在进行正式的请求之前,就会先有这个过程:DNS服务将域名解析成对应的ip地址。其解析过程:

1.检查本机host中是否有配置相应的ip映射关系,如果有则使用这个ip地址,完成域名解析 2.检查浏览器缓存中是否缓存过该域名对应的ip地址 3.检查找本机系统中是否缓存过ip 4.向本地域名解析服务发起域名解析的请求 5.向根域名解析服务器发起域名解析请求 6.按照根域名(.) -> 顶级域(如:.com) -> 第二层域(如:xxx.com) -> 子域(如:www.xxx.com)的顺序查找到ip地址 8.返回解析结果给用户

2.建立TCP连接

经典的三次握手

第一次握手: 建立连接时,客户端发送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连接成功)状态,完成三次握手。

简单来说其步骤就是,客户端发送数据包告诉服务端连接请求,服务端返回数据包响应表示可以收到建立连接请求并且返回数据包通知客户端,客服端再次发送数据包确认并建立TCP连接

3.发送HTTP请求

一个HTTP请求包含请求行,请求头,请求主体

3.1请求行

常见的信息有请求地址、请求方法GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS、TRACEHttp协议信息

3.2请求头

常见的请求头信息有

Host:主机名 ConnectionHTTP/1.1增加的,使用keepalive,一个连接可以发多个请求 User-Agent:客户端程序的信息,就是发送请求的浏览器信息,可根据这个信息来对不同设备进行处理 Accept:浏览器可以接收的媒体数据类型 Accept-Encoding:是浏览器用来告知服务器它能够支持的内容编码及内容编码的优先级顺序,可一次性指定多种内容编码 Accept-Language:告诉服务器浏览器能够处理的自然语言集 Content-Type:请求中的数据的媒体类型 Cookie:浏览器记录的用户相关信息

当然还有很多其他的,这里就不罗列完了,另外还可以自己添加一些自定义请求头,在服务端手动获取处理就行了

3.3请求主体

主要是请求所带的一些参数信息,这里就不多赘述

4.使用浏览器缓存

浏览器缓存主要强缓存协商缓存,具体使用哪一种缓存,强缓存的优先级比协商缓存高,怎么设置还是得从实际出发

4.1强缓存

强缓存可以用ExpiresCache-Control来控制,强缓存最大的特点就是通过服务器返回的过期时间来判断缓存是否过期,不会再去和服务端做校验看资源是否真正更新了

Expires:主由服务端返给客户端,在过期时间内,浏览器可直接使用缓存而不需要重新请求,但它也有不少局限,首先它只能精确到秒,对于那种毫秒级更新的资源它没办法判断,其次它受限于本地时间,如果修改了本地时间,可能会造成缓存失效。ExpiresHttp/1时候的请求头,现在这样设置通常也是为了兼容

Cache-Control:是现在主要使用的方式,可以在请求头以及响应头中设置,常见设置有max-age(表示缓存在多少秒后过期)no-cache(表示使用Etag或者Last-Modified字段来控制缓存,即协商缓存),通常来讲,Cache-control的优先级是要高于Expires

4.2协商缓存

在强缓存失效后就会用协商缓存,协商缓存主要使用Last-Modified 和 ETag请求头控制,这两个字段都是由服务端返回的。如果说缓存还生效,那么这次请求的状态码会返回304,表示资源未改动,如果缓存失效,返回新的资源并设置缓存,返回200状态码

Last-Modified:在设置Last-Modified后,下次请求时请求头会带上If-Modified-Since这个字段,然后服务器会用这个时间与资源修改时间做对比,然后就是返回304还是200的问题了,同样它也只能精确到秒,所以一些需要毫秒级更新的资源使用这个并不准确

ETag:通常与If-None-Match配合使用,Etag为服务端为这资源生成的唯一标识,下次客户端请求的时候,带上这个值,由服务端再做校验,如果资源未改动,返回304,使用原来的缓存,反之返回200,设置缓存

浏览器缓存.png
浏览器缓存.png

图出自博客

5.浏览器接收响应

响应分为状态码,响应头,响应主体组成,响应主体通常是一些文件如(HTML、JS、CSS文件)或者说是一些响应数据(请求的响应数据),状态码和响应头这里就不罗列了。有个特别的通常为了提升响应的效率,服务端会将一些进行gzip压缩,然后响应头里面会有个响应头content-encoding:gzip,然后浏览器会再进行解压

6.页面渲染

在接收响应成功之后,浏览器就开始渲染页面了。其步骤包括:1.解析HTMLDOM树、2.解析CSSCSS树、3.合并DOM树已经CSS树、4.遍历整个节点、5.按照CSS的优先级渲染每个节点的样式,呈现在屏幕上。当浏览器遇到一个script标记时,DOM构建将暂停,直至脚本完成执行,然后继续构建DOM。每次去执行JS脚本都会严重阻塞DOM树的构建,如果JS脚本还操作的CSSOM,而正好这个CSSOM还没有下载和构建,浏览器甚至会延迟脚本执行和构建DOM,直至完成其CSSOM的下载和构建。所以这里通常会有个常见的小优化,CSS放在顶部,JS放在底部,并且减少使用在加载的过程中操作DOM,当然这也是比较老的做法了。在页面加载的过程中,所写的CSSJS也要尽量避免重绘和回流

7.四次握手

四次握手用于断开Tcp连接,客户端发送请求表示要断开连接,服务接受到请求后响应,客户端收到响应准备断开连接,服务端将其余数据或没响应的数据全部返给客户端(即又一次握手),客户端收到后响应后再发送请求确认关闭,服务端收到后断开连接

1)客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。 TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。 2)服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。 3)客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。 4)服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。 5)客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2∗∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。 6)服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。

参考链接

博客