
学习一个问题,一定要回到实践,所以下面的就以一个时间场景来描述网络基础,同时它也是一个经典的面试题。很多面试官在面试面试员基础的时候以它起手,就可以随着问题去不停的追问,可以说是覆盖了一整个 WEB 前端领域。
输入网址按下回车到界面渲染的整个过程发生了什么

- DNS 解析:DNS 协议
- TCP 协议 三次握手
- HTTP 协议 以及 SSL协议
- 服务器处理,发送资源
- 接收响应
- 解析资源
- 渲染界面
- 连接断开: TCP 协议
上面写的各种协议就是我需要学习重点,下面一次展开。
DNS协议
万恶的根源
面试官:"浏览器从输入URL到显示页面发生了什么?" 我:"DNS解析域名xxxxxxx"
bash
nslookup www.baidu.com
返回如下结果,DNS 解析出两个 IP 地址,这个是基于 CND 分发系统的两个IP 地址,这个地址才是我访问的实际地址。

DNS的概念
DNS = Domain Name System(域名系统)
将人类能够理解的域名解析为计算机能够理解的 IP 地址。
DNS解析域名的过程
这个过程也叫做 DNS 的查询过程,这个过程是一个迭代和递归的过程。
- 递归查询体现在和没一个阶段的服务器来回的过程中
- 迭代查询体现在才本地 DNS 服务器到根服务器在到 TTL 服务器(顶级域名服务器),最后到权威服务器的没一个服务器提升的过程中。
补充一点,在本地 DNS 服务器之前还有两个也可以进行 DNS的 解析。
- 应用程序缓存
- 本地 hosts
www.bilibili.com/video/BV1F5...
解析过程基本可以说是分成两种情况. 第一次解析: 本地DNS服务器寻找未果,就是根服务器找,根服务器就一千多个自然是没有的,然后它会叫你去.conDNS服务器上面去寻找,未果,它叫你去二级域名服务器去找. 然后真的找到了,就返回到本地DNS服务器缓冲下来. 同时也返回给浏览器. 第二次解析: 直接查本地DNS服务器是否存在,如果存在就直接返回.

大型应用会采用动态解析 IP 地址的方式
如果进行的

