一个页面从输入URL到加载显示完成,这个过程发生了什么?
一、解析URL
1、流程
当在浏览器中输入URL后,浏览器首先对拿到的URL进行识别。判断你输入的是一个合法的 URL 还是一个待搜索的关键词,并且根据你输入的内容进行自动补全、字符编码等操作,解析 URL 得到里面的参数,将域名和需要请求的资源分离开来,从而了解需要请求的是哪个服务器,请求的是服务器上什么资源等等。
URL 就是我们输入的网址,而网址里面含有域名。我们常见的URL是这样的:http://www.baidu.com,这个域名由三部分组成:协议名、域名、端口号,这里端口是默认所以隐藏。
2、URL格式:
protocol://hostname[:port]/path/[;parameters][?query][#fragment]
- protocol:传输协议
- hostname:存放资源的服务器的域名或 IP 地址。
- port:端口号。可选,省略时使用方案的默认端口,HTTP默认为80。
- path:文件路径。
- parameters:用于指定特殊参数。可选
- query:用于给动态网页传递参数。可有多个参数,用“&”符号隔开,每个参数的名和值用“=”符号隔开。可选。
- fragment:信息片段,用于指定网络资源中的片段。可选。例如一个网页中有多个名词解释,可使用fragment直接定位到某一名词解释。
3、示例
https://www.baidu.com/s?wd=nginx&tn=25017023_2_dg&ch=8&ie=utf-8
- 协议:
https://
- 表示这是一个安全的超文本传输协议,用于加密和安全地传输数据。 - 域名:
www.baidu.com
- 这是网站的域名,表示链接指向的是百度的主页。 - 查询参数:
s
: 这是查询参数的键,wd
是search word
的缩写,表示搜索词。nginx
: 这是查询参数的值,表示用户搜索的关键词是nginx
。
- 追踪代码:
tn=25017023_2_dg
: 这是追踪代码,通常用于跟踪搜索来源或搜索结果页面的特定参数。tn
代表track number
,后面的数字和字符是追踪代码的具体值。
- 通道代码:
ch=8
: 这代表搜索结果的通道代码,ch
可能代表channel
,数字8
可能是特定的通道标识。
- 字符编码:
ie=utf-8
: 这表示页面的字符编码是UTF-8,这是一种广泛使用的字符编码,能够支持多种语言的字符。
整个URL的意思是:通过HTTPS协议访问百度的主页,搜索关键词为
nginx
,并且包含了追踪代码和字符编码的信息。
二、浏览器封装HTTP请求报文
1、流程
对 URL
进行解析之后,浏览器确定了目标服务器和文件名,接下来就需要根据这些消息封装成一个 HTTP 请求报文发送出去。
2、HTTP请求报文例子
3、封装
发送端在层与层之间传输数据时,每经过一层必定会被打上一个该层所属的首部信息。反之,接收端在层与层之间传输数据时,每经过一层就会把该层对应的首部信息消去。
三、DNS解析
1、缓存判断
1.1、浏览器缓存
- 浏览器首先检查自身的DNS缓存。
- 如果缓存中有对应域名的有效条目(且未超过其TTL(Time To Live)值,即生存时间),则直接使用该IP地址,从而快速开始建立与目标服务器的连接。
- TTL值由域名所有者在DNS记录中设定,指示了该记录可以被缓存的最大时间长度。
- 没有则调用系统库函数进行查询。
1.2、操作系统缓存
- 当浏览器缓存未命中,操作系统(如Windows、macOS、Linux等)会检查其自身的DNS解析缓存。
- 操作系统也有一个域名解析的过程,在hosts文件里可以读写。
- 同样,如果找到有效且未过期的记录,系统会直接返回IP地址给浏览器。
1.3、路由器缓存
- 如果在浏览器和操作系统缓存中都没有找到匹配项,查询请求会到达用户的路由器。
- 路由器会检查其内置的DNS缓存,看是否之前已经解析过相同域名。
- 如果路由器缓存中有记录且仍然有效,它会直接将IP地址返回给用户设备,避免进一步向外查询
以上三步都是DNS客户端的缓存
2、递归查询至ISP DNS服务器
- 若上述缓存均未命中:用户的设备或路由器将DNS请求递归式地发送给ISP(互联网服务提供商)的DNS服务器。
- ISP DNS 就是在客户端电脑上设置的首选 DNS 服务器,它们在大多数情况下都会有缓存。
- ISP的DNS服务器尝试在其缓存中查找记录。如果找到,直接返回结果;如果没有,ISP DNS服务器将代表用户继续查询,执行迭代查询直到获得最终IP地址。
3、迭代查询过程
- 根域名服务器查询:ISP的DNS服务器(或执行递归查询的任何DNS服务器)首先向全球的根域名服务器查询,请求顶级域名(TLD)服务器的地址。
- 顶级域名服务器查询:根据根域名服务器的回复,查询下一步指向相应的顶级域名服务器(如.com、.org)。
- 权威DNS服务器查询:顶级域名服务器提供负责具体域名的权威DNS服务器地址,最后查询到达这里,权威DNS服务器直接返回该域名对应的IP地址。
4、保存结果至各级缓存
- 无论在哪一步骤找到IP地址:该域名-IP映射记录都会在返回路径上的每一个DNS服务器以及最终用户的设备上被缓存。这包括浏览器、操作系统、路由器以及ISP的DNS服务器,确保未来对该域名的查询能够更快地得到响应。
DNS 使用的是 UDP 协议,也就是说上面各种请求的转发,都是基于 UDP 这个无连接协议的。
四、建立TCP连接(三次握手)
获取到了目标服务器的 IP 地址之后,此时网络层便会通过IP地址寻得对应服务器的物理地址,这个时候就可以开始发送封装好了的 HTTP 请求报文了
那么既然需要发送请求,必然就需要 TCP 通过三次握手为浏览器和服务器之间建立可靠的连接,保证双方都具有可靠的接收和发送能力。
1、三次握手流程图
2、简单理解
- 第一次握手(请求建立连接):
- Alice打电话给Bob:"嗨,Bob,我们今天下午3点在公园门口见面怎么样?"
- 这相当于客户端发送一个SYN(同步序列编号)包给服务器,表示想要建立连接。
- 第二次握手(确认接收):
- Bob接到了电话,回答Alice:"好的,Alice,下午3点公园门口见,我会准时到的。"
- 这就像服务器接收到SYN包后,回复一个SYN-ACK(同步-确认)包给客户端,确认收到了连接请求,并告知自己的初始序列号,表示愿意建立连接。
- 第三次握手(连接确认):
- Alice再次回应Bob:"太好了,Bob,我会准时到的,期待见面!"
- 类比于客户端收到服务器的SYN-ACK后,发送一个ACK(确认)包给服务器,确认客户端已准备好,连接正式建立。
3、具体流程
-
第一次握手(请求建立连接):
- 一开始,客户端和服务端都处于
CLOSED
状态。先是服务端主动监听某个端口,处于LISTEN
状态 - 客户端发送连接请求报文段,将
SYN
标志位设置为 1,表示请求建立连接。seq
为x
。然后,客户端进入SYN_SENT
状态,等待服务器的确认; - 标志位为SYN,表示""请求建立新连接";
序号为seq=x(x一般为1);
- 一开始,客户端和服务端都处于
-
第二次握手(确认接收):
- 服务器收到客户端发送的
SYN
报文段,需要对这个SYN
报文段进行确认,设置ack
为x+1(seq+1)
;同时,自己自己还要发送SYN
请求信息,将SYN
标志位置为 1,seq
为y
;服务器端将上述所有信息放到一个报文段(即 SYN+ACK 报文段)中,一并发送给客户端,此时服务器进入SYN_RCVD
状态; - 标志位为SYN和ACK,表示"确认客户端的报文
seq
序号有效,服务器能正常接收客户端发送的数据,并同意创建新连接"(即告诉客户端,服务器收到了你的数据);
序号为seq=y;
确认号为ack=x+1,表示收到客户端的序号seq并将其值加1作为自己确认号ack的值;
- 服务器收到客户端发送的
-
第三次握手(连接确认):
- 客户端收到服务器的
SYN+ACK
报文段。然后将ACK
标志位设置为 1,表示确认收到服务器同意连接的信号,向服务器发送ACK
报文段,这个报文段发送完毕以后,客户端和服务器端都进入ESTABLISHED
状态,完成 TCP 三次握手。 - 标志位为ACK,表示"确认收到服务器端同意连接的信号"(即告诉服务器,我知道你收到我发的数据了);
序号为seq=x+1,表示收到服务器端的确认号ack,并将其值作为自己的序号值;
确认号为ack=y+1,表示收到服务器端序号seq,并将其值加1作为自己的确认号ack的值;
- 客户端收到服务器的
五、浏览器发送HTTP请求
TCP 三次握手完成后,浏览器与目标服务器之间就建立了一个可靠的虚拟通道,于是浏览器就可以发送自己的 HTTP 请求了。
请求报文的结构
1. 应用层 (HTTP)
- HTTP协议:浏览器构造HTTP请求报文,这是在应用层完成的。HTTP定义了客户端和服务器之间的通信格式,包括请求方法、URL、头部字段和可选的请求体。
2. 传输层 (TCP)
- TCP协议:应用层生成的HTTP报文被传输层的TCP协议封装,通过TCP段的形式发送。TCP负责将数据分割成适当大小的段,为每个段添加源端口和目的端口信息,以及序列号和确认号等,以确保数据的可靠传输、错误检测、流量控制和拥塞控制。
3. 网络层 (IP)
- IP协议:TCP段再被封装到IP数据包中。IP层负责将数据包从源主机路由到目标主机,通过分配给每个数据包一个源IP地址和目的IP地址,实现跨网络的寻址和传输。
4. 数据链路层 (以太网、Wi-Fi等)
- 以太网帧或Wi-Fi帧:在局域网中,IP数据包被进一步封装成适合物理媒介传输的数据帧,例如以太网帧。数据链路层通过MAC地址标识网络中的设备,并提供错误检测功能(如CRC校验)。
六、服务器处理请求
1、流程
2、处理请求
接受 TCP 报文后,会对连接进行处理,对HTTP协议进行解析(请求方法、域名、路径等),并且进行一些验证:
- 验证是否配置虚拟主机
- 验证虚拟主机是否接受此方法
- 验证该用户可以使用该方法(根据 IP 地址、身份信息等)
七、返回HTTP响应
1、响应报文的结构
2、状态码
类别 | 描述 | |
---|---|---|
1xx | 信息性状态码 | 接受的请求正在处理 |
2xx | 成功状态码 | 请求正常处理完毕 |
3xx | 重定向状态码 | 需要进行附加操作以完成请求 |
4xx | 客户端错误状态码 | 服务器无法处理请求 |
5xx | 服务器错误状态码 | 服务器处理请求出错 |
3、常见的状态码
状态码 | 含义 |
---|---|
100 | 请求已经接收,客户端可以继续发送请求 |
101 | 服务器已经收到并且理解了客户端的请求 |
200 | 一切正常 |
204 | 请求成功,无资源可返回 |
206 | 范围请求,请求范围内资源正常返回 |
301 | 永久重定向 |
302 | 暂时重定向,URL可能还会改变 |
400 | 请求存在语法错误 |
401 | 请求需要有通过HTTP认证的认证信息 |
403 | 禁止访问 |
404 | 资源没找到,not found |
500 | 服务端处理请求发生了错误,或web应用存在某些bug或临时的故障 |
503 | 服务器过载或者临时维护 |
504 | 网关超时,代理服务器等待应用服务器响应时的超时 |
八、浏览器接受响应
服务器在接收到浏览器发送的HTTP请求之后,会将收到的HTTP报文封装成HTTP的Request对象,并通过不同的web服务器进行处理,处理完的结果以HTTP的Response对象返回,主要包含状态码,响应头,响应报文三个部分。
1、流程
-
首先查看 Response header,根据不同状态码做不同的事(比如上面提到的重定向)。
-
如果响应资源进行了压缩(比如 gzip),还需要进行解压。
-
然后,对响应资源做缓存。
-
接下来,根据响应资源里的 MIME 类型去解析响应内容(比如 HTML、Image各有不同的解析方式)。
九、浏览器进行语法解析,渲染页面
1、流程图
3、具体流程
下载完的网页将被交给浏览器内核(渲染进程)进行处理:
- 根据顶部定义的DTD类型进行对应的解析方式;
- 渲染进程内部是多线程的,网页的解析将会被交给内部的GUI渲染线程处理;
- 首先渲染线程中的HTML解释器,将HTML网页和资源从字节流解释转换成字符流;
- 再通过词法分析器将字符流解释成词语;
- 之后经过语法分析器根据词语构建成节点;最后通过这些节点组建一个DOM树;
- 这个过程中,如果遇到的DOM节点是JavaScript代码,就会调用JavaScript引擎对JavaScript代码进行解释执行,此时由JavaScript引擎和GUI渲染线程的互斥,GUI渲染线程就会被挂起,渲染过程停止;如果JavaScript代码的运行中对DOM树进行了修改,那么DOM的构建需要从新开始;
- 如果节点需要依赖其他资源,如(图片,CSS等),便会调用网络模块的资源加载器来加载它们,但它们是异步的,不会阻塞当前DOM树的构建;
- 如果遇到的是JavaScript资源URL(没有标记异步),则需要停止当前DOM的构建,直到JavaScript的资源加载并被JavaScript引擎执行后才继续构建DOM;
- 对于CSS,CSS解释器会将CSS文件解释成内部表示结构,生成CSS规则树;
- 然后合并CSS规则树和DOM树,生成render渲染树;
- 最后对render树进行布局和绘制,并将结果通过IO线程传递给Browser控制进程进行显示。
十、关闭TCP连接(四次挥手)
完成一次 HTTP 请求后,服务器并不是马上断开与客户端的连接。在 HTTP/1.1 中,Connection: keep-alive 是默认启用的,表示持久连接,以便处理不久后到来的新请求,无需重新建立连接而增加慢启动开销,提高网络的吞吐能力。
为了避免服务器与客户端双方的资源占用和损耗,当双方没有请求或响应传递时,任意一方都可以发起关闭请求。与创建TCP连接的3次握手类似,关闭TCP连接,需要4次握手。
1、四次挥手流程图
2、简单理解
- 第一次挥手(FIN请求断开):
- Alice对Bob说:"Bob,我们聊得差不多了,我这边准备挂电话了。"
- 这对应于TCP连接中的一方(假设是客户端)发送一个FIN标志位的包给另一方(服务器),表示自己没有更多数据要发送了,希望关闭连接。
- 第二次挥手(ACK确认收到):
- Bob回应Alice:"好的,Alice,我知道了,你随时可以挂。"
- 服务器接收到客户端的FIN包后,回复一个ACK包,确认收到了断开连接的请求,但此时服务器可能还有数据要发送,所以连接并未立即关闭。
- 第三次挥手(服务器FIN请求断开):
- 过了一会儿,Bob说:"Alice,我也讲完了,现在我们真的可以结束了。"
- 一旦服务器准备好关闭连接,它也会发送一个带有FIN标志位的包给客户端,表示它也不再发送数据了。
- 第四次挥手(客户端ACK确认):
- Alice回答:"好的,Bob,再见!祝你有美好的一天。"
- 客户端收到服务器的FIN包后,发送最后一个ACK包,确认收到了服务器的断开请求。此时,两边都知道连接可以安全关闭了。
3、具体流程
-
第一次挥手:
-
客户端想要释放连接,向服务器发送
FIN
报文,将FIN
标记位设置为1,同时指定一个序列号seq=u
。随后客户端进入FIN_WAIT_1
状态。 -
标志位为FIN,表示"请求释放连接";
序号为seq=u;
-
-
第二次挥手:
-
服务器端接收到从客户端发出的
FIN
报文后,确认了客户端想要释放连接,随后服务器端会发送
ACK
报文,并且把客户端的序列号+1作为ACK
报文的序列号值。表明已经收到客户端的报文,此时服务器端处于CLOSE_WAIT
状态。 -
随后服务器端开始准备释放服务器端到客户端方向上的连接。客户端收到从服务器端发出的
ACK
应答报文,确认了服务器收到了客户端发出的释放连接请求。随后客户端结束FIN_WAIT_1
状态进入FIN_WAIT_2
状态。 -
标志位为ACK,表示"接收到客户端发送的释放连接请求";
序号为seq=v;
确认号为ack=u+1;表示是在接收到客户端报文的基础上,将其序号seq值加1作为本段报文确认号ack的值
-
-
第三次挥手:
-
服务器端自从发出
ACK
确认报文之后,经过CLOSE_WAIT
阶段,服务器端将最后数据发送完毕后就向客户端发出连接释放报文段,报文包含FIN
和ACK
标志位。随后服务器端结束CLOSE_WAIT
状态,进入LAST_ACK
状态。并且停止再服务器端到客户端方向上发送数据,但是服务器端仍然能够接收从客户端传输过来的数据。 -
标记位为FIN和ACK,表示"已经准备好释放连接了"。
序号为seq=w;
确认号为ack=u+1;
-
-
第四次挥手:
-
客户端收到来自服务器端发出的
FIN
+ACK
报文,确认了服务器端已做好释放连接的准备,结束FIN_WAIT_2
状态,并向服务器端发送一个ACK
报文作为应答,将服务器端的序列号值+1作为自己的ACK
报文的序列号值。进入TIME_WAIT
状态。 -
服务器端收到从客户端发出的TCP报文之后结束
LAST-ACK
阶段,进入CLOSED
阶段。由此正式确认关闭服务器端到客户端方向上的连接。 -
客户端等待计时器设置的时间2MSL之后,结束
TIME-WAIT
阶段,进入CLOSED
阶段,至此完成"四次挥手"。 -
标记位为ACK,表示"接收到服务器准备好释放连接的信号"。
序号为seq=u+1;表示是在收到了服务器端报文的基础上,将其确认号ack值作为本段报文序号的值。
确认号为ack=w+1;表示是在收到了服务器端报文的基础上,将其序号seq值作为本段报文确认号的值。
-