阶段一:用户操作与浏览器初步处理 (应用层)
-
用户输入URL: 你在浏览器地址栏输入一个网址,比如 http://www.example.com/index.html,然后按下回车。
-
URL解析:
-
浏览器首先会解析这个URL,提取出关键信息:
- 协议 (Protocol): http (表明使用HTTP协议)
- 域名 (Domain Name): www.example.com (需要访问的服务器的名称)
- 路径 (Path): /index.html (请求服务器上的具体资源/文件)
- 如果URL中还包含端口号 (如 http://www.example.com:8080),也会被解析出来。HTTP默认端口是80,HTTPS是443。如果省略,则使用默认端口。
-
如你笔记中提到的,如果路径部分省略 (如只输入 www.example.com),浏览器通常会请求服务器根目录下的默认文件(如 index.html 或 default.html)。
-
-
检查浏览器缓存 (DNS及页面缓存):
- 浏览器会先检查自己的缓存中是否有这个域名对应的IP地址 (DNS缓存)。
- 同时,浏览器也会检查是否有该URL的页面缓存 (HTTP缓存),如果缓存有效且未过期,可能会直接从缓存中加载页面,这个过程就大大简化了。但我们这里讨论完整流程,假设缓存未命中或已过期。
-
构建HTTP请求报文:
-
浏览器确定了要请求的资源和服务器后,会构建一个HTTP请求报文。
-
典型的HTTP请求报文包含:
- 请求行 (Request Line): 例如 GET /index.html HTTP/1.1 (方法、请求的URI、HTTP协议版本)
- 请求头 (Request Headers): 包含一系列键值对,如 Host: www.example.com, User-Agent: Mozilla/5.0 ..., Accept: text/html, Connection: keep-alive 等,提供了关于客户端和请求的附加信息。
- 请求体 (Request Body): 对于GET请求通常为空。对于POST等请求,会包含要发送给服务器的数据。
-
正如你的笔记所说,这个“孤单小弟HTTP”数据包准备好了,但它不知道该往哪里去。
-
阶段二:域名解析为IP地址 (应用层 - DNS)
-
DNS查询:
-
HTTP请求需要发送到目标服务器,但这需要目标服务器的IP地址,而不是域名。因此,操作系统会发起DNS查询。
-
查询顺序 (涉及多级缓存),正如你笔记中详细描述的:
- 浏览器缓存: 浏览器自身查看有无该域名的DNS缓存。
- 操作系统缓存: 若浏览器缓存未命中,查询操作系统的DNS缓存。
- Hosts文件: 操作系统查看本地的 hosts 文件是否有该域名的静态配置。
- 本地DNS服务器 (LDNS): 如果以上均未命中,操作系统会将DNS查询请求发送给在网络配置中指定的本地DNS服务器(通常由你的ISP提供,或者你手动配置的如Google的 8.8.8.8)。
-
本地DNS服务器的解析过程 (递归或迭代查询):
在域名中,越靠右的位置表示其层级越高。
- LDNS收到请求后,先查自己的缓存。
- 若LDNS缓存未命中,它会向根DNS服务器发起查询。根服务器不会直接给出IP,而是告诉LDNS管理 .com (或其他顶级域) 的顶级域DNS服务器 (TLD DNS Server) 的地址。
- LDNS再向TLD DNS服务器查询。TLD DNS服务器会告诉LDNS负责 example.com 这个域名的权威DNS服务器 (Authoritative DNS Server) 的地址。
- LDNS最终向权威DNS服务器查询 www.example.com 的IP地址。权威DNS服务器拥有该域名的最终解析记录。
- 权威DNS服务器将IP地址返回给LDNS。
- LDNS将IP地址返回给操作系统,并缓存该记录以备后续查询。
-
操作系统得到IP地址后,将其返回给浏览器。
-
阶段三:建立TCP连接 (传输层)
-
TCP三次握手:
-
HTTP协议通常基于可靠的TCP协议进行传输。在发送HTTP请求之前,浏览器(客户端)需要与服务器(通过上一步获得的IP地址和HTTP默认端口80或HTTPS的443)建立TCP连接。
-
这个过程就是著名的三次握手:
- SYN (同步序列编号): 客户端发送一个TCP段,其中SYN标志位置1,并选择一个初始序列号 (client_isn)。客户端进入 SYN-SENT 状态。
- SYN+ACK (同步确认): 服务器收到SYN后,如果同意连接,则回复一个TCP段,其中SYN和ACK标志位都置1,确认号 (ack) 设置为 client_isn + 1,并选择自己的初始序列号 (server_isn)。服务器进入 SYN-RCVD 状态。
- ACK (确认): 客户端收到服务器的SYN+ACK后,发送一个ACK段,其中ACK标志位置1,确认号 (ack) 设置为 server_isn + 1。客户端进入 ESTABLISHED 状态。服务器收到这个ACK后,也进入 ESTABLISHED 状态。
-
三次握手的目的是同步双方的初始序列号,并确认双方都具有收发数据的能力。
-
阶段四:数据在协议栈中的封装与发送 (传输层、网络层、数据链路层、物理层)
现在,HTTP请求报文准备好了,TCP连接也建立了,数据要开始真正的旅程了。
-
应用层数据交给传输层: 浏览器将HTTP请求报文交给操作系统的协议栈中的TCP模块。
-
传输层封装 (TCP段):
- TCP模块为HTTP报文添加TCP头部,形成TCP段 (TCP Segment)。
- TCP头部包含:源端口号(浏览器随机分配的临时端口)、目标端口号(Web服务器的80或443端口)、序列号、确认号、窗口大小、各种标志位等。
- 如果HTTP报文过大,超过了MSS (Maximum Segment Size),TCP会将其分割成多个TCP段。
-
网络层封装 (IP包):
-
TCP段被交给IP模块。IP模块为其添加IP头部,形成IP数据包 (IP Packet)。
-
IP头部包含:源IP地址(客户端IP)、目标IP地址(服务器IP)、协议号(指明上层是TCP,值为6)等。
-
选择源IP地址和出口网卡: 如果客户端有多个网卡(多个IP地址),操作系统会根据路由表和目标IP地址来决定使用哪个网卡的IP作为源IP,以及从哪个物理接口发送出去。
- 路由表匹配规则:将目标IP与路由表中各条目的子网掩码进行“与”运算,看结果是否与该条目的目标网络地址匹配。如果有多条匹配,通常选择最长前缀匹配的条目。如果没有特定匹配,则走默认网关。
-
-
数据链路层封装 (以太网帧):
-
IP数据包被交给网络接口层(例如以太网驱动程序)。它会为IP包添加以太网帧头和帧尾,形成以太网帧 (Ethernet Frame)。
-
帧头包含:目标MAC地址和源MAC地址,以及类型字段(指明上层是IP协议,值为 0x0800)。
-
帧尾包含:帧校验序列 (FCS),用于差错检测。
-
确定目标MAC地址 (ARP):
- 情况1:目标服务器与客户端在同一子网:客户端需要目标服务器的MAC地址。它会先检查自己的ARP缓存表。如果有缓存,直接使用。如果没有,客户端会发送一个ARP广播请求:“谁的IP是 [服务器IP]?请告诉我你的MAC地址。” 同一子网内的目标服务器收到后会单播回复其MAC地址。客户端收到后更新ARP缓存。
- 情况2:目标服务器与客户端不在同一子网:数据包需要发送给默认网关 (路由器)。客户端需要默认网关的MAC地址。同样,先查ARP缓存,没有则发送ARP广播请求:“谁的IP是 [默认网关IP]?请告诉我你的MAC地址。” 默认网关回复其MAC地址。
- 关键点:此时,以太网帧中的目标MAC地址是下一跳设备(同一子网的服务器,或默认网关)的MAC地址,而IP包中的目标IP地址始终是最终Web服务器的IP地址。
-
-
物理层传输:
- 网卡将以太网帧转换成电信号(或光信号),通过物理介质(网线、光纤、无线电波)发送出去。
阶段五:数据包在网络中的传输 (路由器与交换机)
-
交换机 (二层设备):
-
数据帧到达本地网络的交换机。
-
交换机工作在数据链路层。它查看帧的目标MAC地址。
-
交换机维护一个MAC地址表(MAC地址与端口的映射关系)。
- 如果目标MAC地址在表中,交换机直接从对应的端口将帧转发出去。
- 如果目标MAC地址不在表中,或者目标MAC是广播地址 (FF:FF:FF:FF:FF:FF),交换机会将帧从除了接收端口外的所有其他端口泛洪 (flood) 出去。
- 交换机通过学习源MAC地址来构建和更新其MAC地址表(“这个MAC地址是从哪个端口进来的”)。
-
交换机不改变帧的内容,原样转发。它没有IP地址和MAC地址的概念(指其端口)。
-
-
路由器 (三层设备):
-
如果数据包的目的地不在本地子网,它会被发送到默认网关——路由器。
-
路由器工作在网络层。它的每个端口都有自己的MAC地址和IP地址。
-
接收操作: 路由器端口接收到以太网帧,检查目标MAC地址是否是自己的端口MAC。如果是,则接收该帧,并去掉帧头帧尾,暴露出IP包。
-
路由决策: 路由器查看IP包的目标IP地址,并查询其路由表。
- 路由表包含:目标网络地址、子网掩码、下一跳IP地址(Next Hop)、出接口(Interface)。
- 路由器根据目标IP地址匹配路由表条目,找到数据包应该从哪个接口转发出去,以及下一跳路由器的IP地址是什么。如果没有精确匹配,则走默认路由。
-
重写MAC头部 (关键步骤): 路由器确定了下一跳后,需要为IP包重新封装一个新的以太网帧头。
- 新的帧头中,源MAC地址是路由器发送端口的MAC地址。
- 目标MAC地址是下一跳路由器(或最终目标主机,如果下一跳就是目标且在同一链路)的MAC地址。这个MAC地址同样通过ARP协议(查询ARP缓存或发送ARP请求)获得。
- IP包头中的源IP和目标IP地址在整个路由过程中保持不变!
-
发送操作: 新的帧通过物理层发送出去。
-
这个过程(解封装、查路由表、重新封装、发送)会在每一跳路由器上重复,直到数据包到达目标服务器所在的网络。
-
阶段六:服务器处理请求并返回响应 (应用层、传输层等)
-
服务器接收数据包: 数据包最终到达目标Web服务器。
- 服务器的网卡接收以太网帧,物理层转为数字信号。
- 数据链路层检查目标MAC地址,是自己的,去掉帧头帧尾,交给网络层。
- 网络层检查目标IP地址,是自己的,去掉IP头部,根据协议号(TCP)交给传输层。
- 传输层检查目标端口号(如80),发现是HTTP服务在监听,去掉TCP头部,将HTTP请求报文交给Web服务器应用程序(如Nginx, Apache)。
-
Web服务器处理请求:
- Web服务器解析HTTP请求报文。
- 根据请求的资源(如 /index.html),从服务器的磁盘或其他地方获取内容。
- 可能涉及到后端逻辑处理(如数据库查询、动态页面生成等)。
-
构建HTTP响应报文:
-
服务器构建一个HTTP响应报文。
-
典型的HTTP响应报文包含:
- 状态行 (Status Line): 例如 HTTP/1.1 200 OK (协议版本、状态码、状态描述)
- 响应头 (Response Headers): 如 Content-Type: text/html, Content-Length: 1234, Server: Apache 等。
- 响应体 (Response Body): 实际的网页内容 (HTML, CSS, JavaScript, 图片等)。
-
-
服务器发送响应:
- 这个HTTP响应报文会经历与客户端发送请求时类似的封装过程(TCP封装 -> IP封装 -> 以太网帧封装),但这次源IP/端口是服务器的,目标IP/端口是客户端的。
- 响应数据包通过互联网,经过路由器逐跳转发,最终到达客户端所在的网络。
阶段七:客户端接收并渲染页面 (物理层到应用层)
-
客户端接收数据包: 客户端的网卡接收到来自服务器的响应数据包。
- 经历与服务器接收时类似的解封装过程:物理层 -> 数据链路层 (检查MAC) -> 网络层 (检查IP) -> 传输层 (检查端口,重组TCP段) -> 将HTTP响应报文交给浏览器。
-
浏览器解析并渲染页面:
- 浏览器接收到HTTP响应报文。
- 解析HTTP响应头,根据 Content-Type 等信息知道如何处理响应体(例如,作为HTML文档)。
- 解析HTML: 构建DOM (Document Object Model) 树。
- 解析CSS: 构建CSSOM (CSS Object Model) 树。
- 合并DOM和CSSOM: 构建渲染树 (Render Tree)。
- 布局 (Layout/Reflow): 计算每个节点在屏幕上的确切位置和大小。
- 绘制 (Paint): 将渲染树的节点绘制到屏幕上。
- 处理页面中的其他资源: 如果HTML中包含其他资源(如CSS文件、JavaScript文件、图片),浏览器会针对每一个资源重复上述大部分过程(发起新的HTTP请求 -> DNS查询 -> TCP连接 -> ... -> 下载资源 -> 解析/执行)。这通常是并发进行的。
- 执行JavaScript: JS可能会修改DOM和CSSOM,导致重新布局和绘制。
阶段八:TCP连接关闭 (传输层)
-
TCP四次挥手: 当数据传输完毕(例如浏览器认为页面加载完成,或者一方决定关闭连接),会进行TCP的四次挥手来断开连接。
-
假设客户端发起关闭:
- FIN: 客户端发送一个FIN段,表示数据发送完毕。客户端进入 FIN-WAIT-1 状态。
- ACK: 服务器收到FIN后,回复一个ACK段作为确认。服务器进入 CLOSE-WAIT 状态。客户端收到ACK后进入 FIN-WAIT-2 状态。此时,从客户端到服务器方向的连接关闭,但服务器可能还有数据要发送给客户端。
- FIN: 当服务器也没有数据要发送时,服务器发送一个FIN段给客户端。服务器进入 LAST-ACK 状态。
- ACK: 客户端收到服务器的FIN后,回复一个ACK段。客户端进入 TIME-WAIT 状态(等待一段时间确保服务器收到ACK,防止已失效的请求报文段出现在本连接中)。服务器收到ACK后进入 CLOSED 状态。客户端等待 2MSL (Maximum Segment Lifetime,报文最大生存时间的两倍) 后,也进入 CLOSED 状态。
-
至此,从键入网址到网页显示的整个主要流程就完成了。这是一个极其精妙和复杂的协作过程!