浏览器输入URL到页面加载的全过程
身为前端开发人员,浏览器绝对是咱们日常打交道最多的工具之一。大家可能或多或少都了解过浏览器的底层原理和工作流程,可有时候转头就忘。今天,咱们就来系统地梳理一番,帮大家形成完整的知识链,深度认识一下这位“老伙伴”。这次,咱们聊聊是从输入URL到页面加载的全过程。
一、输入URL并解析
日常上网时,咱们在浏览器地址栏里输入URL地址,然后按下回车键。这时候,浏览器就开始工作啦,它首先会检查输入的URL地址是否合法,确认没问题后再进行解析。
二、缓存查找
浏览器有自己的缓存机制。当咱们输入URL后,它会先去浏览器缓存、系统缓存以及路由缓存里找找,看有没有对应的页面。要是找到了,就直接把页面内容显示出来;要是没找到,就得接着往下走流程了。
三、DNS域名解析
浏览器得知道输入的域名对应的IP地址,才能找到服务器获取数据,这就需要进行DNS域名解析。这个过程还挺复杂的:
- 浏览器先查看自己的缓存,看有没有对应域名的IP地址记录。
- 如果浏览器缓存里没有,就去系统的hosts文件里查找。
- hosts文件里也没有的话,再查看本地DNS缓存。
- 要是还没找到,就会递归查询DNS服务器。
要是DNS服务器也没有这个域名的记录,就会执行迭代查询,从根DNS服务器开始,向更高级别的DNS服务器发送查询请求。举个例子,像“www.juejin.cn”这个域名,“.cn”是顶级域,“juejin.cn”是二级域,“www.juejin.cn”就是三级域,也就是子域 。
可能有人会问,为啥非要进行DNS解析呢?简单来说,URL域名是为了方便我们记忆的,就像是“人类语言”。但计算机和网络设备可不认识这些名字,它们只懂“机器语言”,也就是IP地址。所以,得通过DNS解析把域名翻译成IP地址,这样浏览器才能找到对应的服务器。
四、建立TCP连接
网络通信其实不太可靠,数据容易出现丢失、乱码或者重复的情况。为了保证数据能可靠传输、按顺序传递,还能进行流量和拥塞控制,在向服务器发送请求之前,需要先建立TCP连接,而不是直接请求数据。
TCP连接是通过客户端和服务器端相互发送TCP报文来确认连接的。TCP报文是TCP协议传输数据的基本单位,由首部(Header)和数据部分(Data)组成 ,它有6个控制标志:
- URG:Urgent紧急的,用于优先发送紧急数据。
- ACK:确认号有效,建立连接后所有报文都必须把ACK设为1 。
- PSH:提示接收端立即将数据提交给应用层,比如在实时聊天的时候就会用到。
- RST:重置连接,用于异常终止连接或者拒绝连接。
- SYN:同步序列号,在建立连接的时候会用到,也就是三次握手过程。
- FIN:终止连接,在四次挥手释放连接的时候会用到。
TCP连接要经过三次握手:
- 第一次:SYN(同步):客户端发送一个TCP报文,里面把SYN设为1,表示请求建立连接,还会带上一个随机序列号(Seq=x),这是为了防止历史重复连接。发送完后,客户端就进入SYN_SENT状态 。
- 第二次:SYN – ACK(同步 – 确认):服务端收到客户端的SYN报文后,会回复一个报文。这个报文里SYN和ACK都设为1 ,表示确认客户端的SYN,同时也带上自己的随机序列号(Seq=y)和确认号(Ack=x + 1),意思是期望下次收到x + 1的数据。回复完后,服务端进入SYN_RCVD状态 。
- 第三次:ACK(确认):客户端收到服务端的SYN – ACK报文后,再发送一个最终确认报文。这个报文把ACK设为1 ,序列号设为Seq=x + 1,确认号设为Ack=y + 1。服务端收到这个报文后,双方就进入ESTABLISHED状态 ,连接建立完成。
这里要注意,只有第三次握手的时候才可以携带数据,前两次握手主要是客户端和服务器端确认连接、同步序列号。可能有人好奇,为啥是三次握手,不是两次或者四次呢?假如是两次握手,客户端发送SYN报文后,要是因为网络堵塞又重新发了一次。服务器端先收到的可能是旧的SYN报文,回复给客户端时,客户端发现期望的确认号不对,就会发送RST报文重新连接,可这时候服务器已经建立连接了,就会浪费服务器资源。三次握手就刚刚好,客户端能确认服务端可以接收请求,服务端也能确认客户端能接收回应,双方都知道连接已经建立好了。
五、发起HTTP请求
TCP连接建立好之后,浏览器就会正式发送HTTP请求去读取文件。请求报文是根据用户请求的URL来设置的,包括请求方法、请求头和请求体。比如说:
GET /index.html HTTP/1.1 Host: www.example.com User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0 Accept: text/html,application/xhtml+xml Accept-Language: en-US,en;q=0.9 Connection: keep-alive
六、服务器响应请求并返回结果
服务器收到浏览器的请求后,会这么做:
- 解析请求:分析请求方法、路径以及HTTP版本。
- 处理请求:如果是静态资源,直接读取返回;要是动态资源,就调用后端代码,生成HTML或JSON数据。
- 构造HTTP响应:服务器返回HTTP响应报文,像下面这样:
HTTP/1.1 200 OK // 状态行(状态码 + 状态描述) // 响应头 Content-Type: text/html; charset=utf-8 // 数据类型 Content-Length: 1234 // 数据长度 Set-Cookie: session_id=abc123 // 设置Cookie // 响应体 <!DOCTYPE html> <html>...</html>
七、浏览器接收并处理响应
- 解析响应:浏览器收到响应后,会先检查状态码:
- 200:表示成功,接着解析后续内容。
- 301/302:代表重定向,需要跳转到新的URL。
- 404:说明资源不存在。
- 500:表示服务器出错误了。
- 解析响应头:
- Content-Type:这个字段决定了浏览器如何处理数据,比如“text/html”类型的数据,浏览器就会把它渲染成网页。
- Set-Cookie:用于存储Cookie,在会话管理的时候会用到。
八、浏览器解析HTML、构建DOM、CSSOM、渲染页面
- 构建DOM树:浏览器通过词法分析,把HTML内容解析成DOM树(dom tree)。DOM树是由dom元素和属性节点组成的,树的根节点是document对象。
- 构建CSS规则树:生成CSS规则树(CSS Rule Tree) 。
- 构建render树:Web浏览器把DOM和CSSOM结合起来,构建出渲染树(render tree)。
- 布局(Layout):计算出每个节点在屏幕中的位置。
- 绘制(Painting):遍历render树,使用UI后端层绘制每个节点,这样页面就显示出来啦。
九、关闭连接
当所有数据都传输完成后,客户端或者服务器会主动关闭连接,这时候就会触发TCP四次挥手,也就是“优雅断开连接”的过程:
- 第一次挥手:A → B(FIN):客户端A发送一个带FIN标志位的TCP报文,告诉服务端B:“我不发数据了,我这边结束发送。” 发送完后,A进入FIN_WAIT_1状态 。
- 第二次挥手:B → A(ACK):服务端B收到后,回复一个ACK报文,告诉A:“好,我知道你不发了。” A收到这个ACK后,进入FIN_WAIT_2状态 ,这时候B还能继续发送数据,因为它的发送通道还没关闭。
- 第三次挥手:B → A(FIN):等B把数据都发完了,就发送一个带FIN的TCP报文,告诉A:“我也发完了,准备关闭。” 发完后,B进入LAST_ACK状态 。
- 第四次挥手:A → B(ACK):A收到B的FIN报文后,回复一个ACK报文,告诉B:“收到,我这就断了。” 然后A进入TIME_WAIT状态 ,等待2倍的最大报文生存时间(MSL),确保服务器接收到ACK。最后,A完全关闭连接,B收到ACK后,也关闭连接。
好啦,以上就是从输入URL到页面加载的整个过程,希望这次梳理能让大家对浏览器的工作流程有更清晰的认识!