不同用户虽然输入的域名是一样的,但是可能由于使用的网络的不同,地区的不同,它最后访问到的 IP 地址可能都是不一样的。
这样做的意义是什么
1. 提升访问速度:就近接入最优节点
- 运营商分流 :通过识别用户所属运营商(如中国移动、中国电信、中国联通),动态返回对应运营商线路的 IP 地址。
- 示例 :北京联通的用户访问
www.bilibili.com
,DNS 返回北京联通的服务器 IP(如123.123.123.123
),避免跨运营商传输的延迟。
- 示例 :北京联通的用户访问
- 地理位置优化:根据用户所在城市(如北京、上海、广州、深圳等),返回距离最近的服务器 IP,减少网络跳数和传输时间。
2. 增强系统稳定性:故障自动切换
- 多节点冗余 :每个城市部署多个服务器 IP(如图中广州对应多个 IP),当某个节点故障时,DNS 可快速切换至备用节点。
- 示例:广州电信的服务器 IP 宕机,用户请求会被动态解析至深圳或成都的电信节点,避免服务中断。
- 负载均衡:根据服务器实时负载(如 CPU、带宽占用率),动态分配用户到压力较小的节点,防止单点过载。
3. 优化资源利用率:灵活调度流量
- 动态调整带宽:在高峰期(如电商大促、热门活动),临时扩容特定地区的服务器资源,并通过 DNS 动态引导用户至新增节点。
- 成本控制:优先将流量导向成本较低的机房或带宽资源,结合业务需求平衡性能与成本。
5. 提升容灾能力:应对突发风险
- 抗 DDoS 攻击:攻击者难以集中攻击单一 IP,DNS 可快速将用户切换到未受影响的节点。
- 区域性故障隔离:若某城市发生网络故障(如自然灾害),动态解析可将该区域用户流量导向其他城市,缩小影响范围。
6. 支持全球化与本地化服务
- 智能区分国内外用户:海外用户访问时返回国际 CDN 节点,国内用户返回本地节点,兼顾访问速度与合规要求。
- 多语言/内容适配:结合用户地理位置,动态返回适配语言或本地化内容的服务器 IP。
总结
以 www.bilibili.com
为例的动态解析架构,本质是通过 智能 DNS(如 DNSPod、AWS Route 53) 或 CDN 边缘节点调度 实现以下目标:
- 更快:就近接入,降低延迟。
- 更稳:故障切换,负载均衡。
- 更灵活:资源弹性伸缩,流量精细调度。
和前端强相关的内容
DNS优化
html
<link rel="dns-prefetch" href="//cdn.example.com">
<link rel="dns-prefetch" href="https://api.thirdparty.com">
当前浏览器解析到这一行的是,会提前把 link标签上面 对应的href地址提前进行解析。带来的帮助如下:
- DNS 解析和HTML 解析可以并行进行
- 动态DNS 场景(CDN多线路)需要频繁的解析,预加载可以优先缓存最优的 IP。
DNS 和 CDN 的关系
谈论他们的关系,本质是在谈论他们之间的协作关系。
- DNS 负责精准的域名解析与动态路由,是CDN智能调度的"导航系统";
- CDN 通过分布式缓存与负载均衡,将DNS的解析结果转化为实际性能提升。 两者的结合不仅加速了内容传输,还增强了网络稳定性与安全性,成为企业级Web服务的标配
一个负责引路,一个负责载物。或者是导航员和快递站。DNS 负责找到里用户最近的快递站,快递站负责准备好快递。
HTTP 协议
超文本传输_协议_(Hypertext Transfer Protocol,HTTP)是一个简单的请求-响应_协议。
浏览器负责解析 HTTP 报文,然后调用操作系统的网络模块进行工作。
HTTP协议可以等同于合同,大家按照合同办事就好,这个大家指的是浏览器和服务端,换句话来说就是它们之间的通信规则。
它本身是没有什么业务逻辑的,只存在通信规则的逻辑,所以我们作为一个 WEB 前端的开发而言,平时接触的 fetch 和 Axios 都是对于 HTTP 协议的使用。
所以我们学习的重点放在如下地方:
- 业务开发的高频问题
- 跨域(CORS)
- 缓存优化(强缓存和协商缓存)
- 上传/下载进度
- 鉴权(JWT、 Cookie)
- 性能提升
- HTTP/2和 HTTP/3 带来的特性
- 调试和后端协作技巧
- 接口调式(看得懂 Network 面试,请求方法、状态码、请求头/响应头,快速定位问题)
- 和后端协作(method、参数格式、返回格式,扩展的知识点RESTfull和)
推视频课程: www.bilibili.com/video/BV1rt... 推介书籍:《图解 HTTP 》
HTTP协议包含的内容
- 请求报文
http
GET /api/data HTTP/1.1 ← 起始行
Host: example.com ← 头部
User-Agent: Chrome
Accept: application/json ← 空行分隔头部和主体
← 主体(此请求没有主体)
- 第一行:请求方法(GET)、资源路径(/index.html)、协议版本(HTTP/1.1)。
- 后续行:请求头(描述浏览器信息、可接受的内容类型等)。
- 响应报文
http
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1234
<!DOCTYPE html>
<html>...</html>
- 第一行:协议版本、状态码(200)、状态描述(OK)。
- 后续行:响应头(内容类型、长度等)。
- 空行后是响应体(实际返回的 HTML 内容)。
实际的场景 network显示如下:
请求头如下:
http
GET /yoran-secret/observer-detail.html?src=/life/xxxxx.md HTTP/2
Host: wyc7758775.github.io
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:141.0) Gecko/20100101 Firefox/141.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br, zstd
Sec-GPC: 1
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
If-Modified-Since: Sun, 24 Aug 2025 12:30:13 GMT
If-None-Match: W/"68ab05d5-4660"
Priority: u=0, i
响应头如下:
http
HTTP/2 304
date: Sun, 24 Aug 2025 15:02:39 GMT
via: 1.1 varnish
cache-control: max-age=600
etag: W/"68ab05d5-4660"
expires: Sun, 24 Aug 2025 15:12:39 GMT
x-served-by: cache-qpg1224-QPG
x-cache: MISS
x-cache-hits: 0
x-timer: S1756047759.443579,VS0,VE242
vary: Accept-Encoding
x-fastly-request-id: f28eeca139135be912285ed1f25113ba29ddc43a
X-Firefox-Spdy: h2
想要在 network 面板中查看 HTTP/2 的只能在火狐中查看,谷歌浏览器看不到。具体请看:stackoverflow.com/questions/4...
业务开发高频问题
跨域(CORS)
JSONP、服务器反向代理(ng配置和 vite 配置proxy)、服务端配置白名单
-
JSONP: 已经不在使用 -
Vite 的 proxy 配置:
js
server: {
proxy:{
"/api": {
target: "http://xxx.xxx.xx.x:8080",
changeOrigin: true, // 允许跨域
secure: false, //忽略安全证书
rewrite: (path) => path.replace(/^\/api/, '')
},
},
}
当我需要请求的地址为http://xxx.xxx.xx.x:8080/user/login
则我们在vue里使用axios发起请求为 axios.get('/api/user/login')
。
- 生产环境 NG 配置
生产环境使用 Nginx 反向代理:
部署时通过 Nginx 配置反向代理,将前端静态资源(如 https://your-domain.com
)的请求转发到跨域接口(如 https://api.other-domain.com
)。
nginx
server {
listen 443 ssl; # 监听 HTTPS 端口
server_name api.example.com; # 实际域名
# SSL 证书配置(替换为实际证书路径)
ssl_certificate /path/to/fullchain.pem; # 证书文件
ssl_certificate_key /path/to/privkey.pem; # 私钥文件
# 强制 HTTP 跳转 HTTPS(可选)
if ($scheme != "https") {
return 301 https://$host$request_uri;
}
# 反向代理规则(同上)
location /api {
proxy_pass http://localhost:8080/api;
proxy_set_header Host $host;
# ...其他头信息
}
}
缓存优化(强缓存和协商缓存)
具体看[[HTTP 缓存机制]]
上传/下载进度
浏览器对于这部分的实现有两种方式,fetch
和XHR
。
XHR
作为传统的网络实现,自带了progress
事件实现监听下载进度,自带upload
属性用于监听上传进度。
js
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://example.com/large-file.zip');
// 监听下载进度
xhr.addEventListener('progress', (e) => {
if (e.lengthComputable) {
const percent = (e.loaded / e.total) * 100;
console.log(`下载进度:${percent.toFixed(1)}%`);
} else {
console.log('总大小未知,无法计算百分比进度');
}
});
// 监听上传进度
const upload = xhr.upload
upload.addEventListener('progress', (e) => {
if (e.lengthComputable) {
const percent = (e.loaded / e.total) * 100;
console.log(`上传进度:${percent.toFixed(1)}%`);
} else {
console.log('总大小未知,无法计算百分比进度');
}
});
xhr.onload = () => {
console.log('下载完成');
};
xhr.send();
Fetch
API 的不提供,需要ReadableStream
和 请求头配置Content-Length
来获取上传的进度,比较复杂,这里不展开说,我也没记住, 核心就是 fetch 需要配置其他的 API 来实现 xhr 中对应的功能,包括中断请求。
js
const file = ... // 你要上传的文件
let uploaded = 0;
const total = file.size;
const stream = new ReadableStream({
async start(controller) {
const reader = file.stream().getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
controller.enqueue(value);
uploaded += value.length;
// 这里可以更新进度条
console.log(已上传: ${uploaded} / ${total});
}
controller.close();
}
});
fetch('/upload', {
method: 'POST',
body: stream,
headers: {
'Content-Type': 'application/octet-stream',
'Content-Length': total
}
});
鉴权(JWT、 Cookie)
HTTP 协议是无状态的。
无状态的概念就是服务器不会通过 HTTP 协议来判断你是谁,不知道你是谁,自然就不会联系上下文,你不知道你知道问过什么?因为服务器要应对的不单单是你一个人。
举个例子🌰:
- 有状态:你和朋友聊天,朋友记得你之前说过的话(比如:"你上次说要去旅游,去了吗?")。
- 无状态:你每次给客服打电话,客服每次接电话都像第一次见你(比如你第一次说"我要订餐",第二次说"我要付款",客服不会自动关联这两次请求)。
设你用浏览器访问一个网站:
- 第一次请求:你登录网站,服务器返回"登录成功"。
- 第二次请求:你点击"查看购物车",服务器不知道你是谁,因为它没记住你上次登录过!
结果:你每次操作(比如查看购物车、下单),服务器都像第一次见到你,需要你反复证明自己的身份。
- 简单高效:服务器不用费心思记每个用户的状态,能轻松处理海量请求。
- 适合早期网页:早期的网页只是展示静态内容(比如新闻),不需要记住用户。
所以问题就来了,我们实际业务中,确实是需求知道用户是谁的。那么就用以下的其他手段来解决这个问题了(扩展)
方法 1:Cookie
- 原理:服务器在第一次返回响应时,通过
Set-Cookie
头告诉浏览器:"存一个身份证(Cookie)"。 - 后续请求:浏览器每次自动带上这个 Cookie,服务器就知道你是谁了。
- 例子:登录后,服务器给你一个 Cookie,之后你每次访问页面都自动带着它,服务器就知道你是已登录用户。
方法 2:Session
- 原理:服务器在内存或数据库中存一个用户状态(比如 Session ID),通过 Cookie 把 Session ID 传给浏览器。
- 例子:你登录后,服务器生成一个 Session ID 存起来,并通过 Cookie 给你,后续请求用这个 ID 找到对应的 Session 数据。
方法 3:Token(如 JWT)
- 原理:服务器生成一个加密的 Token(令牌),里面直接包含用户信息,浏览器每次请求带着这个 Token。
- 例子:登录后服务器返回一个 Token,后续请求在
Authorization: Bearer <token>
头中带上它,服务器解密后就知道你是谁。
但是这样不是违背初衷了吗?直接给 HTTP 设计为有状态的不就可以了吗?如果面试官问:"HTTP 无状态是缺点吗?为什么不直接设计成有状态?"
可以回答 : "HTTP 的无状态设计是权衡后的结果。早期为了简单高效,它不需要维护状态,适合传输静态资源。后来为了满足动态交互需求,通过 Cookie 等机制在应用层扩展了状态管理,而不是修改协议本身。这种分层设计既保持了 HTTP 的轻量,又让开发者能灵活应对业务需求。"
Cookie 和 JWT 的区别:
- Cookie 的局限性:依赖同源策略,跨域(如前端
app.example.com
调用后端api.example.com
)需额外配置 CORS,且 Cookie 无法直接用于移动端(无浏览器环境)。 - JWT 的优势:作为请求头(如
Authorization: Bearer <token>
)的一部分,天然支持跨域;移动端可直接存储 Token 并随请求发送,无需依赖 Cookie。
JWT 或 Cookie 的使用场景高度依赖后端的架构设计、技术选型和业务需求。前端的选择往往是"被动适配"。
提升性能和体验
- HTTP/0.9
- 仅支持Get请求
- 仅支持HTML格式资源
- HTTP/1.0
- 增加POST和HEAD请求方式
- 支持多种数据格式的请求和访问
- 支持cache缓存功能
- 新增状态码、多字符集支持、内容编码等
- 早起HTTP/1.0 不支持Keep-alive长连接,只支持串行连接
- 后期HTTP/1.0增加Connection:keep-alive字段(非标准字段),开始支持长连接
- HTTP/1.1
- 增加持久连接(默认开启 Connection: keep-alive)
- 增加管道机制(支持多个请求同时发送)
- 增加PUT/PATCH/OPTION/DELETE等请求方式
- 增加Host字段(指定服务器域名)->案例:搜素百度 查看network
- 增加100状态码 (Continue),支持貝发送头信息
- 扩展身份认证机制(配合 SSL 实现数据的安全传输)
- 支持传送内容的一部分和文件断点续传
- 新增了24个错误状态码
- HTTP/2.0
- 新增双工通道(客户端同时发起多个请求,服务端同时处理多个请求)
- 服务端推送(服务器会把客户端需要的资源一起推送到客户端,合适加载静态资源)
- 头信息压缩机制(每次请求都会带上所有信息发给服务端)(HTTP协议不带状态)
- 二进制协议(头信息和数据体使用二进制进行压缩)
- 多工(新增会先发送已处理好的部分,再响应其他请求,最后再处理没有处理好的部分)
- HTTP/3(2018年草案,2020年正式发布)
- 底层协议变革:
- 基于QUIC 协议(UDP 实现),避免 TCP 的队头阻塞,提升移动网络下的连接稳定性18;
- 0-RTT 建连:复用会话上下文,减少握手延迟12;
- 独立流控制:每个数据流独立处理,丢包不影响其他流68;
- 连接迁移:通过
Connection ID
标识连接,切换网络时无需重建12。
- 意义:为高延迟、高丢包场景(如 5G、物联网)提供更优性能28。
- 底层协议变革:
版本更新中有一些feature 扩展:
keep-alive
- 每次请求都需经历 TCP 三次握手 (建立连接)和 四次挥手(关闭连接)的开销(约 100ms~300ms,取决于网络延迟);
- 若网页包含多个资源(如图片、CSS、JS),每个资源都需单独建立 TCP 连接,导致"连接数爆炸"(例如一个页面加载 10 个资源,需 10 次 TCP 连接),
keep-alive
解决的就是这个。
- 双工通道(
keep-alive
的 patch)- 多个请求共享同一个 TCP 连接,服务端只需维护少量长连接(如 1 个连接处理 100 个请求),资源利用率更高。
- 10 个资源的请求通过多路复用并行传输,总耗时接近单个请求的延迟(如 100ms),加载速度提升 90%。 而
keep-alive
还是要串行传输,用时1000ms。
- QUIC
- 融合 UDP 性能和 TCP 的可靠性
这三点也是重要的性能提升,除此之外,还有配置Content-Encoding: gzip
,应用层调用压缩算法对于请求头和响应头进行压缩,降低传输数据的负载,这个也是一个很重要的性能优化。
除了上面这些,另外一块就是[[HTTP 缓存机制]],和性能优化强相关。
调试和后端协作
RESTfull 规范参考路径:www.ruanyifeng.com/blog/2014/0...
- 通用HTTP状态码
类型 | 说明 |
---|---|
1xxx | 信息,服务器收到请求,需要请求者继续执行操作 |
2xxx | 操作成功并被接受处理 |
3xxx | 重定向,需要进一步的操作以完成请求 |
4xxx | 客户端错误,请求包含语法错误或无法完成请求 |
5xxx | 服务器端的锅,服务器在处理请求的过程中发生错误 |
304 重定向
重定向的出现和[[HTTP 缓存机制]]有直接的关系. 在HTTP响应报文是默认配置的情况下,页面在已经访问过之后,再次访问的话,就有可能返回304的状态. 数据是直接从缓存中拿.
503 服务器错误
找后台去处理吧
TCP协议
TCP: Transmission Control Protocol, 传输控制协议。
和[[HTTP协议]]中说的一样,我们作为前端开发,首先要思考的是TCP协议解决的是什么问题,我们重点学习的地方是什么?
它是由操作系统实现的,浏览器只是调用 API 的工具,所以我们学习的也只是浏览器通过的net
模块,进行 NodeJs 开发的时候,这个是必须要学习的,以及TCP 的三次握手和四次挥手的过程,我们学习他们的这个过程,目的是为了更好和运维,或者后台进行沟通。这就是为什么我们需要学习接口 API 的命名规范。
TCP协议的 feature
TCP的特点是相对于UDP而言的,所以在讨论TCP 有什么特点的时候,不带上 UDP 的话是没有意义的。
TCP 主要是用于
- 特点: 面向连接(首发数据前,必须建立可靠的连接)
- 建立连接基础: 三次握手
- 应用场景: 数据必须准确无误的收发 HTTP请求、FTP文件传输、邮件收发
- 优点: 稳定、重传机制、拥赛控制机制、断开连接
- 缺点: 速度慢、效率低、占用资源、容易被攻击(三次握手 -> DOS、DDOS攻击)
TCP/IP协议组: 提供点对点的连接机制,制定了数据封装、定址、传输、路由、数据接受的标准.
一、TCP三次握手
建立TCP连接的前奏
标识位 | 数据包 |
---|---|
SYN | Synchronize Sequrnce Numbers 同步序列编号 |
ACK | Acknowledgement 确认字符 |
状态
名称 | 作用 |
---|---|
LISTEN | 监听TCP杜阿口的连接请求(我等着你发送连接请求呢) |
SYN-SENT | 在发送连接请求后等待匹配的连接请求(我发送了连接请求,我等你回复哈) |
SYN-RECEIVED | 在收到和发送一个连接请求后等待对连接请求的(我收到你的连接请求了哈, 我等你回复我) |
ESTABLSHED | 代表一个打开的连接,数据可以传送给用户(建立了连接,我和你说一下) |
TCP连接图示

