请求网络的通用流程
# 01.请求网络的通用流程
# 目录介绍
- 01.工作案例引入
- 1.1 一个诡异的网络故障
- 1.2 为什么要学网络请求流程
- 02.浏览器输入链接
- 2.1 思考几个问题
- 2.2 网络访问流程
- 2.3 会发生哪些过程
- 03.网络分层设计
- 3.1 思考分层问题
- 3.2 看分层的案例
- 3.3 设计网络思路
- 3.4 方案设计考量
- 3.5 网络分层优势
- 3.6 OSI真的很难学
- 3.7 TCP/IP-4层模型
- 04.应用层设计流程
- 4.1 搞懂约定协议
- 4.2 HTTP传输协议
- 4.3 域名解析IP
- 4.4 DNS域名系统
- 4.5 数据处理转化
- 05.传输层设计流程
- 5.1 传输协议设计
- 5.2 TCP和UDP协议
- 5.3 为何要端口号
- 06.网络层设计流程
- 6.1 MAC层设计
- 6.2 网关
- 6.3 路由协议转发
- 07.请求流程深度剖析
- 7.1 URL解析的细节
- 7.2 DNS查询深度
- 7.3 TCP连接建立
- 7.4 TLS握手过程
- 7.5 HTTP请求构造
- 08.响应处理与渲染
- 8.1 服务器处理请求
- 8.2 响应数据回传
- 8.3 浏览器解析渲染
- 8.4 关键渲染路径
- 09.网络分层协议总览
- 9.1 各层协议汇总
- 9.2 数据封装全景
- 9.3 抓包工具验证
- 10.思考题与作业
- 10.1 基础思考题目
- 10.2 进阶思考题目
- 10.3 动手实践作业
# 01.工作案例引入
# 1.1 一个诡异的网络故障
场景:小周是一名工作两年的后端工程师,在公司开发一个电商API。某天收到运营反馈:"后台管理页面经常加载到一半卡住,等了十几秒才刷出来,有时候直接超时"。
关键是这个故障很"诡异"——小周自己在服务器上执行 curl 完全正常,办公室WiFi下用浏览器也正常,但运营同事在公司外(比如咖啡厅、家里)访问就频繁卡顿。
小周在服务器上抓包排查:
# 服务器上 curl 测试,一切正常
$ curl -w "TCP: %{time_connect}s, TTFB: %{time_starttransfer}s, Total: %{time_total}s" -o /dev/null -s https://api.shop.com/health
TCP: 0.035s, TTFB: 0.082s, Total: 0.120s
# 运营同事描述的情况
1. 打开页面 → 白屏等待 5-8 秒
2. 偶尔能加载出来 → 图片加载到一半卡住
3. 刷新几次后偶尔能正常用几分钟
4. 切换手机热点(4G)→ 恢复正常!
2
3
4
5
6
7
8
9
疑惑:服务器是好的(curl 测试正常),办公室网络也正常,为什么从外网访问就卡?切换4G就恢复了,说明问题不在服务器,不在代码——那到底在哪?
继续排查发现:运营同事使用的网络环境,DNS解析响应不稳定,有时正常(5ms),有时超时重试(3-5秒)。更关键的是,网站用了CDN,有些图片资源的CDN节点在小周所在地区没有覆盖,回源路径又因为运营商互联问题延迟极高。
flowchart LR
A["运营同事<br/>(咖啡厅WiFi)"] --> B["DNS解析<br/>超时3秒"]
B --> C["TCP连接<br/>正常"]
C --> D["HTTPS握手<br/>正常"]
D --> E["请求管理API<br/>正常"]
E --> F["请求图片CDN<br/>回源超时"]
F --> G["页面卡住<br/>等超时"]
style A fill:#FFB6C1
style B fill:#FF6B6B
style F fill:#FF6B6B
2
3
4
5
6
7
8
9
10
追问链:
- "为什么同一个域名在办公室和咖啡厅解析结果不一样?" → 不同网络环境使用的DNS服务器不同,递归解析链路的延迟和缓存情况也不同
- "curl 测服务器是好的,但页面加载就是慢——说明问题不在服务器,在哪?" → 在网络链路上——DNS解析、CDN回源、跨运营商互联都可能成为瓶颈
- "切换4G就恢复正常,这说明了什么?" → 4G网络和咖啡厅WiFi走的是不同的网络路径,4G的DNS服务器性能更好、CDN节点更近
- "从浏览器输入URL到页面完全加载,中间经历了多少个环节?" → 这是一条完整的网络请求链路:DNS→TCP→TLS→HTTP请求→服务器处理→CDN→浏览器渲染
- "每个环节如果出了问题,现象分别是什么?怎么定位和排查?" → 这就是本章要回答的核心问题
小周最后定位到:运营同事的WiFi网络使用了性能很差的运营商DNS,导致DNS查询经常超时;同时该运营商的网络到CDN源站的跨网链路质量很差。修复方案:切到公共DNS(223.5.5.5),并为该地区配置了额外的CDN节点。
这一串追问,答案全部写在网络请求的通用流程里。
# 1.2 为什么要学网络请求流程
flowchart LR
A["输入URL"] --> B["URL解析"]
B --> C["DNS查询"]
C --> D["TCP握手"]
D --> E["TLS握手"]
E --> F["HTTP请求"]
F --> G["服务器处理"]
G --> H["响应回传"]
H --> I["浏览器渲染"]
style A fill:#90EE90
style I fill:#90EE90
2
3
4
5
6
7
8
9
10
11
你每天都在用浏览器上网,从输入 URL 到页面加载完成,背后是一条精密的多层协议链。但大多数开发者只关注应用层代码,对这条链路中的其他环节缺乏感知:
- 为什么有时候页面加载慢,但
curl测试又是好的?——网络路径不同,瓶颈可能在中间环节 - 为什么有的网站第一次打开很慢,后面就快了?——DNS缓存和TCP连接复用在起作用
- 为什么明明是同一个WiFi,别人能打开就你打不开?——DNS配置不同,解析结果可能不同
- 为什么抓到数据包了,却看不懂每一层在做什么?——不理解分层封装和对等层通信的概念
- 为什么
ping通了一台服务器,但它的网页就是打不开?——应用层健康和网络层可达是两回事
本章的目标,就是通过"一次完整的网络请求"这条主线,把网络协议栈的每一层串起来:
- 应用层:DNS 怎么把域名解析成 IP?HTTP 请求怎么写?
- 传输层:TCP 和 UDP 的区别?端口号的作用?
- 网络层:IP 路由、MAC 地址、网关——数据包怎么跳到目的地?
- 分层设计:为什么网络要分层?每一层分别做什么?
- 请求全过程:从 URL 输入到页面渲染,每一层的数据封装和解封装
- 问题排查:当网络出问题时,怎么在每一层定位根因?
带着这六个问题,我们从一次诡异的网络故障开始,走完一次网络请求的完整旅程。
# 02.浏览器输入链接
# 2.1 思考几个问题
先思考几个问题
- 在浏览器中输入了一个链接,为什么会打开网页,然后看到图片,文字,这些东西是哪里来的?
- 在浏览器中输入非http类型链接,为何有的可以打开,有的打不开,对链接有何要求?
- 为什么打开购物网站后,本来是打开我的收藏,但是去重定向到了首页登陆页面?
然后简述一下问题
- 第一个问题:打开网页,会去请求服务器数据,然后返回response数据后,会渲染到网页上进行展示。
- 第二个问题:浏览器输入链接,必须要求访问的链接scheme是受支持的,也就是支持的协议。否则未知的协议链接肯定打不开。
- 第三个问题:针对购物网站,会有身份校验(token)的环节,如果过期了则会重定向到登陆页面进行登陆。
# 2.2 网络访问流程
大概流程如下所示
- 域名解析
- TCP的三次握手
- 建立TCP连接后发起HTTP请求
- 服务器响应HTTP请求
- 浏览器解析html代码
- 同时请求html代码中的资源(如js、css、图片等)
- 最后浏览器对页面进行渲染并呈现给用户
# 2.3 会发生哪些过程
主要会发生以下这些过程:域名解析、建立HTTP连接、发送HTTP请求、数据传输、渲染网页、断开HTTP连接。而这些离不开网络7层,下面打工充先来说一下网络分层的设计!
# 03.网络分层设计
# 3.1 思考分层问题
工作中,我们也时常会听到这些术语,比如三层交换机、七层规则等等。网络分层的概念,可谓深入人心。
可是你有没有想过,网络为什么要分层呢?难道是非分不可吗?
回答这个问题之前,我们先做个有趣的假设:这会儿是在网络诞生的前夜,什么 IP 协议、TCP 协议都还不存在,而你是网络的缔造者,面临设计网络这个伟大的任务。面对这么好的机会,你会选择做怎样的设计呢?
# 3.2 看分层的案例
说到分层,我们先从我们平时使用框架开发一个程序来说,我们往往会按照每一层做不同的事情的原则将系统分为 三层(复杂的系统分层可能会更多):
- Repository(数据库操作)
- Service(业务操作)
- Controller(前后端数据交互)
复杂的系统需要分层,因为每一层需要专注于一类事情。我们网络分层的原因也是一样,每一层只专注于做一类事情。
# 3.3 设计网络思路
第一种思路:应用程序包办一切。
程序把应用层的数据,按某种编码转化为二进制数据,然后程序去操控网卡,把二进制数据发送到网络上。
这期间,通信的连接方式、传输的可靠性、速度和效率的保证等等,都需要这个程序去实现。然后下次开发另外一个应用的时候,就把上面这些活,再干一遍。
第二种思路:应用程序、操作系统、网络设备等环节各自分工。
应用程序只负责实现应用层的业务逻辑,操作系统负责连接的建立、处理网络拥塞和丢包乱序、优化网络读写速度等等,然后把数据交给网卡,后者和交换机等设备做好联动,负责二进制数据在物理线路上的传送和接收。
# 3.4 方案设计考量
第一种大包大揽的方式,实现难度太大、耦合度太高,怎么看都是一个"反面典型"。所以,应该选择第二种,也就是分层的方式去实现。
其实这个思路,跟编程的思想是类似的。在编程中,需要把一些逻辑抽象为函数或者对象,以实现更好的解耦和复用。
在网络世界里也是如此,每一层干好自己的分内事,那么所有的层次配合起来工作的时候,就显得有条不紊了。
分层设计的核心原则:
1. 每层只使用下层提供的服务
应用层不关心数据是TCP还是UDP传输的
传输层不关心数据是通过以太网还是WiFi发送的
2. 每层只向上层提供服务
通过清晰的接口(如Socket API)暴露能力
隐藏内部实现细节
3. 层与层之间通过协议通信
同一层的两个端点(如两台机器的传输层)遵循相同的协议
这就是"对等层通信"的概念
4. 数据通过逐层封装/解封装传递
发送方逐层添加头部
接收方逐层剥离头部
2
3
4
5
6
7
8
9
10
11
12
13
14
15
疑惑:分层是好,但层次太多不也会增加开销吗?
答疑:确实如此。每增加一层就增加一份头部开销。以太网帧最小46字节数据,但如果应用层只发1字节数据,加上TCP头(20B)+IP头(20B)+以太网帧头尾(18B)=59字节,协议开销占98.3%。这就是为什么TCP有Nagle算法来合并小包。但分层带来的可维护性、可扩展性和标准化价值远超这点开销。
# 3.5 网络分层优势
为什么计算机网络要分层呢?分层的优势在哪里?我们再来较为系统的说一说:
- 各层之间相互独立:各层之间相互独立,各层之间不需要关心其他层是如何实现的,只需要知道自己如何调用下层提供好的功能就可以了(可以简单理解为接口调用)。「这个和我们对开发时系统进行分层是一个道理。」
- 提高了整体灵活性:每一层都可以使用最适合的技术来实现,你只需要保证你提供的功能以及暴露的接口的规则没有改变就行了。「这个和我们平时开发系统的时候要求的高内聚、低耦合的原则也是可以对应上的。」
- 大问题化小:分层可以将复杂的网络间题分解为许多比较小的、界线比较清晰简单的小问题来处理和解决。这样使得复杂的计算机网络系统变得易于设计,实现和标准化。 「这个和我们平时开发的时候,一般会将系统功能分解,然后将复杂的问题分解为容易理解的更小的问题是相对应的,这些较小的问题具有更好的边界(目标和接口)定义。」
说到计算机网络分层,我想到了计算机世界非常非常有名的一句话,这里分享一下:
计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决,计算机整个体系从上到下都是按照严格的层次结构设计的。
# 3.6 OSI真的很难学
网络模型一般是指OSI(Open System Interconnection开放系统互连)七层参考模型
| OSI中的层 | 功能 | TCP/IP协议族 |
|---|---|---|
| 7 应用层 | 文件传输,电子邮件,文件服务,虚拟终端 | TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet 等等 |
| 6 表示层 | 数据格式化,代码转换,数据加密 | 没有协议 |
| 5 会话层 | 解除或建立与别的接点的联系 | 没有协议 |
| 4 传输层 | 提供端对端的接口 | TCP,UDP |
| 3 网络层 | 为数据包选择路由 | IP,ICMP,OSPF,EIGRP,IGMP |
| 2 数据链路层 | 传输有地址的帧以及错误检测功能 | SLIP,CSLIP,PPP,MTU |
| 1 物理层 | 以二进制数据形式在物理媒体上传输数据 | ISO2110,IEEE802,IEEE802.2 |
# 3.7 TCP/IP-4层模型
这是目前被广泛采用的一种模型,我们可以将 TCP / IP 模型看作是 OSI 7层模型的精简版本,由以下4层组成:
- 应用层
- 传输层
- 网络层
- 网络接口层
应用层协议:
- HTTP 协议(超文本传输协议,网页浏览常用的协议)
- DHCP 协议(动态主机配置)
- DNS 系统原理(域名系统)
- FTP 协议(文件传输协议)
- Telnet协议(远程登陆协议)
- 电子邮件协议等(SMTP、POP3、IMAP)
传输层协议:
- TCP 协议
- 报文段结构
- 可靠数据传输
- 流量控制
- 拥塞控制
- UDP 协议
- 报文段结构
- RDT(可靠数据传输协议)
网络层协议:
- IP 协议(TCP/IP 协议的基础,分为 IPv4 和 IPv6)
- ARP 协议(地址解析协议,用于解析 IP 地址和 MAC 地址之间的映射)
- ICMP 协议(控制报文协议,用于发送控制消息)
- NAT 协议(网络地址转换协议)
- RIP 协议、OSPF 协议、BGP 协议(路由选择协议)
网络接口层:
- 差错检测技术
- 多路访问协议(信道复用技术)
- CSMA/CD 协议
- MAC 协议
- 以太网技术
# 04.应用层设计流程
# 4.1 搞懂约定协议
在计算机网络中,有许多约定的网络协议用于实现不同层级的通信和数据交换。
# 4.2 HTTP传输协议
知道了目标地址,浏览器就开始打包它的请求。
对于普通的浏览请求,往往会使用HTTP协议;但是对于购物的请求,往往需要进行加密传输,因而会使用HTTPS协议。无论是什么协议,里面都会写明"你要买什么和买多少"。
为何输入非http链接打不开?因为这是一种传输协议。
# 4.3 域名解析IP
浏览器只知道名字是"http://www.baidu.com",但是不知道具体的地点,所以不知道应该如何访问。于是,它打开地址簿去查找。
可以使用一般的地址簿协议DNS去查找,还可以使用另一种更加精准的地址簿查找协议HTTPDNS。
无论用哪一种方法查找,最终都会得到这个地址:108.114.138.24,这也就是所说的域名解析。这个是IP地址,是互联网世界的"门牌号"。
# 4.4 DNS域名系统
域名系统(DNS,Domain Name System)将人类可读的域名 (例如,http://www.baidu.com) 转换为机器可读的 IP 地址 (例如,220.181.38.148)。」 我们可以将其理解为专为互联网设计的电话薄。
IP 地址是一个网卡再网络世界中的通讯地址,我们可以把它理解为我们现实世界中的家庭地址。
# 4.5 数据处理转化
表示层负责处理数据的表示和转换,以确保不同系统之间的数据能够正确解释和交换。表示层的主要功能包括:
- 数据格式化:表示层负责将应用层传递下来的数据进行格式化,以便在网络中传输。这包括数据的编码、压缩、加密等操作,以确保数据的可靠传输和安全性。
- 数据加密与解密:表示层可以对数据进行加密,以保护数据的机密性。在接收端,表示层负责对加密的数据进行解密,以还原原始数据。
- 数据压缩与解压缩:表示层可以对数据进行压缩,以减少数据在网络中的传输量,提高传输效率。在接收端,表示层负责对压缩的数据进行解压缩,以还原原始数据。
- 数据格式转换:表示层可以将数据从一种格式转换为另一种格式,以便不同系统之间的数据交换。例如,将数据从一种编码方式转换为另一种编码方式,或将数据从一种数据结构转换为另一种数据结构。
- 数据描述与标记:表示层可以为数据添加描述和标记,以便接收端能够正确解释和处理数据。这包括定义数据的结构、类型、顺序等信息,以确保数据的正确解析和使用。
表示层的主要目标是提供一种独立于具体应用的数据表示方式,使得不同系统之间能够进行可靠的数据交换和解释。它为上层的应用层提供了一个统一的接口,隐藏了底层网络的细节,使得应用程序能够以一种统一的方式处理数据。
# 05.传输层设计流程
# 5.1 传输协议设计
应用层到传输层,DNS、HTTP、HTTPS 所在的层我们称为应用层。经过应用层封装后,浏览器会将应用层的包交给下一层去完成,通过 socket 编程来实现。
# 5.2 TCP和UDP协议
传输层有两种协议,一种是无连接的协议UDP,一种是面向连接的协议TCP。
对于有支付来讲,往往使用 TCP 协议。所谓的面向连接就是,TCP 会保证这个包能够到达目的地。如果不能到达,就会重新发送,直至到达。
TCP 协议里面会有两个端口,一个是浏览器监听的端口,一个是电商的服务器监听的端口。操作系统往往通过端口来判断,它得到的包应该给哪个进程。
(图示:TCP/UDP传输层协议的端口号与数据分发流程)
# 5.3 为何要端口号
网络协议使用端口号来标识和区分不同的应用程序或服务。以下是一些原因:
- 多应用程序通信:在计算机上同时运行多个应用程序时,每个应用程序都需要与网络进行通信。通过使用端口号,可以将传入的数据包路由到正确的应用程序,确保不同应用程序之间的通信不会混淆或冲突。
- 端到端通信:端口号使得网络通信成为端到端的,即从发送方的应用程序到接收方的应用程序。通过指定源端口和目标端口,数据包可以准确地传递到目标应用程序,而不会被其他应用程序接收。
- 标准化和互操作性:端口号的使用是为了标准化网络通信。通过约定一些常用的端口号,不同的应用程序和设备可以遵循相同的规则,实现互操作性。例如,HTTP使用端口号80,SMTP使用端口号25。
- 安全性和访问控制:端口号也用于实现安全性和访问控制。通过限制特定端口的访问权限,可以控制哪些应用程序可以接收传入的连接或数据包。这有助于防止未经授权的访问和网络攻击。
- 网络服务识别:通过查看目标端口号,可以识别正在运行的服务或应用程序。这对于网络管理、故障排除和监控非常有用,因为可以确定特定端口上的服务是否正常运行。
# 06.网络层设计流程
# 6.1 MAC层设计
MAC地址的作用是什么?为什么有了IP地址还需要MAC地址?
MAC(Media Access Control)地址是网卡的物理地址,长度为48位(6个字节),通常表示为12个十六进制数,如 00:1A:2B:3C:4D:5E。
IP地址解决的是"目标在哪里"的问题,而MAC地址解决的是"下一跳交给谁"的问题。数据包在网络中传输时,IP地址始终不变(标识最终目的地),但MAC地址每经过一个路由器就会改变(标识当前这一跳的下一个设备)。
数据包从 A → 路由器R1 → 路由器R2 → B 的过程中:
IP头:源IP=A, 目标IP=B (始终不变)
MAC头变化:
A→R1: 源MAC=A的MAC, 目标MAC=R1的MAC
R1→R2: 源MAC=R1的MAC, 目标MAC=R2的MAC
R2→B: 源MAC=R2的MAC, 目标MAC=B的MAC
2
3
4
5
6
7
当操作系统知道下一跳的IP地址后,需要通过 ARP协议(Address Resolution Protocol)获取对应的MAC地址。ARP的工作过程:发送方在局域网内广播"谁的IP是xxx?",目标设备回复自己的MAC地址。为了减少广播,系统会维护一个ARP缓存表。
# 6.2 网关
网关是什么?为什么需要网关?
网关(Gateway)是局域网与外部网络之间的"出入口",类似于一个国家的海关。操作系统在启动时,通过DHCP协议获取IP地址和默认网关的IP地址(通常是 192.168.1.1 这样的地址)。
当操作系统判断目标IP地址不在本地局域网内时,就需要将数据包发给网关,由网关负责转发。判断的方法是:用目标IP和本机子网掩码做AND运算,如果结果与本机网络号相同,则是本地通信;否则需要经过网关。
本机IP: 192.168.1.100
子网掩码: 255.255.255.0
目标IP: 10.20.30.40
192.168.1.100 AND 255.255.255.0 = 192.168.1.0 (本机网络号)
10.20.30.40 AND 255.255.255.0 = 10.20.30.0 (目标网络号)
两者不同 → 不在同一局域网 → 发给网关处理
2
3
4
5
6
7
8
网关收到数据包后,会根据路由表判断下一步应该转发到哪里。网关通常就是一个路由器。
# 6.3 路由协议转发
路由器是如何知道去往目标IP应该走哪条路的?
路由器通过路由表来决定数据包的转发方向。路由表中记录了"到某个网络应该走哪个接口、下一跳是谁"的信息。路由表的来源有两种:
- 静态路由:管理员手动配置,适用于小型、拓扑稳定的网络
- 动态路由:路由器之间运行路由协议,自动学习和更新路由信息
常用的动态路由协议:
| 协议 | 全称 | 类型 | 适用范围 | 特点 |
|---|---|---|---|---|
| OSPF | 开放最短路径优先 | 链路状态 | 单个自治系统内部 | 收敛快,基于Dijkstra算法计算最短路径 |
| RIP | 路由信息协议 | 距离向量 | 小型网络 | 简单但收敛慢,最大15跳 |
| BGP | 边界网关协议 | 路径向量 | 自治系统之间 | 互联网骨干协议,支持策略路由 |
数据包在路由器之间层层转发的过程:每到一个路由器,路由器查看IP头中的目标IP地址,查询路由表找到下一跳,用ARP获取下一跳的MAC地址,重新封装MAC头后转发。这个过程反复进行,直到数据包到达目标所在的局域网,最终通过ARP找到目标主机的MAC地址,完成最后一跳的投递。
# 07.请求流程深度剖析
# 7.1 URL解析的细节
当你在浏览器输入 https://www.example.com:443/api/users?page=1#section 后,浏览器首先解析这个URL:
URL的完整结构:
https://www.example.com:443/api/users?page=1#section
│ │ │ │ │ │
协议 主机名 端口 路径 查询参数 片段
各部分解析:
├── 协议(Scheme):https → 决定使用TLS加密
├── 主机名(Host):www.example.com → 需要DNS解析为IP
├── 端口(Port):443 → HTTPS默认端口(可省略)
├── 路径(Path):/api/users → 服务器上的资源路径
├── 查询(Query):page=1 → 传递给服务器的参数
└── 片段(Fragment):#section → 仅浏览器使用,不发送给服务器
2
3
4
5
6
7
8
9
10
11
12
浏览器在发送请求前的预处理:
- HSTS检查:如果该域名在HSTS列表中,自动将http升级为https
- URL编码:将中文、空格等特殊字符转为%XX格式
- 缓存检查:先查看本地是否有有效缓存,有则直接使用(200 from cache)
- 连接复用检查:是否有到目标服务器的可复用TCP连接
# 7.2 DNS查询深度
DNS解析不是简单的"一次查询",而是一个多级缓存+递归/迭代查询的过程:
DNS解析的完整缓存链:
1. 浏览器DNS缓存(Chrome: chrome://net-internals/#dns)
命中 → 直接使用(~0ms)
2. 操作系统DNS缓存
命中 → 直接使用(~0ms)
3. hosts文件(/etc/hosts 或 C:\Windows\System32\drivers\etc\hosts)
命中 → 直接使用(~0ms)
4. 本地DNS服务器(通常是路由器或ISP的DNS)
命中 → 返回缓存结果(~1-5ms)
5. 递归查询
本地DNS → 根DNS → 顶级域DNS → 权威DNS
未命中任何缓存时:约50-200ms
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
疑惑:为什么有些网站第一次访问很慢,后面就快了?
答疑:第一次访问时需要完整的DNS解析(可能耗时几十到几百毫秒),解析结果会被缓存在各级DNS缓存中。后续访问直接命中缓存,DNS解析时间几乎为零。
# 7.3 TCP连接建立
DNS解析得到IP地址后,浏览器发起TCP三次握手:
TCP三次握手的详细过程:
客户端 服务端
│ │
│── SYN(seq=x) ──────────→ │ 第1次:客户端发起连接请求
│ │ seq=x是客户端的初始序列号
│ │
│ ←── SYN-ACK(seq=y,ack=x+1) ── │ 第2次:服务端同意并发起自己的连接
│ │ seq=y是服务端的初始序列号
│ │ ack=x+1确认收到客户端的SYN
│ │
│── ACK(ack=y+1) ────────→ │ 第3次:客户端确认服务端的连接
│ │ 可以携带数据(TFO优化)
│ │
TCP连接建立完成(ESTABLISHED状态)
耗时:1.5 RTT(发SYN + 收SYN-ACK + 发ACK各半个RTT)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
疑惑:为什么是三次握手而不是两次或四次?
答疑:
- 两次握手不够:服务端无法确认客户端收到了自己的SYN-ACK。如果SYN-ACK丢失,服务端以为连接建立了但客户端不知道,会浪费服务端资源。
- 四次握手多余:三次已经足够双方确认彼此的接收和发送能力。
- 本质上三次握手交换了四个信息:双方各自的初始序列号(ISN),以及对对方ISN的确认。
# 7.4 TLS握手过程
如果是HTTPS,TCP连接建立后还需要TLS握手:
TLS 1.2握手(简化版):
客户端 服务端
│── ClientHello(支持的算法列表,随机数) ──→ │
│ ←── ServerHello(选择的算法,随机数) ──── │
│ ←── Certificate(服务器证书) ─────── │
│ ←── ServerKeyExchange(DH参数) ──── │
│ ←── ServerHelloDone ────────────── │
│ │
│ 验证证书合法性 │
│ 计算Pre-Master Secret │
│ │
│── ClientKeyExchange ──────────────→ │
│── ChangeCipherSpec ──────────────→ │
│── Finished(加密验证) ─────────────→ │
│ ←── ChangeCipherSpec ───────────── │
│ ←── Finished ───────────────────── │
│ │
TLS连接建立完成,后续HTTP数据都是加密的
额外耗时:2 RTT(TLS 1.2)或 1 RTT(TLS 1.3)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 7.5 HTTP请求构造
TLS握手完成后,浏览器构造HTTP请求并发送:
GET /api/users?page=1 HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)...
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Cookie: session=abc123; theme=dark
请求构造的关键点:
├── Host头部:在HTTP/1.1中必需,用于虚拟主机区分
├── Accept系列:告诉服务器客户端能接受的内容类型
├── Accept-Encoding:告诉服务器客户端支持的压缩算法
├── Cookie:携带之前服务器设置的Cookie信息
└── Connection: keep-alive:请求保持TCP连接不关闭
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 08.响应处理与渲染
# 8.1 服务器处理请求
服务器收到HTTP请求后的处理流程:
Web服务器(如Nginx)处理流程:
1. 接收TCP数据,解析HTTP请求
2. 匹配server_name(虚拟主机)
3. 匹配location(路由规则)
4. 执行对应操作:
├── 静态资源 → 直接读取文件返回
├── 反向代理 → 转发请求给后端应用服务器
└── 重定向 → 返回301/302
后端应用服务器处理流程:
1. 解析请求参数
2. 身份认证和授权
3. 执行业务逻辑(查询数据库、调用其他服务等)
4. 序列化响应数据(JSON/HTML等)
5. 返回HTTP响应
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 8.2 响应数据回传
服务器的响应沿着请求的反方向传回:
响应数据的回传路径:
服务器应用层 → 构造HTTP响应报文
↓
服务器传输层 → TCP分段 + 加上TCP头部
↓
服务器网络层 → 加上IP头部
↓
服务器数据链路层 → 加上以太网帧头和帧尾
↓
物理层 → 转为电信号/光信号发出
↓
经过路由器层层转发(每一跳替换MAC头)
↓
到达客户端所在局域网
↓
客户端物理层 → 收到信号,转为数据帧
↓
客户端数据链路层 → 剥离帧头帧尾,校验FCS
↓
客户端网络层 → 剥离IP头
↓
客户端传输层 → 剥离TCP头,重组数据
↓
客户端应用层 → TLS解密 → 得到HTTP响应 → 交给浏览器
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 8.3 浏览器解析渲染
浏览器收到HTTP响应后,开始解析和渲染页面:
浏览器的主要处理流程:
1. 解析HTTP响应头
├── Content-Type → 确定如何处理响应体
├── Content-Encoding → 需要解压缩(如gzip)
├── Cache-Control → 决定缓存策略
└── Set-Cookie → 存储Cookie
2. 解压缩响应体(如果有gzip/br压缩)
3. 解析HTML,构建DOM树
4. 解析CSS,构建CSSOM树
5. 合并DOM和CSSOM,生成渲染树(Render Tree)
6. 布局(Layout):计算每个元素的位置和大小
7. 绘制(Paint):将元素绘制到屏幕上
8. 合成(Composite):将多个图层合成最终画面
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 8.4 关键渲染路径
**关键渲染路径(Critical Rendering Path)**是从收到HTML到首次屏幕绘制的最短路径:
关键渲染路径优化:
HTML ──→ DOM Tree ──→ ┐
├──→ Render Tree → Layout → Paint
CSS ──→ CSSOM Tree ──→ ┘
阻塞因素:
├── CSS是渲染阻塞资源:CSSOM未构建完成前不会渲染
├── JS是解析阻塞资源:遇到<script>会暂停DOM解析
│ 除非使用 async 或 defer 属性
└── 外部资源加载时间
优化策略:
├── CSS放在<head>中尽早加载
├── JS放在</body>前或使用defer
├── 关键CSS内联到<head>中
├── 预加载关键资源:<link rel="preload">
├── 预解析DNS:<link rel="dns-prefetch">
└── 预建立连接:<link rel="preconnect">
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 09.网络分层协议总览
# 9.1 各层协议汇总
将整个请求过程中涉及的协议按层整理:
| 层次 | 协议 | 作用 | 本次请求中的角色 |
|---|---|---|---|
| 应用层 | HTTP/HTTPS | 定义请求和响应的格式 | 承载网页内容 |
| 应用层 | DNS | 域名解析 | 将域名转为IP地址 |
| 应用层 | TLS/SSL | 加密通信 | 保证数据安全 |
| 传输层 | TCP | 可靠传输 | 保证数据完整有序到达 |
| 网络层 | IP | 路由寻址 | 数据包跨网络传输 |
| 网络层 | ICMP | 错误报告 | PMTUD、ping等辅助功能 |
| 网络层 | ARP | 地址解析 | IP地址转MAC地址 |
| 数据链路层 | Ethernet | 局域网传输 | 帧的封装和传递 |
| 物理层 | - | 信号传输 | 电/光信号的发送和接收 |
# 9.2 数据封装全景
一次HTTP请求中,数据在各层的封装过程:
应用层数据:
GET /api/users HTTP/1.1\r\nHost: example.com\r\n\r\n
TLS加密后:
[TLS记录头(5B)] [加密的HTTP数据 + MAC]
TCP封装:
[TCP头(20B)] [TLS记录]
IP封装:
[IP头(20B)] [TCP段]
以太网封装:
[以太网头(14B)] [IP包] [FCS(4B)]
实际在网线上传输的数据:
|14B|20B|20B|5B| 加密的HTTP数据 + MAC |4B|
帧头 IP头 TCP头 TLS头 FCS
一个简单的"GET /api/users"请求
经过层层封装后,额外增加了约63字节的协议开销
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 9.3 抓包工具验证
可以使用Wireshark等抓包工具验证上述整个过程:
Wireshark抓包看到的一次HTTPS请求:
1. DNS查询
No. | Time | Source | Dest | Protocol | Info
1 | 0.000 | 你的IP | DNS服务器 | DNS | Standard query A example.com
2 | 0.020 | DNS服务器 | 你的IP | DNS | Standard query response A 1.2.3.4
2. TCP三次握手
3 | 0.021 | 你的IP | 1.2.3.4 | TCP | SYN [seq=0]
4 | 0.051 | 1.2.3.4 | 你的IP | TCP | SYN-ACK [seq=0, ack=1]
5 | 0.052 | 你的IP | 1.2.3.4 | TCP | ACK [ack=1]
3. TLS握手
6 | 0.053 | 你的IP | 1.2.3.4 | TLS | Client Hello
7 | 0.083 | 1.2.3.4 | 你的IP | TLS | Server Hello, Certificate
8 | 0.084 | 你的IP | 1.2.3.4 | TLS | Client Key Exchange
9 | 0.114 | 1.2.3.4 | 你的IP | TLS | Change Cipher Spec, Finished
4. 加密的HTTP数据
10 | 0.115 | 你的IP | 1.2.3.4 | TLS | Application Data (加密的HTTP请求)
11 | 0.145 | 1.2.3.4 | 你的IP | TLS | Application Data (加密的HTTP响应)
从输入URL到收到响应,总耗时约145ms
其中:DNS 20ms + TCP握手 30ms + TLS握手 60ms + HTTP请求 30ms
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
总结:一次HTTPS请求的时间组成
总时间 ≈ DNS解析 + TCP握手 + TLS握手 + HTTP请求处理 + 数据传输
≈ 20ms + 30ms + 60ms + 20ms + 15ms
≈ 145ms(首次请求)
后续请求(连接复用):
≈ 0ms(DNS缓存) + 0ms(连接复用) + 0ms(TLS复用) + 20ms + 15ms
≈ 35ms
这就是为什么Keep-Alive和连接复用如此重要
2
3
4
5
6
7
8
9
# 参考博客
字节一面:"为什么网络要分层?每一层的职责、包含哪些协议?"
https://zhuanlan.zhihu.com/p/598835439
掌握网络架构核心!了解为什么要分层
https://cloud.tencent.com/developer/article/2301115
# 10.思考题与作业
# 10.1 基础思考题目
网络请求完整链路:从输入
https://www.example.com/api/data到页面渲染完成,请按顺序列出所有涉及的协议和步骤,并标注哪些步骤是阻塞的(必须等它完成),哪些可以并行。DNS 解析的缓存层次:浏览器DNS缓存、操作系统DNS缓存、hosts文件、本地DNS服务器、根/顶级/权威DNS——在极端情况下(所有缓存都未命中),一次DNS查询需要多少次网络请求?如果查询的是
www.example.com.cn,解析路径有什么不同?TCP 三次握手的序列号:客户端初始序列号为 1000,服务端初始序列号为 5000。画出三次握手每个包的 seq 和 ack 值。如果第三次握手的 ACK 包丢失了,会发生什么?服务端会一直等下去吗?
MTU 与分片:以太网 MTU 为 1500 字节,IP 头 20 字节,TCP 头 20 字节。如果应用层一次发送 3000 字节的数据,TCP 层会怎样处理?如果网络路径上有一个设备的 MTU 只有 1400 字节,又该怎么办?(提示:PMTUD)
HTTP 状态码场景题:以下场景分别返回什么 HTTP 状态码?
- 用户未登录访问个人中心
- 请求的资源被永久移到了新 URL
- CDN 请求的资源未修改
- API 缺少必填参数
- 服务器内部发生未捕获的异常
# 10.2 进阶思考题目
1.1 节复盘:小周的诡异网络故障。如果运营同事反馈的是"页面可以打开,但图片加载特别慢",你首先怀疑哪个环节?如果反馈的是"页面偶尔打不开,报
ERR_CONNECTION_TIMED_OUT",又该怀疑哪个环节?请画出"网络故障排查决策树"——根据不同的故障现象,定位到对应的网络层。TCP vs UDP 的选型:为什么视频通话用 UDP(或QUIC)而不是 TCP?如果用 TCP 传输视频流,出现丢包时会发生什么?从"实时性"和"可靠性"的权衡来分析。
HTTPS 的性能开销:一次 HTTPS 请求相比 HTTP 多花了多少时间?假设 RTT=50ms,从 TCP 开始到收到第一个字节,HTTP 需要 1.5 RTT(三次握手),HTTPS(TLS 1.2)需要多少?TLS 1.3 呢?如果把 Session Ticket 和 0-RTT 也算上,最优情况下呢?
CDN 回源问题:CDN 节点没有缓存时,需要回源站获取资源。如果源站在北京,CDN 节点在广州,用户也在广州。用户在广州请求一个 CDN 未缓存的资源,数据是怎么走的?延迟主要花在哪些环节?
Chrome DevTools 实战分析:打开 Chrome DevTools 的 Network 面板,访问一个复杂的网站(如淘宝首页),分析每个请求的时间线(Queueing、DNS、TCP、TLS、TTFB、Content Download)。哪个环节占据了最多时间?如果 TTFP 很高,应该从哪个方向优化?
# 10.3 动手实践作业
作业一(必做):用 curl 和 dig 实测一次网络请求的各个环节耗时。
# 1. 查看 DNS 解析耗时
dig www.baidu.com | grep "Query time"
# 2. 查看 HTTP 请求各环节耗时
curl -w "
DNS解析: %{time_namelookup}s
TCP连接: %{time_connect}s
TLS握手: %{time_appconnect}s
首字节: %{time_starttransfer}s
总耗时: %{time_total}s
" -o /dev/null -s https://www.baidu.com
# 3. 对比不同目标服务器的耗时差异
# 看本地和远程服务器的请求时间分布
# 回答:哪个环节差异最大?为什么?
2
3
4
5
6
7
8
9
10
11
12
13
14
15
作业二(选做):用 Wireshark 抓包分析一次完整的 HTTPS 请求。
# 1. 启动 Wireshark,选择网络接口
# 2. 设置过滤条件:tcp.port == 443
# 3. 用浏览器访问 https://www.baidu.com
# 4. 找到以下数据包并截图标记:
# - TCP 三次握手(SYN, SYN-ACK, ACK)
# - TLS ClientHello 和 ServerHello
# - TLS Certificate(服务器证书)
# - Application Data(加密的 HTTP 请求和响应)
# 5. 统计从第一个 SYN 到收到第一个 Application Data 的总时间
2
3
4
5
6
7
8
9
作业三(选做):用 Chrome DevTools 分析关键渲染路径。
# 1. 打开 Chrome DevTools → Performance 面板
# 2. 录制一次页面加载过程
# 3. 关注以下指标:
# - First Contentful Paint (FCP)
# - Largest Contentful Paint (LCP)
# - TTFB(首字节时间)
# - DOM Content Loaded
# - Load 事件
# 4. 找到加载最慢的资源,分析为什么慢
2
3
4
5
6
7
8
9
作业四(架构思考):画出你当前负责的服务的"网络请求全链路图"。
- 从"用户请求"到"服务返回响应",每一层经过哪些网络设备和服务?
- 标注每一个环节的协议栈(应用层、传输层、网络层、链路层)
- 标注每个环节的预估延迟
- 如果你要优化这个链路的 P95 延迟,你会从哪个环节入手?为什么?