从浏览器打开 www.baidu.com 地址回车发送请求到看到页面的过程
1. 将 URL 转换成 IP 地址
我们访问网站实际都是通过ip地址访问的,但用户不可能记住这么多的 IP 地址,相比它 www.baidu.com 好记的多。在计算机中有几个 DNS 协议,存储着所有有效的 URL 对 IP 的映射,通过它来将 URL 转换成IP 地址,这步操作就是 DNS 解析。
输入网址回车的时候,
- 浏览器首先查询自己的缓存(以前有没有访问过),如果有则直接拿,否则就到本地的
dns缓存中找。 - 如果本地的
dns缓存中有,取得对应的ip,否则就去查hosts文件。 - 如果
hosts文件中有,取得对应的ip,否则就向本地的dns服务器发起请求, - 如果本地的
dns服务器有,则返回,没有就向根域名服务器发送请求。 - 根域名服务器上并没有记录具体的域名和
ip地址的对应关系,而是告诉本地dns服务器顶级域名服务器的ip,然后向顶级域名服务器发送请求。 - 顶级域名服务器请求返回权威域名服务器的
ip,再向权威域名服务器请求得到url和ip的映射,并返回给本地dns服务器。 - 本地
dns服务器将ip返回给客户端并做缓存。
2. 建立连接
因为 HTTP 在应用层,TCP 在 传输层,在发送 HTTP 请求前,还要进行 TCP 三次握手。
-
客户端首先向服务器发送一个连接请求报文(
SYN = 1,seq = x)- 标记位
SYN置1表示请求建立新连接 - 初始序号为
seq = x - 客户端进入同步已发送(SYN-SENT)状态
- 标记位
-
服务器收到这个包,识别出是一个请求连接包,如果同意,服务器也发送一个确认报文(SYN = 1,ACK = 1,seq = y, ack = x + 1)
-
标记位 SYN 和 ACK 都置1表示确认客户端的报文 seq 序号有效,可以接收数据了并同意建立新连接
-
初始序号为 Seq = y
-
确认号 ack = x + 1,表示收到客户端的序号 seq 并将其值 +1 作为自己的确认号 ack 的值
-
服务器进入同步接收(SYN-RCVD)状态
-
-
客户端收到这个包,发现确认号 ack 为 x + 1,就知道这是一个回应包,于是在发送一个确认报文(ACK = 1,seq = x + 1,ack = y + 1),表示自己确认收到了服务器的回应,请求建立成功。
- 标志位 ACK 表示确认收到服务器端同意连接的信号
- 序号 seq = x + 1
- 确认号 ack = y + 1,表示收到服务器的序号 seq 并将其值 +1 作为自己的确认号 ack 的值
- 客户端进入已连接(ESTABLISHED)状态
三次握手的作用是确保客户端和服务器双方的都具有发送和接收数据的能力。
3. 发送 HTTP 请求
客户端向服务器发送请求报文,包括请求行、请求头和请求体。
4. 服务器响应并返回请求结果
服务器收到请求,执行服务器代码,将数据封装到一个 http 响应中发送给客户端。
5. 收到响应结果并解析渲染
浏览器收到响应结果,获取 html 文件,并开始解析:
-
解析 HTML / CSS(html/css parse)
-
构建 DOM 树
渲染引擎解析 HTML 文档,将它转化成标签,然后再转化成 DOM 节点(包括 JS 动态生成的)生成内容树(DOM Tree)
生成 DOM 树的详细过程:
字节->字符->标签->节点->DOM 树 -
构建 CSSOM 树(construct)
解析 CSS
<style>标签和行内样式(包括 js 生成的样式和外部 css 文件)生成 CSSOM 树
渲染阻塞:当浏览器遇到 script 时,DOM 树和 CSSOM 树将暂停直至脚本完全执行,然后继续构建。
-
-
构建渲染树(construct)
当 DOM 树和 CSSOM 树都构建完毕,除隐藏元素(display: none 的节点和 head 节点)外,浏览器将它们组合,计算每个节点的最终样式,并构建渲染树(render tree)
-
布局渲染树(layout/reflow)
遍历渲染树,计算每一个元素的大小、 位置等,每个节点在屏幕上出现的精确坐标。
-
绘制渲染树(paint/repaint)
遍历渲染树,使用 UI 层来绘制每个节点。
6. 断开连接
当浏览器加载完所有资源时,还需要与服务器断开连接,这个过程就过是4次挥手。
-
首先客户端发出一个连接释放的报文(FIN = 1,seq = u),并停止再发送数据,主动关闭 TCP 连接。
- 终止控制位 FIN 置 1,表示请求释放连接
- 序号 seq = u,它等于前面已传送过的数据的最后一个字节的序号加 1
- 客户端进入终止等待 1(FIN-WAIT-1)状态
-
服务器收到后确认客户端想要释放连接,就发出确认报文(ACK = 1,seq = v,ack = u + 1),客户端收到服务器的确认。
-
标记位 ACK 表示接收到客户端想要释放连接的请求
-
序号 seq = v,v 等于服务器之前已传送过的数据的最后一个字节的序号加 1
-
确认号 ack = u + 1 表示收到客户端的序号 seq 并将其值 +1 作为自己的确认号 ack 的值
-
服务器进入关闭等待(CLOSED-WAIT)状态
-
客户端进入终止等待(FIN-WAIT-2)状态
这时的 TCP 处于半关闭状态,客户端已经没有数据要发送了,但服务器如果要向客户端发送数据,客户端仍需接收。现在的连接是单向的。
-
-
如果服务器已经没有数据要发送了,进程就通知 TCP 关闭连接,并向客户端发送连接释放报文(FIN = 1,ACK = 1,seq = w,ack = u + 1),等待客户端确认。
- 标记位 FIN,ACK 都置为 1,表示已经准备释放连接了
- 序号 seq = w(服务器可能又发送了一些数据)
- 确认号 ack = u + 1,重复上次已发送过的确认号
- 服务进入最后确认(LAST-ACK)状态
-
客户端接收到服务器发送的连接释放报文,确认了服务器已做好释放连接的准备,并向服务器发送一段报文(ACK = 1,seq = u + 1, ack = w + 1)。服务接收到报文并关闭连接,客户端等待一段时间后也关闭连接。
- 标记位 FIN = 1,表示接收到服务器准备释放连接的信号
- 序号 seq = u + 1
- 确认号 ack = w + 1
- 客户端进入时间等待(TIME-WAIT)状态
- 服务器进入关闭状态
- 客户端过一段时间进入关闭状态