- 第一次握手: 客户端向服务端发送SYN标志位(序号是J), 并进入SYN_SENT状态(等待服务端确认状态)
- 第二次握手: 服务端收到来自客户端的SYN J, 服务端会确认该数据包已收到并发送ACK标志位(序号是J + 1)和SYN标志位(序号是K), 随后进入SYN_REVD状态(请求接受并等待客户端确认状态)
- 第三次握手: 客户端进入连接建立状态后,向服务端发送ACK标志位(K+ 1) , 去人客户端已收到建立连接去人,服务器收到ACK标志后,服务端进入连接已建立状态.
一个完整的三次握手也就是请求 --- 应答 --- 再次确认
J 和 K 都是为了确立是谁在请求. SYN和ACK的结构没有什么不同,只是发送的对象不一样. 图中线条下面的可以很好的解释SYN J 是什么意思.就是 发送序号 200, 序号是SYN . J 可以是200
序号的核心作用是为字节流传输建立有序的"时间线",而"握手步骤的推进"是这一机制的自然结果
序号的定义
假如一个TCP连接的第一个报文序列号(ISN)是0,那么后续每个报文的序列号是固定的。
但是为了防止黑客使用TCP Spoof方法攻击,这个ISN要求是随机的,避免被黑客猜到。在Windows的不同版本,或者Linux的不同版本,这个随机的方法都不太一样。 RFC1948里建议的随机算法是
ini
ISN=M+F(localhost, localport,remotehost, remoteport)
其中M是一个计时器,每4毫秒加1。F是一个Hash算法,比如MD5或者SHA256。 TCP协议要使用的序列号是后面报文实际携带的序列号和ISN的相对值。
为什么不是两次、四次呢:
-
问:三次握手,为什么不能两次 答: 客户端:你在线吗?
服务器:我在,你在线吗?
为什么不是两次握手?
因为在这两次握手之后,对于客户端来说,发送和接收都没有问题,但是对于服务器,只知道接受没有问题,发送的消息客户端有没有收到,服务器是不知道的,存在失联的可能,所以需要第三次握手,也就是 客户端:我还在
此时三次握手之后,服务器知道了自己发送是没问题,因此连接是可靠的。
-
问:为什么不能四次 答: 为什么不是四次握手?
这是两种情况,第一种是把服务器的确认ACK和连接SYN分两次发送,这种在理论上是可行的,就好比
服务器:我在
服务器:你在线吗?
但是一次性能干完的事情,非分两次,太浪费资源了。
第二种情况就是客户端再次发送确认消息,也是同理,三次握手就已经保证了双方的连接是可靠的,客户端再确认也是浪费资源
二、TCP四次挥手

