1、解析URL
当在浏览器中输入URL后,浏览器首先对拿到的URL进行识别。如果输入的 URL 中的协议或者主机名不合法,将会把地址栏中输入的内容传递给搜索引擎。如果没有问题,浏览器会检查 URL 中是否出现了非法字符,如果存在非法字符,则对非法字符进行转义后再进行下一过程。
URL一般包括几大部分:
- Protocol:协议,譬如有http、https、ftp等
- Host:主机域名或IP地址
- Port:端口号,通常端口号不常见是因为大部分都是使用默认的端口,如HTTP默认端口80,HTTPS默认端口443
- Path,目录路径
- Query,即查询参数
- Fragment,即#后的hash值,一般用来定位到某个位置
2、检查缓存
根据下图的逻辑,判断是直接使用缓存内容还是重新向服务器请求资源。
- 通过
Cache-Control
和Expires
来检查是否命中强缓存,命中则直接取本地磁盘的html(状态码为200 from disk(or memory) cache
,内存or磁盘); - 如果没有命中强缓存,则会向服务器发起请求(先进行下一步的TCP连接),浏览器携带标识
If-Modified-Since
(从什么时间以来是否改变) 和If-None-Match
(是否匹配不到)。服务器通过Etag
和Last-Modify
来与服务器确认返回的响应是否被更改(协商缓存),若无更改则返回状态码(304 Not Modified
),浏览器取本地缓存; - 若强缓存和协商缓存都没有命中则返回请求结果。
3、DNS解析域名的实际IP地址
可以在浏览器中输入IP地址浏览网站,也可以输入域名查询网站,虽然得出的内容是一样的但是调用的过程不一样。输入IP地址是直接从主机上调用内容,输入域名是通过域名解析服务器指向对应的主机的IP地址,再从主机调用网站的内容。
在进行DNS解析时,会经历以下步骤:
DNS 解析首先会从你的浏览器的缓存中去寻找是否有这个网址对应的 IP 地址,如果没有就向OS系统的 DNS 缓存中寻找,如果没有就是路由器的 DNS 缓存, 如果没有就是 ISP 的DNS 缓存中寻找。
所以,缓存的寻找过程就是: 浏览器 -> 系统 -> 路由器 -> ISP。
如果在某一个缓存中找到的话,就直接跳到下一步。
如果都没有找到的话,就会向 ISP 或者公共的域名解析服务发起 DNS 查找请求。这个查找的过程还是一个递归查询的过程。
4、建立 TCP 连接。
TCP 协议通过三次握手建立连接。
客户端通过 SYN
报文段发送连接请求,确定服务端是否开启端口准备连接。状态设置为 SYN_SEND
;
服务器如果有开着的端口并且决定接受连接,就会返回一个 SYN+ACK
报文段给客户端,状态设置为 SYN_RECV
;
客户端收到服务器的 SYN+ACK
报文段,向服务器发送 ACK
报文段表示确认。此时客户端和服务器都设置为 ESTABLISHED
状态。连接建立,可以开始数据传输了。
翻译成大白话就是:
客户端:你能接收到我的消息吗?
服务端:可以的,那你能接收到我的回复吗?
客户端:可以,那我们开始聊正事吧。
为什么是3次?:避免历史连接,确认客户端发来的请求是这次通信的人。
为什么不是4次?:3次够了第四次浪费
5、若协议是https则会做加密
HTTPS = HTTP + 加密 + 认证 + 完整性保护
- 要先申请CA证书,并安装在服务器上(一个文件,配置nginx支持监听443端口开启ssl并设置证书路径)
- 浏览器发送请求;
- 网站从浏览器发过来的加密规则中选一组自身也支持的加密算法和hash算法,并向浏览器发送带有公钥的证书,当然证书还包含了很多信息,如网站地址、证书的颁发机构、过期时间等。
- 浏览器解析证书。
- 验证证书的合法性。如颁发机构是否合法、证书中的网站地址是否与访问的地址一致,若不合法,则浏览器提示证书不受信任,若合法,浏览器会显示一个小锁头。
- 若合法,或用户接受了不合法的证书,浏览器会生成一串随机数的密码(即密钥),并用证书中提供的公钥加密。
- 使用约定好的hash计算握手消息,并使用生成的随机数(即密钥)对消息进行加密,最后将之前生成的所有消息一并发送给网站服务器。
- 网站服务器解析消息。用已有的私钥将密钥解密出来,然后用密钥解密发过来的握手消息,并验证是否跟浏览器传过来的一致。然后再用密钥加密一段握手消息,发送给浏览器。
- 浏览器解密并计算握手消息的HASH,如果与服务端发来的HASH一致,此时握手过程结束,之后所有的通信数据将由之前浏览器生成的随机密码并利用对称加密算法进行加密。这里浏览器与网站互相发送加密的握手消息并验证,目的是为了保证双方都获得了一致的密码,并且可以正常的加密解密数据,为后续真正数据的传输做一次测试。
6、发送HTTP请求
TCP连接建立后,浏览器向服务器发送HTTP请求:
- 请求行:包含请求方法(GET/POST)、URL和HTTP版本。
- 请求头:包含浏览器类型、请求数据类型、授权信息等。
- 请求体:对于POST请求,包含提交的数据。
7、服务器处理请求并返回响应
服务器接收到HTTP请求后,进行处理并返回HTTP响应:
- 响应行:包含HTTP版本和状态码(如200表示成功)。
- 响应头:包含内容类型、日期、服务器信息等。
- 响应体:包含请求的资源,如HTML文档、CSS、JS文件等。
8、 浏览器接收响应并处理
浏览器接收到服务器的响应后,开始处理和渲染页面:
- 解析HTML:浏览器解析HTML文档,构建DOM树。
- 解析CSS:浏览器解析CSS文件,生成CSSOM(CSS对象模型)。
- 构建渲染树:结合DOM树和CSSOM,生成渲染树。
- 回流和重绘:根据生成的渲染树,进行回流(Layout),得到节点的几何信息(位置,大小)。根据渲染树以及回流得到的几何信息,得到节点的绝对像素。将像素发送给GPU,展示在页面上。
- 执行JavaScript:如果HTML中包含JS脚本,浏览器会解析并执行JS代码,可能会修改DOM或CSSOM,重新布局和绘制页面。
9、 关闭TCP连接(四次挥手)
当页面加载完成或连接超时,浏览器和服务器通过四次挥手关闭TCP连接:
-
客户端发送FIN包:
- 客户端向服务器发送一个FIN(Finish)包,请求关闭连接。这个包包含一个序列号(seq=u)。
- 状态转换: 客户端从ESTABLISHED状态转换为FIN-WAIT-1状态。
-
服务器返回ACK包:
- 服务器接收到FIN包后,发送一个ACK包,确认已收到关闭请求。这个包包含对客户端FIN包的确认号(ack=u+1)。
- 状态转换: 服务器从ESTABLISHED状态转换为CLOSE-WAIT状态,客户端接收到ACK包后从FIN-WAIT-1状态转换为FIN-WAIT-2状态。
-
服务器发送FIN包:
- 服务器发送一个FIN包,请求关闭连接。这个包包含一个序列号(seq=v)。
- 状态转换: 服务器从CLOSE-WAIT状态转换为LAST-ACK状态。
-
客户端返回ACK包:
- 客户端接收到FIN包后,发送一个ACK包,确认已收到服务器的关闭请求。这个包包含对服务器FIN包的确认号(ack=v+1)。
- 状态转换: 客户端从FIN-WAIT-2状态转换为TIME-WAIT状态,服务器接收到ACK包后进入CLOSED状态。客户端在经过一段时间(通常为2倍的MSL,最大报文段寿命)后进入CLOSED状态,完成连接关闭。