过程如下:
- 第一次挥手: Client 发送一个 FIN,用来关闭 Client 到 Server 的数据传送,Client 进入 FIN_WAIT_1 状态。
- 第二次挥手: Server 收到 FIN 后,发送一个 ACK 给 Client,确认序号为 ack=100+1(与 SYN 相同,一个 FIN 占用一个序号),Server 进入 CLOSE_WAIT 状态。
- 第三次挥手: Server 发送一个 FIN,用来关闭 Server 到 Client 的数据传送,Server 进入 LAST_ACK 状态。
- 第四次挥手: Client 收到 FIN 后,Client 进入 TIME_WAIT 状态,接着发送一个 ACK 给 Server,确认序号为 131+1,Server 进入 CLOSED 状态,完成四次挥手。
在TCP连接之后,就可以进行HTTP的连接传输数据了. 如果数据传输完毕之后 ,肯定是要关闭TCP连接的.而关闭TCP连接就需要经过4次挥手。
从这里过程也可以看出来,为什么TCP 需要四次挥手,因为在第三次挥手的时候,可能还存在服务器数据给客户端传递数据没有传输完毕,所以需要第三次挥手确认一些。确认了之后,两者表示已经没有任何联系了,安全断开。
关键字作用解析
- FIN: finsh 关闭连接
状态:
- FIN-WAIT_1: 等待远程TCP的连接中断请求,或先前的连接中断请求的确认
- FIN-WAIT-2: 从远程TCP等待连接中断请求
- CLOSE-WAIT: 等待从本地用户发来的连接中断请求
- LAST-ACK: 等待原来发向远程TCP的连接中断请求的确认
- TIME-WAIT: 等待足够的时间以确保远程TCP接收到连接中断请求的确认
- CLOSED: 没有任何连接状态
- **TIME-WAIT时长:**2MSL Maximum Segment Lifetime 最大报文生存时间
- MSL的值根据不同的情况而不同,一般是30秒 1分钟 2分钟
- 目的:保证客户端发送的最后一个报文能够发到服务器,一旦报文丢失,服务器会认为,自己最后一次发送的FIN+ACK包,客户端并没有收到,此时,服务器会重新发送一次FIN+ACK包,而客户端可以 在2MSL的TIME-WAIT时间内收到重新传输的FIN+ACK包,接着重新进行第四次挥手,并重启2MSL计时器。
CLOSE-WAIT和 FIN-WAIT 是一回事.只是前者是服务端等待中断请求,后者是客户端等待中断请求的状态
QA
-
为什么四次挥手中间两部不能和起来?
- Client发送第一步是:我要和你断开连接,给个话我
- Server回复第二步是:我知道你要和我断开了,还有数据没传完,你等我传输完呗,等我一下下
- Server回复第三步是:我传输完了噢,客户端你断开吧 (这一步是因为,此时客户端和服务端是出于通信状态的,所以可能还有数据在传输,所以必须存在,也必须是单独的一步)
- Client发送第四步是:知道了知道了,我赶紧断开
-
TCP连接建立后,客户端突然出现故障?
- TCP保活计时器:客户端如果出现故障,服务器每收到一次客户端的请求后都会重新复位保活计时器,时间通常是2小时,若2小时还没有收到客户端的数据,服务器就会发送一个探测报文段,以后每隔75分钟发送一次。若一连发送10个探测报文仍无反应,服务器就认为客户端出了故障,此时将关闭连接。
HTTP 缓存机制
HTTP 协议提供"缓存规则说明书",浏览器作为"工程师"按说明书搭建缓存系统。
把已请求并返回的WEB资源(HTML页面、图片、JS文件、CSS文件、数据等)复制到一个副本存储到浏览器的缓存中。
理解了这个缓存机制的过程,才能够知道为什么现在打包工具都会默认给构建之后的结果添加上hash
值。
- 缓存的好处:
- 减少网络贷款的消耗
- 降低服务器的压力
- 较少网络延迟
强制缓存
有两个 API 可以实现强制缓存,expires
和 Cache-Control
。
它是http/1
时期的属性,通过expires 响应头的属性设置,这个属性表示过期的时间,超过这个时间,资源就从服务器上面取,反之,就从本地内存当中取。
它的判断依据是和本地操作系统的时间进行匹配,所以并不可靠,存在误差以及被修改的可能。
所以http/1.1
退出了cache-control
响应头属性来解决这个问题, 值的单位为秒。它的配置方式如下:
http
res.writeHead(200, {
"Cache-Control": "max-age:5",
});
这个的含义就是,当你渲染界面之后的 5 秒内,再次刷新当前页面的话,会强制从本地从拿旧的资源,和 [[防抖函数]]的逻辑相似。
它是对于expires
的补充,两个数据可以同时配置在请求头中。当它们同时存在的时候,http/1.1
的权重比http/1.0
的高。
当我们借助一些构建工具上自动给有了变化的文件后缀名添加 HASH 值的时候,浏览器认为这个是一个新的资源,绕过了强制缓存的配置,客户端直接从服务器中拉取这个新的资源。
协商缓存
协商缓存顾名思义,就是有条件的缓存。
当同时存在 Cache-Control
和expires
这两个强缓存的属性的时候,优先判断是否满足强制缓存的条件,如果不满足再进入到协商缓存的阶段。
last-modified 实现协商缓存
最简单的方案,就是根据文件的修改时间来进行判断。需要last-modified
和Cache-Control
进行配合, 将Cache-Control
配置为no-cache
,就可以关闭强制缓存的逻辑,直接进入协商缓存,和它相关的可选值在文章的最后有列出。
mtime
是文件最后的修改时间,NodeJs 中通过fs
模块的state
方法中获取到。
js
res.setHeader("last-modified", mtime.toUTCString());
res.setHeader("Cache-Control", "no-cache");
当添加了last-modified
的字段之后,响应头会自动生成一个属性,if-modified-since
,通关它和mtime
的对比,可以判断当前是否配置了last-modified
,如下:
js
const ifModifiedSince = req.headers["if-modified-since"];
if (ifModifiedSince === mtime) {
// 缓存生效
res.statusCode = 304;
res.end();
return;
}
如此就能够实现协商缓存了。
last-modified 的不足
它能够满足绝大部分的场景。但是还是有如下的不足:
- 它只是根据了时间戳来进行判断。如果只是改变了文件名,实际内容没有任何改变的情况下,还是会进行服务器的请求拿取。不够聪明。
- 它的单位是秒,如果修改文件的速度非常的快,在几百毫秒内就完成了。那么它秒的单位就没有办法通过验证。
为了解决上面的这两点不足的地方,http1.1 之后还提供了,etag
的响应头字段来进行解决。
etag 实现协商缓存
逻辑和last-modified
基本一致。
js
const etag = require("etag");
const data = fs.readFileSync("./img4.png");
const etagContent = etag(data);
const ifNoneMatch = req.headers["if-none-match"];
if (ifNoneMatch === etagContent) {
// 缓存生效
res.statusCode = 304;
res.end();
return;
}
res.setHeader("etag", etagContent);
res.setHeader("Cache-Control", "no-cache");
res.end(data);
etag 表示的是对文件内容的解析进而生成的一个 id,只要文件内容有了改变才会进行变更。自然就能够改变last-modified
的两点的不足。它是对其的一个补充方案,而不是替代方案。
etag 依旧带来了新的问题:
- 服务器生成文件资源 Etag 需要付出额外的计算开销,如果资源尺寸比较大,数量较多且修改比较频繁的话,那么生成 Etag 的过程显然会印象服务器的性能。
- Etag 字段值的生成两种类型,一种是强验证,即更具资源内容的每一个字节来进行验证,最可靠,性能消耗也最大。相对应的就是弱验证,它使用资源内容的部分的属性值来进行生成,生成速度快,但是没有办法很高的成功率。尤其是在服务器集群场景下。
所以上面的两种协商缓存的都有不足。根据具体的场景来使用对用的缓存策略才是最好的方案。
同时存在强制缓存和协商缓存的执行策略
描述如下,先判断是否符合强制缓存调节,如果不符合了就走协商缓存的逻辑。
http
Cache-Control: max-age=3600, no-cache # 强制缓存 1 小时,但过期后需协商缓存
ETag: "abc123" # 资源指纹
Last-Modified: Wed, 21 Oct 2023 07:28:00 GMT # 最后修改时间
缓存策略
上面的只是缓存的能够使用的工具。最重要的其实是缓存策略。只有使用正确了缓存策略,才能够用户带来更好的体验。

其他相关API
Cache-Control的相关属性
- no-cache: 忽略缓存在本地的副本,强制从服务器上拿资源
- no-store: 强制缓存在任何情况下都不要保留任何副本
- 包括协商缓存的机制也会失效
- max-age=314600: 知识缓存副本的有效时长,从请求时间开始到过期时间之间的描述
- public: 表明响应可以被任何对象缓存(包括:发送请求的客户端、代理服务器等)
- private: 表明响应只能被耽搁用户缓存,不能作为共享缓存(即代理服务器不能缓存它)
no-cache 意味着不强制从直接从本地拿缓存,每次都和服务器打交道之后再来判断是否拿本地换粗资源
SSL协议
SSL(Secure Socket Layer),由于HTTP协议中传输的数据是明文,所以攻击者能够毫不费力的能够拿到开发者想要保证密文的数据,SSL就是为了解决这个问题而诞生的,它运用一定的加密解密的策略来解决攻击者获取传输过程中数据的安全。
SSL协议建立连接的过程
对称加密
对明文进行加密,让攻击者不知道它看到的数据是什么,但是同时又需要让客户端和服务端都能够解密,这样才可以进行沟通。所以对称加密就出现了,它的通过握手过程如下:
- 预主密钥的生成需要依赖于 TLS和公开的加密算法,所以双方握手的时候需要同步 TLS 和算法的版本。
- 密钥 = 加密 + 解密
根据这两点,也就是说攻击者拿到了预主密钥就可以直接加密和解密数据,那么这条数据传输通道将不再安全。
所以这个时候就需要非对称加密了。
非对称加密(PKI)
它分为两个密钥,公钥和私钥
私钥只在服务端内部,中间人可以拿到公钥对数据进行加密,但是它无法使用公钥对数据进行解密。
这样子就能够解决了中间人攻击截获数据得到的真实数据。
过程如下:

这个时候还有一个问题,就是客户端没有办法判断这个公钥是不是服务端的公钥,是不是你想要的服务端的那个公钥。中间人自己也可以生成公钥,那就意味着,中间人依旧可以截获客服端发出的信息,自己去和客户端进行沟通,获取到客户端发出的请求。
所以这个所以客户端需要判断当前的公钥是不是我们网站服务端发出的哪个公钥,这个时候就引入了一个身份认证系统,那就是证书。什么证书,它是如何解决这个问题呢?
证书,数字证书-Digital Certification
相当于一个网站的身份证信息。没一个证书上面都有一个指纹信息,这个信息是通过申请的证书添加的资料来生成的,是一个通过哈希函数解析出来的散列值,这个就是指纹信息。他是有一个CA 组织发布,分配在各个国家当中。
整个过程如下:

证书市面上只有几家是可以让浏览器正确认识的,这些公司收到大家的信任,相信它们不会乱搞。
如何结合上 TCP 三次握手的话,这个过程如下:

这个过程称之为 TLS 握手。有什么面试官也会问 SSL 加密的过程是什么?也是这个。
SSL和TLS的差别
明明标题是 SSL,为什么文章内容都是 TLS?
因为TLS 就是 SSL,等同于 SLL 3,只是改了一个名字。也可以说TLS1.0就等于SSL的3.0。 我们现在使用TLS的居多, 但是平时在说 HTTPS 的时候,这个 S 一般都称呼为 SSL 是基于习惯,约定俗成。
那为什么SSL要改名为TLS呢?完全是大公司斗争的产物。
原文地址:tim.dierks.org/2014/05/sec...
简单描述如下:
- Sll 第一个版本被网景开发出来之后,不好用,所以没有发布过。
- 市面上第一个版本是 sll 2
- 但是这玩意儿还是存在问题,微软先解决了,并且加入了自己的一些内容,只支持 IE 和另外一个。
- 网景自己也想 adress 问题,所以就弄了一个 TLS 出来,不使用微软的标准和版本来解决问题
SSL的成本和性能
- SSL证书需要购买申请,功能越强大的证书费用越高
- 根据ACM CONEXT数据显示,使用HTTPS协议会使页面的加载时间延长近50%,增加10%到20%的耗电。
- HTTPS连接缓存不如HTTP高效,流量成本高。
- HTTPS连接服务品端资源占用高很多,支持访客多的网站需要投入更大的成本。
- HTTPS协议握手阶段比较费时,对网站的响应速度有影响,影响用户体验。比较好的方式是来用分而治之,类似12306网站的主页使用HTTP协议,有关于用户信息等方面使用HTTPS.
上面的这些点都以及过时,在 TLS1.3 和 HTTP2/3出来的现在,消耗的成本可以忽略不计
http、ssl、tcp 协议的协作时序
- 客户端与服务器先通过TCP三次握手建立传输层连接。
- 在TCP连接基础上,进行SSL/TLS握手(交换证书、协商加密算法、生成会话密钥)。
- SL/TLS通道建立后,HTTP协议才开始传输应用层数据(如网页内容、API请求)
其他补充
-
\[URI\]\] 和 \[\[URL\]\] 、\[\[URN\]\] 三者的关系区别
域名存在的最大意义就是民用.让大众能够记住,而不是冷冷的一串数字。一个IP能够映射多个域名,但是一个域名只能应对一个IP地址。

如上面的链接所示:一个 URL
由以下不同的部分组成:
- 协议 :通常是 https 或 http,一种告诉浏览器或者设备如何访问资源的方法,当然还有其他的协议,如
ftp
、mailto
或者file
。 - 接下来是
://
。 - 主机名:表示 IP 地址的注册名称(域名) 或 IP 地址,用于识别连接到网络的设备的数字标识符。
- 后面是可选的端口号,前面是冒号
:
。 - 路径:可以引用文件系统路径,通常作为一个代码段使用。
- 参数 :以问号开头的可选查询参数,其中多个参数用
&
连接 - hash:用于为页面上的标题提供快速链接,如锚点链接。
2. UDP(大喇叭喊人)
广播式的协议.没有安全性. 主要用于IM系统当中.
- UDP: User Data Protocol 用户数据报协议
- 特点:面向无连接(不可靠的协议,无状态传输机制)
- 无连接信息发送机制
- 应用场景:无需确保通讯质量且要求速度快、无需确保信息完整
- 消息收发、语音通话、直播 (O0)
- 优点:安全、快速、漏洞少 (UDP flood攻击)
- 缺点:不可靠、不稳定、容易丢包
- 总结:只要目的源地址、端口号、地地址、端口号确定,则可以直接发送信息报文,但不能保证一定能收到或收到完整的数据。
以上。