前言
对于网络,也做了一下问题得整理,估计后面会写flutter文章,因为最近在学flutter,所以干脆,今晚也整理一下网络篇吧,毕竟前端得知识体系,从基本得js,css,html以及ts到react,vue还有构建工具vite,webpack,虽然也有移动端,跨端,小程序,桌面应用eletron,还有node服务。这些体系是很庞大得。这也是根据个人需求,结合自己得项目去完善自己得体系。
通过这两篇先了解一下浏览器得组成吧
javascript - 从浏览器多进程到JS单线程,JS运行机制最全面的一次梳理 - 程序生涯 - SegmentFault 思否
网络
最经典问题:从输入URL到页面加载的过程
javascript - 从输入URL到页面加载的过程?如何由一道题完善自己的前端知识体系! - 程序生涯 - SegmentFault 思否
浏览器的循环机制
浏览器的循环机制(Event Loop,事件循环)是 JavaScript 处理异步操作的核心机制,它决定了代码的执行顺序,尤其是在单线程的 JS 环境中如何协调同步任务、异步任务(如定时器、网络请求、DOM 事件等)的执行。
一、JS 单线程与异步的必要性
- 单线程:JavaScript 设计为单线程(同一时间只能执行一段代码),这是因为它需要频繁操作 DOM,多线程可能导致 DOM 冲突(比如同时修改和删除同一个元素)。
- 异步需求 :如果所有操作都是同步的,耗时操作(如网络请求、定时器)会阻塞后续代码执行,导致页面卡顿。因此,浏览器通过 Event Loop 实现了异步操作的非阻塞执行。
二、任务队列(Task Queue)
浏览器将任务分为两类,分别放入不同的队列:
- 同步任务(Synchronous Task)
- 定义:立即执行的任务,在主线程上按顺序执行,前一个任务完成后才能执行后一个。
- 示例:普通的
console.log()
、变量声明、函数调用等。
- 异步任务(Asynchronous Task)
- 定义:不立即执行的任务,会被挂起,待特定条件满足后(如定时器到期、网络请求完成),才会进入任务队列等待执行。
- 异步任务又分为两种:
- 宏任务(Macro Task) :包含 Script(整体代码)、
setTimeout
、setInterval
、setImmediate
(Node 环境)、I/O 操作(如网络请求、文件读写)、DOM 事件(如click
、load
)。 - 微任务(Micro Task) :包含
Promise.then/catch/finally
、async/await
(本质是 Promise 语法糖)、queueMicrotask
、MutationObserver
(监听 DOM 变化的 API)。
- 宏任务(Macro Task) :包含 Script(整体代码)、
三、Event Loop 执行流程
浏览器的 Event Loop 遵循以下步骤,循环往复:
- 执行同步任务:先执行主线程中的同步任务,直到同步任务栈为空。
- 执行微任务队列 :检查微任务队列,按顺序执行所有微任务 (直到微任务队列为空)。
- 微任务执行过程中产生的新微任务,会直接加入当前微任务队列末尾,并在本轮一并执行。
- 执行宏任务队列 :从宏任务队列中取出第一个任务执行(只执行一个)。
- 更新渲染:执行完一个宏任务后,浏览器可能会进行 DOM 渲染(并非每次都渲染,由浏览器优化决定)。
- 重复循环:回到步骤 2,继续检查微任务队列,循环往复。
四、关键规则总结
- 同步任务优先:同步任务全部执行完,才会处理异步任务。
- 微任务先于宏任务 :每轮 Event Loop 中,微任务队列会被一次性清空,再执行一个宏任务。
- 宏任务逐个执行:每次只从宏任务队列取一个任务执行,执行后立即处理微任务,再进行下一轮。
五、示例:代码执行顺序分析
通过一个例子理解执行流程:
javascript
console.log('1'); // 同步任务
setTimeout(() => {
console.log('2'); // 宏任务
Promise.resolve().then(() => console.log('3')); // 微任务
}, 0);
Promise.resolve().then(() => {
console.log('4'); // 微任务
setTimeout(() => console.log('5'), 0); // 宏任务
});
console.log('6'); // 同步任务
执行步骤解析:
- 执行同步任务:
console.log('1')
→console.log('6')
,输出1 6
,同步栈为空。 - 执行微任务队列:此时微任务队列有
Promise.then(console.log('4'))
。- 执行
console.log('4')
,输出4
。 - 该微任务中产生新宏任务
setTimeout(console.log('5'))
,加入宏任务队列。 - 微任务队列为空,本轮微任务执行完毕。
- 执行
- 执行宏任务队列:取第一个宏任务
setTimeout(console.log('2'))
。- 执行
console.log('2')
,输出2
。 - 该宏任务中产生新微任务
Promise.then(console.log('3'))
,加入微任务队列。 - 宏任务执行完毕,检查微任务队列。
- 执行
- 执行新的微任务队列:
Promise.then(console.log('3'))
,输出3
,微任务队列为空。 - 执行下一个宏任务:取宏任务队列中的
setTimeout(console.log('5'))
,执行输出5
。
最终输出顺序 :1 → 6 → 4 → 2 → 3 → 5
。
六、浏览器与 Node.js 事件循环的差异
虽然核心思想类似,但浏览器和 Node.js 的 Event Loop 存在细节差异:
- 宏任务执行顺序 :Node.js 中宏任务有更细的分类(如
timers
、poll
、check
等),执行顺序不同。 - 微任务优先级 :Node.js 中
process.nextTick
(非标准)优先级高于其他微任务,而浏览器中无此 API。
总结
浏览器的 Event Loop 是单线程 JS 处理异步的核心机制,核心流程可概括为: 同步任务 → 清空微任务 → 执行一个宏任务 → (可选渲染)→ 重复。 理解这一机制,能帮助我们预测代码执行顺序,避免因异步导致的逻辑错误(如在异步任务中操作未初始化的变量)。
浏览器缓存
浏览器缓存是现代 Web 性能优化的核心机制,通过存储和复用资源(如 HTML、CSS、JS、图片等),显著减少重复请求,提升页面加载速度。以下是其工作原理、分类及最佳实践的详细解析:
一、缓存类型与层次结构
浏览器缓存分为多个层次,按优先级从高到低排列:
- Service Worker 缓存
- 由 JavaScript 控制的可编程缓存,可拦截网络请求并自定义响应。
- 适用于离线应用、推送通知等高级场景。
- 内存缓存(Memory Cache)
- 临时存储在内存中的资源,读取速度极快,但页面关闭后数据丢失。
- 通常缓存高频访问的资源(如 JS、图片)。
- 磁盘缓存(Disk Cache)
- 存储在硬盘中的资源,读取速度较慢,但持久性强。
- 缓存体积较大、不常变化的资源(如 CSS、库文件)。
- Push Cache(HTTP/2)
- 服务器推送的资源缓存,仅在当前会话有效。
- 协商缓存(Validation Cache)
- 资源需与服务器验证是否有更新,再决定是否使用本地副本。
二、缓存控制的核心 HTTP 头
- 强缓存(无需请求服务器)
通过 Cache-Control
和 Expires
控制:
http
Cache-Control: max-age=3600 # 资源有效期3600秒
Cache-Control: no-cache # 强制协商缓存
Cache-Control: no-store # 禁用所有缓存
Cache-Control: private # 仅用户浏览器可缓存
Cache-Control: public # 允许中间代理缓存
Expires
是 HTTP/1.0 的遗留字段,优先级低于 Cache-Control
:
http
Expires: Wed, 21 Oct 2025 07:28:00 GMT # 资源过期时间点
- 协商缓存(需验证)
通过 ETag
和 Last-Modified
实现:
http
ETag: "33a64df551425fcc55e4d42a148795d9f25f89" # 资源内容哈希值
Last-Modified: Tue, 12 Jul 2022 19:15:56 GMT # 资源最后修改时间
客户端请求时携带验证信息:
http
If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89"
If-Modified-Since: Tue, 12 Jul 2022 19:15:56 GMT
若资源未修改,服务器返回 304 Not Modified
,否则返回新资源(状态码 200
)。
三、浏览器缓存决策流程
- 检查 Service Worker
- 若注册且命中缓存,直接返回。
- 检查强缓存
- 若
Cache-Control
或Expires
有效,直接使用本地副本。
- 若
- 执行协商缓存
- 发送
ETag
和Last-Modified
验证,根据服务器响应决定是否使用缓存。
- 发送
- 无缓存可用
- 从服务器获取资源,并更新本地缓存。
四、最佳实践
- 静态资源缓存策略
-
长期缓存(适用于不变的资源,如库文件、图片):
httpCache-Control: max-age=31536000, immutable
- 添加版本号或哈希到文件名(如
app.12345.js
),更新时修改文件名触发重新下载。
- 添加版本号或哈希到文件名(如
-
频繁更新的资源(如业务逻辑 JS):
httpCache-Control: max-age=0, must-revalidate
- HTML 缓存策略
-
通常禁用 HTML 强缓存,强制每次验证:
httpCache-Control: no-cache
- 动态内容缓存
-
使用
头指定根据请求头(如
sqlUser-Agent
)缓存不同版本:
httpVary: User-Agent
- Service Worker 预缓存
-
预缓存关键资源,提升离线体验:
javascriptself.addEventListener('install', (event) => { event.waitUntil( caches.open('my-cache-v1').then((cache) => { return cache.addAll([ '/', '/index.html', '/main.css', '/app.js' ]); }) ); });
- 避免常见陷阱
- 避免
Cache-Control: public
用于含用户敏感信息的响应。 - 确保 CDN 配置与源站一致,避免缓存不一致问题。
- 使用
no-transform
防止中间代理修改资源内容。
五、开发调试技巧
- 禁用缓存
- 开发者工具 → Network → Disable cache(调试时勾选)。
- 查看缓存状态
- 开发者工具 → Network → Size 列:
(from memory cache)
(from disk cache)
304 Not Modified
- 开发者工具 → Network → Size 列:
- 清除缓存
- 开发者工具 → Application → Clear Storage → Clear site data。
六、常见问题与解决方案
问题 | 原因 | 解决方案 |
---|---|---|
新代码未生效 | 强缓存未过期 | 使用文件名哈希或降低 max-age |
缓存验证频繁 | ETag 生成不优化 | 使用文件内容哈希作为 ETag |
CDN 缓存不一致 | CDN 配置错误 | 设置合适的 TTL 和刷新机制 |
移动端缓存异常 | 运营商劫持或中间代理问题 | 使用 HTTPS 和 Cache-Control: no-transform |
掌握浏览器缓存机制是构建高性能 Web 应用的基础,合理配置缓存策略可显著提升用户体验并降低服务器负载。
浏览器在什么情况下会造成cookie的参数丢失
浏览器中 Cookie 参数丢失的情况通常与 Cookie 的属性设置、浏览器安全策略、跨域行为或环境限制有关。以下是常见的导致 Cookie 丢失的场景及原因分析:
一、Cookie 属性设置不当
Cookie 的核心属性(如 Secure
、SameSite
、Path
、Domain
)若设置不合理,会直接导致浏览器拒绝携带或存储 Cookie。
1. Secure
属性与协议不匹配
- 规则 :标记为
Secure
的 Cookie 仅能通过 HTTPS 协议 传输,HTTP 协议下浏览器会忽略该 Cookie。 - 丢失场景 : 若服务器在 Cookie 中设置了
Secure=true
,但前端页面通过 HTTP 协议加载(或接口请求使用 HTTP),浏览器会拒绝发送该 Cookie,导致参数丢失。
2. SameSite
属性限制跨站请求
用于控制跨站请求时 Cookie 的发送策略,取值包括
-
SameSite=Strict
:仅在 同站请求 (域名完全一致)中发送 Cookie,跨站请求(如从a.com
到b.com
)完全不发送。 -
SameSite=Lax
:默认值,跨站的 GET 请求(如链接跳转)可能发送,但跨站的 POST 请求、AJAX 等通常不发送。 -
SameSite=None
:允许跨站请求发送,但 必须同时设置Secure=true
(否则浏览器会忽略该 Cookie)。 -
丢失场景 :
- 跨站 AJAX 请求(如前端
a.com
调用b.com
的接口)时,若b.com
的 Cookie 设置为SameSite=Strict
或Lax
,且请求非 GET 跳转,Cookie 会被浏览器拦截。 - 若设置
SameSite=None
但未同时设置Secure=true
,浏览器会直接忽略该 Cookie,导致存储失败。
- 跨站 AJAX 请求(如前端
3. Path
属性路径不匹配
- 规则 :
Path
定义了 Cookie 生效的路径,仅当请求路径 包含或等于Path
时,浏览器才会发送该 Cookie。 - 丢失场景 : 例如,服务器设置 Cookie 的
Path=/admin
,但前端页面或接口请求路径为/user
(不包含/admin
),浏览器会认为路径不匹配,不携带该 Cookie。
4. Domain
属性域名不匹配
-
规则 :
Domain
定义了 Cookie 生效的域名,仅当请求域名 是该域名的子域名或本身 时,Cookie 才会被发送。若未设置Domain
,默认值为当前请求的域名(不含子域名)。 -
丢失场景
- 若服务器设置
Domain=example.com
,则子域名a.example.com
可以携带该 Cookie,但父域名example.com
本身也可以;但如果设置Domain=a.example.com
,则example.com
无法携带该 Cookie。 - 若
Domain
设置为不存在的域名(如拼写错误),浏览器会拒绝存储该 Cookie。
- 若服务器设置
二、跨域请求与 CORS 配置问题
跨域场景下,若前端或服务器的 CORS(跨域资源共享)配置不当,会导致 Cookie 无法在请求中携带。
**1. 跨域请求未开启 withCredentials**
- 规则 :前端通过
XMLHttpRequest
或fetch
发起跨域请求时,默认不会携带 Cookie。需显式设置withCredentials: true
(前端),同时服务器需响应Access-Control-Allow-Credentials: true
。 - 丢失场景 : 跨域请求时,前端未设置
withCredentials: true
,或服务器未返回Access-Control-Allow-Credentials: true
,浏览器会拦截 Cookie,导致请求中不包含 Cookie 参数。
2. CORS 允许的域名与实际不符
- 规则 :服务器需通过
Access-Control-Allow-Origin
指定允许跨域的前端域名,若设置为*
(通配符),则 无法与withCredentials: true
同时使用(浏览器会拒绝此类请求)。 - 丢失场景 : 若服务器设置
Access-Control-Allow-Origin: *
,但前端开启了withCredentials: true
,浏览器会因安全限制拒绝发送 Cookie,导致丢失。
三、浏览器隐私与安全策略限制
浏览器的隐私模式、安全设置或第三方 Cookie 拦截策略,可能主动阻止 Cookie 的存储或发送。
1. 隐私模式(无痕模式)限制
- 部分浏览器在隐私模式下会限制 Cookie 的持久化存储,或关闭窗口后自动清除所有 Cookie。若在隐私模式下操作,可能出现 Cookie 临时有效但关闭后丢失,或无法存储的情况。
2. 第三方 Cookie 被拦截
-
浏览器默认可能拦截
第三方 Cookie
(即 Cookie 的
Domain
与当前页面域名不同的 Cookie,常见于广告、跟踪脚本等)。例如:
- 页面
a.com
中嵌入了b.com
的脚本,该脚本尝试设置Domain=b.com
的 Cookie,浏览器可能将其视为第三方 Cookie 并拦截,导致存储失败。 - 部分浏览器(如 Safari)对第三方 Cookie 的限制更严格,默认完全阻止,导致 Cookie 无法生效。
- 页面
3. 浏览器安全配置或插件拦截
- 用户可能手动在浏览器设置中开启了 "阻止所有 Cookie" 或 "仅阻止第三方 Cookie",导致 Cookie 无法存储。
- 浏览器扩展(如广告拦截器、隐私保护插件)可能主动删除或阻止特定 Cookie(尤其是带有跟踪标识的 Cookie)。
四、Cookie 过期或存储限制
1. 过期时间设置导致自动删除
- 若 Cookie 设置了
Expires
(绝对过期时间)或Max-Age
(相对过期时间),到期后浏览器会自动删除该 Cookie,导致参数丢失。 - 若未设置过期时间,Cookie 为 "会话 Cookie",仅在当前浏览器会话(窗口 / 标签页)存续期间有效,关闭窗口后会被清除。
- 超出存储容量限制
- 浏览器对单个域名的 Cookie 存储容量有限制(通常为 4KB 左右),且总数量限制(通常 50 个左右)。若新 Cookie 超出容量或数量限制,浏览器可能拒绝存储,或覆盖旧 Cookie 导致丢失。
五、服务器或前端代码错误
- 服务器设置 Cookie 时响应头格式错误
-
Cookie 需通过 HTTP 响应头
javascriptSet-Cookie
设置,若格式错误(如缺少必要属性、字符非法),浏览器无法解析,导致 Cookie 存储失败。例如:
Set-Cookie
中包含逗号(未转义)、空格等非法字符。- 同时设置冲突的属性(如
SameSite=None
未搭配Secure=true
)。
前端代码误操作删除 Cookie
- 前端通过
document.cookie
手动删除 Cookie(如设置Max-Age=0
或过期时间为过去),或框架 / 库的自动处理逻辑(如登录状态清除)可能误删 Cookie。
总结
Cookie 参数丢失的核心原因可归纳为:属性配置不匹配(协议、路径、域名、SameSite)、跨域 / CORS 配置错误、浏览器隐私安全限制、过期或存储超限、代码 / 格式错误。排查时需结合具体场景,检查 Cookie 属性、请求协议、跨域配置、浏览器设置及代码逻辑。
如何用cookie做单点登录
cookie常用的属性
http与https的区别?
HTTP(超文本传输协议)和 HTTPS(超文本传输安全协议)是互联网数据传输的两种主要协议,其核心区别在于 安全性 和 数据传输方式。以下是详细对比:
一、核心区别
特性 | HTTP | HTTPS |
---|---|---|
安全性 | 明文传输,数据易被窃听、篡改或冒充 | 加密传输(SSL/TLS),防窃听、篡改和冒充 |
端口号 | 默认 80 | 默认 443 |
协议结构 | 基于 TCP 直接通信 | 在 TCP 和 HTTP 之间加入 SSL/TLS 层 |
URL 前缀 | http:// |
https:// |
证书要求 | 无需证书 | 需 SSL/TLS 证书(通常需付费购买) |
搜索引擎排名 | 无优势 | Google 等优先收录 HTTPS 网站 |
浏览器提示 | 无特殊提示 | 显示 "安全锁" 图标(有效证书) |
二、加密原理(HTTPS)
HTTPS 通过 SSL/TLS 协议 实现加密,采用 对称加密 + 非对称加密 混合模式:
- 握手阶段(非对称加密) :
- 客户端向服务器发送支持的加密算法列表。
- 服务器返回 SSL/TLS 证书(含公钥)和选定的加密算法。
- 客户端验证证书有效性(通过 CA 机构),生成 会话密钥,并用服务器公钥加密后发送给服务器。
- 服务器用私钥解密,获取会话密钥。
- 数据传输阶段(对称加密) :
- 双方使用 会话密钥(对称加密)加密 / 解密后续通信数据。
优势:结合非对称加密的安全性和对称加密的高效性,确保数据传输安全。
三、证书机制
HTTPS 依赖 SSL/TLS 证书 验证服务器身份,证书由 CA(证书颁发机构) 签发:
-
证书内容:包含网站域名、公钥、颁发机构、有效期等信息。
-
验证流程
- 浏览器内置全球受信任的 CA 根证书。
- 服务器返回证书时,浏览器通过 CA 根证书验证证书签名有效性。
- 验证证书中的域名是否与访问域名一致。
-
分类
- DV(域名验证):仅验证域名所有权,安全性最低。
- OV(组织验证):验证域名所有权及组织信息(如公司)。
- EV(扩展验证) :严格验证组织合法性,浏览器地址栏显示公司名称(如
https://www.paypal.com
)。
四、性能影响
HTTPS 因加密过程会引入额外性能开销:
-
首次连接:SSL/TLS 握手约需 0.2-0.5 秒(具体取决于网络和服务器性能)。
-
持续优化
- TLS 会话复用:缓存会话密钥,减少重复握手。
- HTTP/2:与 HTTPS 强绑定,进一步优化传输效率(如二进制分帧、多路复用)。
- OCSP Stapling:预获取证书吊销信息,避免在线验证延迟。
五、应用场景
- 必须使用 HTTPS :
- 涉及用户敏感信息的网站(如登录、支付、个人信息管理)。
- 移动应用的 API 接口(防中间人攻击)。
- 需符合合规要求的网站(如 GDPR、PCI-DSS)。
- 推荐使用 HTTPS :
- 所有网站(Google 已将 HTTPS 列为排名因素之一)。
- Progressive Web App(PWA)必须使用 HTTPS。
- 涉及用户交互的页面(如评论、表单提交)。
六、迁移注意事项
从 HTTP 迁移至 HTTPS 需注意:
- 证书部署:申请证书并正确配置服务器(如 Nginx、Apache)。
- URL 重定向 :配置 HTTP 到 HTTPS 的永久重定向(
301 Moved Permanently
)。 - 混合内容处理:确保页面内所有资源(如图片、脚本)均通过 HTTPS 加载。
- Cookie 安全 :设置
Secure
属性,确保 Cookie 仅通过 HTTPS 传输。 - SEO 影响:正确配置重定向,避免搜索引擎降权。
七、总结
HTTPS 是 HTTP 的安全升级版,通过加密和身份验证解决了 HTTP 的安全缺陷。随着证书成本降低(如 Let's Encrypt 提供免费证书)和浏览器对 HTTP 的限制(如 Chrome 将 HTTP 标记为 "不安全"),HTTPS 已成为网站的标配。在实际应用中,除非是纯静态且无敏感信息的页面,否则均建议采用 HTTPS。
怎么设置取消预检
在 HTTP 跨域请求中,预检请求(OPTIONS 请求) 是浏览器的一种安全机制,用于验证服务器是否允许实际请求(尤其是 "非简单请求")。预检请求无法被 "完全取消",但可以通过合理配置服务器和优化请求,避免触发预检 或减少预检请求的频率。
一、先明确:什么情况下会触发预检请求?
浏览器对请求进行分类,只有 "非简单请求" 才会触发预检(OPTIONS):
简单请求(不触发预检)需同时满足:
-
请求方法 :仅限
GET
、HEAD
、POST
; -
请求头 :仅包含浏览器默认头部(如
Accept
、
sqlAccept-Language
、
cssContent-Language
),或
cssContent-Type
为以下之一:
application/x-www-form-urlencoded
multipart/form-data
text/plain
-
无自定义头部(如
X-Token
、Authorization
等,除非服务器明确允许)。
非简单请求(触发预检):
- 使用
PUT
、DELETE
、CONNECT
、OPTIONS
、TRACE
、PATCH
等方法; - 包含自定义头部(如
X-Requested-With
、Authorization
); Content-Type
为application/json
、application/xml
等非简单类型;- 携带
withCredentials
(跨域携带 Cookie)。
二、如何避免或减少预检请求?
1. 让请求符合 "简单请求" 条件(最直接)
-
使用
GET
/POST
/HEAD
方法; -
不使用自定义头部(或仅用浏览器默认头部);
-
POST
请求的Content-Type
设为application/x-www-form-urlencoded
或multipart/form-data
(而非application/json
)。示例:将 JSON 数据转为表单格式发送,避免
Content-Type: application/json
:javascript// 避免:Content-Type: application/json(会触发预检) fetch('https://api.example.com', { method: 'POST', headers: { 'Content-Type': 'application/json' }, // 非简单类型 body: JSON.stringify({ name: 'test' }) }); // 改为:Content-Type: application/x-www-form-urlencoded(简单请求) fetch('https://api.example.com', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ name: 'test' }) // 表单格式 });
2. 服务器配置:缓存预检结果(减少重复请求)
通过 Access-Control-Max-Age
头设置预检请求的缓存时间(单位:秒),浏览器在缓存有效期内不会重复发送预检。
配置示例(不同服务器):
-
Nginx:
nginxlocation / { add_header Access-Control-Allow-Origin "https://your-domain.com"; add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE"; add_header Access-Control-Allow-Headers "X-Token, Content-Type"; add_header Access-Control-Max-Age 86400; # 缓存1天(86400秒) # 处理预检请求(直接返回204) if ($request_method = 'OPTIONS') { return 204; } }
-
Node.js(Express):
javascriptconst express = require('express'); const app = express(); app.use((req, res, next) => { res.setHeader('Access-Control-Allow-Origin', 'https://your-domain.com'); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE'); res.setHeader('Access-Control-Allow-Headers', 'X-Token, Content-Type'); res.setHeader('Access-Control-Max-Age', '86400'); // 缓存1天 // 处理预检请求 if (req.method === 'OPTIONS') { return res.sendStatus(204); } next(); });
-
Apache(.htaccess):
apacheHeader set Access-Control-Allow-Origin "https://your-domain.com" Header set Access-Control-Allow-Methods "GET, POST, PUT, DELETE" Header set Access-Control-Allow-Headers "X-Token, Content-Type" Header set Access-Control-Max-Age 86400 # 处理预检请求 <IfModule mod_rewrite.c> RewriteEngine On RewriteCond %{REQUEST_METHOD} OPTIONS RewriteRule ^(.*)$ $1 [R=204,L] </IfModule>
3. 避免不必要的跨域场景
- 若前后端同源(域名、端口、协议一致),则不会触发跨域检查,自然无预检请求;
- 若必须跨域,可通过 "后端代理"(如 Nginx 反向代理)将跨域请求转为同源请求,绕过浏览器的跨域限制。
三、关键说明
- 预检请求是浏览器的行为,服务器无法完全禁用(除非不处理跨域请求);
- 合理配置
Access-Control-Max-Age
可大幅减少预检请求的次数; - 对于必须使用非简单请求的场景(如
PUT
方法、application/json
类型),预检请求是必要的安全验证,无法避免。
通过以上方法,可以有效减少预检请求的触发频率,优化跨域请求性能。
TCP三次握手跟四次挥手
csdn如何清除缓存
http2怎么设置优先级
在 HTTP/2 中,请求优先级(Request Prioritization)由客户端设置,用于告知服务器不同请求的相对重要性,帮助服务器优化资源传输顺序。优先级通过流(Stream)的元数据 定义,主要涉及两个核心参数:依赖关系(Stream Dependency) 和权重(Weight)。
一、优先级的核心参数
- 依赖关系(Stream Dependency) 每个请求流(Stream)可以指定依赖另一个流(通过
Stream ID
),表示 "当前流需在被依赖流之后处理"。- 若不依赖任何流,则为 "根流"(优先级最高的层级)。
- 依赖关系可形成树状结构(如 CSS 流依赖 HTML 流,图片流依赖 CSS 流)。
- 权重(Weight) 对于依赖同一父流的所有子流,通过权重(
1-256
的整数)分配相对优先级:- 权重越高,获得的带宽和处理资源越多(按比例分配)。
- 默认权重为
16
。
二、如何设置优先级?
优先级由客户端在创建流时通过 HTTP/2 帧的 PRIORITY
帧 或流的初始设置指定。不同场景下的设置方式如下:
1. 浏览器自动设置(无需手动干预)
现代浏览器(Chrome、Firefox 等)会根据资源类型自动分配优先级,无需开发者手动设置:
- 最高优先级:HTML 文档、关键 CSS(阻塞渲染的 CSS)。
- 高优先级:非阻塞 CSS、字体文件。
- 中优先级:JS 脚本、图片(视口内)。
- 低优先级:视口外图片、视频等非关键资源。
2. 手动设置(通过代码控制)
若需在自定义 HTTP/2 客户端(如 Node.js、curl 等)中手动设置优先级,需通过 HTTP/2 协议的 API 指定依赖关系和权重。
示例 1:Node.js 中使用 http2
模块设置优先级
javascript
const http2 = require('http2');
const client = http2.connect('https://example.com');
// 1. 创建根流(不依赖任何流,权重 256)
const stream1 = client.request({ ':path': '/index.html' }, {
priority: {
exclusive: false, // 是否独占父流的带宽(false 表示与其他子流共享)
dependency: 0, // 依赖的流 ID(0 表示无依赖,根流)
weight: 256 // 权重(1-256)
}
});
// 2. 创建依赖于 stream1 的子流(权重 128)
const stream2 = client.request({ ':path': '/style.css' }, {
priority: {
exclusive: false,
dependency: stream1.id, // 依赖 stream1
weight: 128
}
});
// 3. 创建依赖于 stream1 的另一个子流(权重 64,优先级低于 stream2)
const stream3 = client.request({ ':path': '/image.jpg' }, {
priority: {
exclusive: false,
dependency: stream1.id,
weight: 64
}
});
示例 2:使用 curl
测试优先级(需支持 HTTP/2
bash
# 通过 --http2-priorities 选项设置优先级(格式:流ID:依赖ID:权重:是否独占)
curl --http2-priorities "1:0:256:0, 2:1:128:0, 3:1:64:0" https://example.com
三、注意事项
- 服务器的自主性:优先级是客户端的 "建议",服务器可根据自身策略调整(如负载过高时可能忽略优先级)。
- 避免过度复杂:过多层级的依赖关系可能导致调度效率下降,通常保持简单的树状结构即可。
- 与其他特性的配合:优先级需结合多路复用发挥作用,单一连接上的多流才能体现优先级调度的价值。
通过合理设置优先级,可显著提升页面关键资源的加载速度(如优先渲染 HTML 和 CSS),优化用户体验。对于大多数前端开发者,浏览器的自动优先级分配已足够高效,无需手动干预;仅在特殊场景(如自定义客户端)才需手动配置。
http1.1method
HTTP/1.1 定义了一系列请求方法(Method),用于指定客户端对服务器资源的操作类型。这些方法明确了请求的语义,服务器根据方法类型处理请求并返回相应结果。以下是 HTTP/1.1 规范中定义的主要方法及其特点:
- GET
- 作用:请求获取指定资源(如网页、图片、接口数据等)。
- 特点:
- 仅用于 "获取" 资源,不应该对服务器资源进行修改(安全方法)。
- 多次请求同一资源,结果应一致(幂等性)。
- 请求参数通常附加在 URL 中(有长度限制,因浏览器 / 服务器而异)。
- 无请求体(body)。
- 典型场景 :浏览网页、查询数据(如
GET /api/users
)。
- POST
- 作用:向服务器提交数据,通常用于创建新资源或触发服务器处理操作(如表单提交)。
- 特点:
- 会修改服务器资源(非安全方法)。
- 多次提交可能产生不同结果(非幂等性,如重复提交订单可能创建多个记录)。
- 数据放在请求体(body)中,无长度限制(理论上),支持多种格式(如表单、JSON、文件等)。
- 典型场景 :用户注册、提交表单、上传文件(如
POST /api/users
)。
- PUT
- 作用:向指定资源位置上传 / 更新资源,通常用于 "全量更新" 资源。
- 特:
- 会修改服务器资源(非安全方法)。
- 多次请求同一资源,结果一致(幂等性,如多次 PUT 同一内容,最终资源状态相同)。
- 需要指定资源的完整路径(如
PUT /api/users/123
表示更新 ID 为 123 的用户)。
- 典型场景:更新用户信息、替换文件内容。
- DELETE
- 作用:请求服务器删除指定资源。
- 特点:
- 会删除服务器资源(非安全方法)。
- 多次请求同一资源,结果一致(幂等性,删除后再删,结果仍是 "不存在")。
- 典型场景 :删除用户、删除订单(如
DELETE /api/users/123
)。
- HEAD
- 作用:与 GET 类似,但仅请求资源的头部信息(响应头),不返回实体主体(body)。
- 特点:
- 用于获取资源的元数据(如文件大小、修改时间),而无需下载完整内容。
- 安全且幂等(同 GET)。
- 典型场景:检查文件是否存在、获取资源更新时间。
- OPTIONS
- 作用:请求服务器支持的 HTTP 方法(或跨域预检请求)。
- 特点:
- 用于 "探测" 服务器能力(如
OPTIONS /api
可返回服务器支持的方法:GET、POST 等)。 - 在跨域请求中,浏览器会自动发送 OPTIONS 作为 "预检请求",验证服务器是否允许实际请求(如跨域 POST)。
- 安全且幂等。
- 用于 "探测" 服务器能力(如
- TRACE
- 作用:回显服务器收到的请求,用于测试或诊断(如查看请求经过代理后的变化)。
- 特点:
- 服务器会将收到的请求原样返回,帮助排查请求在传输过程中的修改。
- 因安全风险(可能泄露敏感信息),多数服务器禁用此方法。
- 安全且幂等。
- CONNECT
- 作用:建立客户端与目标服务器的隧道连接(通常用于 HTTPS 代理)。
- 特点:
- 仅用于代理服务器,客户端通过
CONNECT
请求让代理建立与目标服务器的 TCP 连接,之后客户端与目标服务器直接通信(如 HTTPS 握手)。 - 非安全,非幂等。
- 仅用于代理服务器,客户端通过
补充:方法的 "安全性" 与 "幂等性"
- 安全性:指请求是否会修改服务器资源(安全方法:GET、HEAD、OPTIONS、TRACE;非安全:POST、PUT、DELETE、CONNECT)。
- 幂等性:指多次执行同一请求,结果是否一致(幂等方法:GET、HEAD、PUT、DELETE、OPTIONS、TRACE;非幂等:POST)。
这些方法构成了 HTTP/1.1 交互的基础,其中 GET、POST 是最常用的,而 PUT、DELETE 常与 RESTful API 设计结合使用。
http2请求同一域名还会受到浏览器的限制嘛
HTTP/2 几乎消除了 HTTP/1.x 时代 "同一域名并发连接数" 的限制,转而通过 "单个连接的最大并发流数" 进行轻量限制(通常 100 个)。这一限制对绝大多数场景足够宽松,因此实际开发中几乎无需担心同一域名的请求限制,也不再需要依赖 "域名分片" 优化(甚至不推荐,会破坏 HTTP/2 多路复用的优势)。
http2.0新特性
HTTP/2.0 是 HTTP 协议的重大升级(2015 年标准化),旨在解决 HTTP/1.x 的性能瓶颈(如队头阻塞、连接复用效率低等),核心目标是提升传输效率、减少延迟。其主要新特性如下:
- 二进制分帧层(Binary Framing)
HTTP/1.x 以文本格式传输数据(换行符分隔),解析复杂且易出错;而 HTTP/2 引入 "二进制分帧" 作为基础传输单位:
- 将请求 / 响应数据分割为二进制帧(Frame),每个帧包含标识(Stream ID)、类型、长度等元数据。
- 帧是 HTTP/2 传输的最小单位,可被独立处理、交错发送和重组。
- 这一设计为后续的 "多路复用""优先级" 等特性奠定了基础。
- 多路复用(Multiplexing)
彻底解决 HTTP/1.x 的 "队头阻塞" 问题:
- HTTP/1.x 中,一个 TCP 连接上只能串行处理请求(或通过多连接并行,但受浏览器连接数限制,通常≤6 个),前一个请求未完成会阻塞后续请求。
- HTTP/2 允许在单个 TCP 连接上同时传输多个请求 / 响应:不同请求的帧通过 "Stream ID" 标识,接收方会根据 ID 重组为完整的请求 / 响应。
- 优势:减少 TCP 连接建立 / 关闭的开销(握手、慢启动等),大幅提升并行请求效率。
- 头部压缩(Header Compression)
解决 HTTP/1.x 头部冗余问题:
-
HTTP/1.x 每次请求都会携带完整头部(如
User-Agent
、Cookie
等),且未压缩,重复传输大量冗余数据(占请求体积的 40%+)。 -
HTTP/2 使用
HPACK 算法
压缩头部:
- 维护 "静态字典"(预定义常见头部,如
GET
、Host
)和 "动态字典"(记录当前连接中出现过的头部,复用索引)。 - 对重复头部仅传输索引,非重复头部压缩后传输,减少 50%+ 的头部数据量。
- 维护 "静态字典"(预定义常见头部,如
- 服务器推送(Server Push)
主动向客户端推送资源,减少请求次数:
- HTTP/1.x 中,客户端需先请求主资源(如 HTML),解析后才知道需要加载依赖资源(如 CSS、JS),再发起二次请求,增加延迟。
- HTTP/2 允许服务器在客户端请求一个资源时,主动推送相关依赖资源(无需客户端显式请求)。
- 示例:客户端请求
index.html
时,服务器可主动推送style.css
和app.js
,节省一轮请求 - 响应时间。
- 请求优先级(Request Prioritization)
优化资源加载顺序:
- 客户端可对不同请求设置优先级(0-255,数值越小优先级越高),服务器根据优先级调度帧的发送顺序。
- 例如:优先加载关键 CSS/HTML,再加载图片等非关键资源,提升页面渲染速度。
- 流量控制(Flow Control)
避免接收方被过量数据淹没:
- 基于 "流"(Stream)和 "连接" 两级控制:接收方通过
WINDOW_UPDATE
帧告知发送方可传输的最大数据量(窗口大小)。 - 确保接收方(如浏览器)有足够的缓冲区处理数据,防止内存溢出或拥塞。
总结
HTTP/2.0 通过二进制分帧 重构了传输层,结合多路复用 解决了连接效率问题,通过头部压缩 减少带宽消耗,再配合服务器推送 和优先级优化加载体验,最终实现了比 HTTP/1.x 更高的性能(实测页面加载速度提升 50%+ 很常见)。
不过,HTTP/2 仍基于 TCP 协议,存在 TCP 层的队头阻塞问题(单个数据包丢失会阻塞整个连接),这一问题在 HTTP/3(基于 QUIC 协议)中得到了进一步解决。
http2.0多路复用如何实现得
HTTP/2 的多路复用(Multiplexing)是其解决 HTTP/1.x 性能瓶颈的核心特性,主要通过二进制分帧层(Binary Framing Layer) 实现,允许在单一 TCP 连接上并行传输多个请求 / 响应,彻底解决了 HTTP/1.x 中 "队头阻塞"(Head-of-Line Blocking)问题。
核心实现原理:
- 二进制分帧层(基础)
HTTP/2 不再使用 HTTP/1.x 的文本格式传输数据,而是将所有传输内容分割为二进制帧(Frame):
- 帧是 HTTP/2 传输的最小单位,每个帧大小固定(默认最大 16KB),包含头部(Frame Header)和 payload(数据)。
- 帧头部包含 流 ID(Stream ID)(用于标识所属的 "流")、帧类型(如 HEADERS 帧、DATA 帧等)、长度等元信息。
- 流(Stream):多请求的并行载体
-
流的定义:流是一组有序帧的集合,对应一个完整的请求 / 响应(如客户端向服务器发送的一个请求,或服务器返回的一个响应)。
-
唯一标识:每个流有唯一的整数 ID(客户端发起的流为奇数,服务器发起的为偶数),帧通过流 ID 关联到特定流。
-
并行性 :多个流(即多个请求 / 响应)的帧可以在同一个 TCP 连接上交错传输(无需按顺序),接收方通过帧的流 ID 重新组装成完整的请求 / 响应。
例如:在一个 TCP 连接上,客户端可以同时发送请求 A 的 HEADERS 帧、请求 B 的 DATA 帧、请求 A 的 DATA 帧,服务器通过流 ID 区分并分别处理。
- 解决队头阻塞问题
- HTTP/1.x 中,一个 TCP 连接同一时间只能处理一个请求,后续请求必须排队等待前一个完成(队头阻塞),多请求需建立多个 TCP 连接(受浏览器连接数限制,通常 6-8 个)。
- HTTP/2 中,单一 TCP 连接上的多个流并行传输,某一流的帧传输延迟(如丢包)不会阻塞其他流,仅影响该流自身的组装,大幅提升效率。
- 流的其他特性(辅助优化)
- 优先级:可以为流设置优先级(通过帧头部的 Priority 字段),服务器会根据优先级分配资源(如优先处理高优先级的流),优化用户体验(如优先加载页面关键资源)。
- 流控制:支持流量控制机制,避免接收方被发送方的大量数据淹没(类似 TCP 滑动窗口)。
- 双向性:流是双向的,客户端和服务器可在同一流上交替发送帧(如 WebSocket 式的双向通信)。
总结
HTTP/2 多路复用的核心是通过二进制分帧 将请求 / 响应拆分为独立的帧,再通过流 ID 标识归属,使多个请求 / 响应的帧能在单一 TCP 连接上交错传输,实现并行处理。这一机制减少了 TCP 连接建立的开销(如握手、慢启动),解决了队头阻塞,显著提升了网络传输效率。
tcp 的拥塞控制是怎么做的
TCP 的拥塞控制是为了避免网络因数据量过大而发生拥塞(如路由器缓存溢出、链路过载),同时最大化利用网络带宽的关键机制。其核心思路是通过动态调整发送方的发送速率(即 "拥塞窗口"),根据网络实时状态(是否发生丢包、延迟增加等)自适应调整,实现 "探测 - 适应 - 稳定" 的闭环。
TCP 拥塞控制主要通过四个核心算法协同工作:慢启动(Slow Start) 、拥塞避免(Congestion Avoidance) 、快速重传(Fast Retransmit) 、快速恢复(Fast Recovery)。下面详细说明其工作机制:
核心概念:拥塞窗口(cwnd)与慢启动阈值(ssthresh)
在了解具体算法前,需要先明确两个关键变量:
- 拥塞窗口(cwnd,Congestion Window):发送方维护的一个动态值,代表当前允许发送的未确认数据的最大字节数(或报文段数量),直接决定发送速率。
- 慢启动阈值(ssthresh,Slow Start Threshold) :一个临界值,用于划分 "慢启动" 和 "拥塞避免" 两个阶段。当
cwnd < ssthresh
时,使用慢启动;当cwnd ≥ ssthresh
时,使用拥塞避免。
1. 慢启动(Slow Start):逐步探测网络承载能力
当 TCP 连接刚建立(或从拥塞中恢复后),发送方对网络状况一无所知,此时需要通过 "慢启动" 逐步探测网络能承受的最大数据量。
- 规则 : 初始时,
cwnd
设为较小值(通常为 1~2 个 MSS,MSS 是最大报文段长度),ssthresh
设为一个较大值(如 65535 字节)。 每收到一个对端的确认报文(ACK),cwnd
就翻倍 (指数增长)。 例如:初始cwnd=1
→ 收到 ACK 后cwnd=2
→ 再收到 ACK 后cwnd=4
→ 以此类推。 - 目的:避免一开始就发送大量数据导致网络拥塞,通过指数增长快速接近网络的承载上限。
- 终止条件 :当
cwnd
增长到等于ssthresh
时,慢启动结束,进入 "拥塞避免" 阶段。
2. 拥塞避免(Congestion Avoidance):平稳增长,避免拥塞
当 cwnd
达到 ssthresh
后,说明已接近网络的承载上限,此时需要放缓增长速度,避免触发拥塞。
- 规则 : 每经过一个往返时间(RTT,数据发送到收到 ACK 的时间),
cwnd
只增加 1 个 MSS (线性增长)。 例如:cwnd=10
→ 经过一个 RTT 后cwnd=11
→ 再经过一个 RTT 后cwnd=12
。 - 目的:通过线性增长逐步试探网络的最大容量,在保证网络稳定的前提下最大化吞吐量。
3. 拥塞发生时的处理:根据 "丢包信号" 调整策略
当网络发生拥塞时,发送方会通过两种信号感知:超时重传(Timeout) 或 收到 3 个重复 ACK(Duplicate ACK)。两种信号对应不同的处理逻辑。
情况 1:超时重传(严重拥塞信号)
如果发送方超过重传超时时间(RTO)仍未收到 ACK,说明报文可能因网络严重拥塞而丢失(缓存溢出被丢弃)。
- 处理规则:
- 将
ssthresh
设为当前cwnd
的一半(ssthresh = cwnd / 2
),减少后续发送量。 - 将
cwnd
重置为初始值(1 个 MSS),重新进入 "慢启动" 阶段,彻底降低发送速率。
- 将
情况 2:收到 3 个重复 ACK(轻微拥塞信号)
如果发送方收到 3 个相同的 ACK(重复确认),说明有报文段丢失,但网络仍在正常传输(否则不会收到 ACK),属于轻微拥塞。
- 快速重传(Fast Retransmit): 不等待超时,立即重传丢失的报文段(无需等到 RTO),减少重传延迟。
- 快速恢复(Fast Recovery) :
- 将
ssthresh
设为当前cwnd
的一半(ssthresh = cwnd / 2
)。 - 将
cwnd
设为ssthresh + 3
(加 3 是因为 3 个重复 ACK 说明已有 3 个报文段被接收,网络仍有一定容量)。 - 之后每收到一个重复 ACK,
cwnd
增加 1;当收到新的 ACK(确认重传的报文段)后,cwnd
恢复为ssthresh
,进入 "拥塞避免" 阶段。
- 将
- 目的:在轻微拥塞时,避免重新进入慢启动(减少吞吐量波动),快速恢复发送速率。
整体流程示例
假设初始 cwnd=1
,ssthresh=16
:
- 慢启动阶段 :
cwnd
从 1→2→4→8→16(指数增长),当cwnd=16
时达到ssthresh
,进入拥塞避免。 - 拥塞避免阶段 :
cwnd
线性增长(16→17→18→...)。 - 若发生超时重传 :
ssthresh=18/2=9
,cwnd=1
,重新慢启动(1→2→4→8→9),再进入拥塞避免(9→10→...)。 - 若收到 3 个重复 ACK :
ssthresh=18/2=9
,cwnd=9+3=12
,快速恢复后进入拥塞避免(12→13→...)。
总结
TCP 拥塞控制通过 "慢启动" 快速探测网络容量,"拥塞避免" 平稳增长发送速率,再通过 "快速重传" 和 "快速恢复" 处理不同程度的拥塞,动态平衡 "吞吐量" 和 "网络稳定性"。这一机制是 TCP 能在复杂网络环境中可靠传输数据的核心保障。
udp 和 tcp 区别
TCP(Transmission Control Protocol,传输控制协议)和 UDP(User Datagram Protocol,用户数据报协议)是 TCP/IP 协议栈中最核心的两个传输层协议,它们在设计目标、工作方式和适用场景上有显著区别,核心差异体现在可靠性、连接性、传输效率等方面。
1. 连接性:是否需要建立连接
- TCP :面向连接 通信前必须通过 "三次握手" 建立连接,通信结束后通过 "四次挥手" 释放连接,就像打电话前先拨号确认对方接听,挂电话前说 "再见"。 例:TCP 连接建立后,发送方和接收方会维护一个 "连接状态"(如序号、窗口大小等),确保双方状态同步。
- UDP :无连接 通信前无需建立连接,发送方直接封装数据并发送,接收方收到后直接处理,就像发快递 ------ 直接填地址寄出,不需要提前通知收件人。 例:UDP 发送数据时,不关心接收方是否在线、是否准备好接收,发送后也不保留连接状态。
2. 可靠性:是否保证数据完整送达
-
TCP :可靠传输 通过一系列机制确保数据不丢失、不重复、按序到达:
- 确认应答(ACK):接收方收到数据后必须返回确认,发送方未收到确认则重传。
- 超时重传:发送方若超过一定时间未收到 ACK,自动重传数据。
- 序号与确认号:给每个数据包编号,接收方按序号重组,发现缺失则要求重传。
- 校验和:检测数据传输中是否损坏,损坏则丢弃并要求重传。
例:用 TCP 传输文件,即使中途有数据包丢失,最终也能通过重传保证文件完整。
-
UDP :不可靠传输 仅提供最基本的校验和(检测数据损坏),若损坏则直接丢弃,不保证送达、不保证顺序、不重传。 例:UDP 发送的数据包可能丢失、重复或乱序到达,接收方收到后也无需确认。
3. 传输方式:字节流 vs 数据报
- TCP :字节流传输 数据被视为连续的字节流(无边界),发送方和接收方通过 "滑动窗口" 控制数据流量,接收方会将收到的字节按顺序拼接成完整数据。 例:发送 "ABCDE",TCP 可能分两次发送 "A" 和 "BCDE",接收方会自动拼接为 "ABCDE"。
- UDP :数据报传输 数据以 "数据报" 为单位独立传输(有明确边界),每个数据报包含完整的源 / 目的端口和数据,接收方会按数据报原样处理,不会合并或拆分。 例:发送 "ABCDE",若用 UDP 分两次发送,则接收方会收到两个独立的数据报(如 "AB" 和 "CDE"),不会自动拼接。
4. 效率与开销:速度与资源占用
- TCP :效率低,开销大 为实现可靠性,TCP 需要维护连接状态、处理确认 / 重传、拥塞控制等,头部开销较大(20~60 字节),传输速度较慢。
- UDP :效率高,开销小 无连接状态维护,无确认 / 重传机制,头部仅 8 字节(固定),传输速度快,资源占用低。
5. 拥塞控制与流量控制
- TCP :有拥塞控制和流量控制
- 流量控制:通过 "滑动窗口" 限制发送方速率,避免接收方缓冲区溢出(接收方告诉发送方可接收的最大数据量)。
- 拥塞控制:通过慢启动、拥塞避免等算法(见前文)动态调整发送速率,避免网络拥塞。
- UDP :无拥塞控制和流量控制 发送方会以恒定速率发送数据,不考虑网络状态和接收方能力,可能导致网络拥塞或接收方缓冲区溢出。
6. 适用场景
- TCP :适合对可靠性要求高、可接受延迟 的场景,如:
- 文件传输(FTP、HTTP/HTTPS):需保证文件完整。
- 邮件发送(SMTP):不能丢失邮件内容。
- 登录交互(SSH、Telnet):指令需准确执行。
- UDP :适合对实时性要求高、可容忍少量数据丢失 的场景,如:
- 实时音视频(视频通话、直播):少量丢包不影响整体体验,延迟更重要。
- 游戏数据(多人在线游戏):位置、操作等实时数据,延迟过大会影响体验。
- 广播 / 组播(如 DNS 查询):简单请求 - 响应,无需复杂控制。
总结对比表
特性 | TCP(传输控制协议) | UDP(用户数据报协议) |
---|---|---|
连接性 | 面向连接(三次握手建立连接) | 无连接(直接发送) |
可靠性 | 可靠(不丢失、不重复、按序到达) | 不可靠(可能丢失、乱序) |
传输单位 | 字节流(无边界) | 数据报(有边界) |
头部大小 | 20~60 字节(可变) | 8 字节(固定) |
拥塞 / 流量控制 | 有 | 无 |
速度 | 较慢 | 较快 |
适用场景 | 文件传输、HTTP、邮件等 | 音视频、游戏、DNS 等 |
简单说,TCP 像 "挂号信"(确保送达),UDP 像 "平信"(快速但不保证到)------ 选择哪种协议,取决于应用更在意 "可靠性" 还是 "实时性"。
说一下 udp 的使用场景
UDP(用户数据报协议)的设计核心是高效率、低延迟 ,但不保证可靠性,因此它的使用场景主要集中在对实时性要求高、可容忍少量数据丢失的场景中。以下是 UDP 的典型应用场景及背后的逻辑:
1. 实时音视频传输(如视频通话、直播、 VoIP)
- 核心需求:实时性优先,延迟必须低(通常要求 < 200ms),少量数据丢失不影响整体体验。
- 为什么用 UDP: 音视频数据是连续的 "流",如果某个数据包丢失,TCP 会触发重传,导致后续数据积压,产生 "卡顿" 或 "延迟累积"(比如直播中画面突然卡住,几秒后又快进播放)。而 UDP 不重传丢失的包,接收方可以通过算法(如丢包补偿、插值)填补缺失,保证画面 / 声音流畅,用户几乎感知不到少量丢包。
- 例子:Zoom、腾讯会议的实时视频、抖音直播、网络电话(Skype 早期核心协议)。
2. 在线游戏(尤其是实时竞技类游戏)
- 核心需求:低延迟传输玩家操作、位置、状态等数据,确保 "操作即时反馈"。
- 为什么用 UDP: 游戏中玩家的位置、按键操作等数据具有 "时效性",比如 1 秒前的位置数据即使重传到达,也已失去意义(玩家早已移动)。TCP 的重传机制会导致数据延迟,引发 "操作卡顿"(比如按了技能,几秒后才生效)。而 UDP 快速传输,即使少量数据包丢失,游戏客户端可以通过 "预测算法"(如根据前几帧位置预测当前位置)临时填补,保证游戏流畅。
- 例子:《英雄联盟》《王者荣耀》《CS:GO》等竞技游戏的核心数据传输。
3. DNS(域名解析)
- 核心需求:短请求 - 响应模式,追求快速解析,单次交互数据量小。
- 为什么用 UDP : DNS 查询通常是 "客户端发送一个短请求(如查询
www.baidu.com
的 IP),服务器返回一个短响应",整个过程数据量极小(通常 <512 字节),UDP 的低开销(8 字节头部)和无连接特性可以快速完成交互。即使偶尔丢包,客户端也会简单重试,效率远高于 TCP 的 "三次握手建立连接→传输→四次挥手释放连接" 的完整流程。
4. 广播与组播通信
- 核心需求:一对多或多对多的数据分发(如局域网设备发现、实时消息推送)。
- 为什么用 UDP: TCP 是 "点对点" 的连接协议,不支持广播(向局域网所有设备发送)或组播(向特定组设备发送)。而 UDP 天然支持广播和组播,适合局域网内的设备发现(如打印机自动连接、智能家居设备配对)、企业内部的实时通知推送等场景。
5. IoT(物联网)设备通信
- 核心需求:低资源消耗(算力、带宽),简单高效的数据上报或控制。
- 为什么用 UDP: 物联网设备(如传感器、智能手表、摄像头)通常硬件资源有限(CPU 弱、内存小),UDP 的无连接特性和低头部开销(8 字节)可以减少设备的计算负担和网络流量。例如,温度传感器每秒上报一次数据,即使偶尔丢失一次,后续数据仍能反映趋势,无需 TCP 的复杂可靠性机制。
6. 实时监控与远程控制
- 核心需求:低延迟传输监控画面或控制指令。
- 为什么用 UDP: 安防摄像头的实时视频流、无人机 / 机器人的远程控制指令,对延迟非常敏感(比如无人机操作指令延迟 1 秒可能导致碰撞)。UDP 的快速传输能保证指令即时生效,少量视频帧丢失不会影响监控画面的整体观察。
总结:UDP 的适用场景共性
所有 UDP 的典型场景都符合一个核心逻辑:"实时性> 绝对可靠性",即 "少量数据丢失可以接受,但延迟必须低"。相比之下,TCP 更适合 "可靠性 > 实时性" 的场景(如文件传输、邮件)。
简单说,UDP 就像 "对讲机"------ 快速传递信息,偶尔没听清可以再问;而 TCP 像 "挂号信"------ 确保送到,但过程较慢。选择哪种协议,本质是权衡 "实时性" 和 "可靠性" 的优先级。
整个 DNS 域名查询过程是怎么样子的
DNS(域名系统)的核心作用是将人类易记的域名(如www.baidu.com
)转换为计算机可识别的 IP 地址(如180.101.50.242
)。整个查询过程可以分为 "本地查询" 和 "网络查询" 两个阶段,涉及多种类型的 DNS 服务器协同工作,以下是详细步骤:
整体流程概览
当用户在浏览器输入www.baidu.com
并回车后,DNS 查询流程大致为: 本地缓存 → 本地DNS服务器 → 根域名服务器 → 顶级域名服务器 → 权威域名服务器
(每一步若找到结果,会立即返回并缓存,无需进入下一步)
详细步骤拆解
1. 检查本地缓存(最快的查询)
首先,浏览器和操作系统会优先检查 "本地缓存" 中是否有该域名对应的 IP 地址:
- 浏览器缓存:浏览器会保存近期查询过的域名 - IP 映射(缓存时间由 DNS 记录的 TTL 决定,通常几分钟到几小时)。
- 操作系统缓存 :如果浏览器缓存中没有,会查询操作系统的本地 DNS 缓存(如 Windows 的
hosts
文件或系统级缓存)。
结果:若缓存中有记录,直接返回 IP 地址,查询结束(整个过程在本地完成,耗时毫秒级)。
2. 向 "本地 DNS 服务器" 发起查询(递归查询)
如果本地缓存无记录,操作系统会向 "本地 DNS 服务器"(通常由网络服务商 ISP 提供,如电信、联通,或用户手动设置的公共 DNS 如114.114.114.114
、8.8.8.8
)发送查询请求。
- 这种查询是递归查询:本地 DNS 服务器会 "全权负责",要么返回结果,要么向其他服务器查询,直到拿到结果。
3. 本地 DNS 服务器查询(迭代查询)
本地 DNS 服务器若自身缓存中没有该域名的 IP(称为 "非权威查询"),会通过迭代查询的方式,逐级向上级 DNS 服务器查询:
Step 3.1:查询 "根域名服务器"
根域名服务器是 DNS 体系的 "顶层",全球共 13 组(编号 A~M),负责指引到 "顶级域名服务器"。
- 本地 DNS 服务器向根服务器发送查询:"请问
www.baidu.com
的 IP 是多少?" - 根服务器不直接存储具体域名的 IP,但知道 "
.com
" 这类顶级域名对应的服务器地址,因此返回:"我不知道,但你可以问.com
顶级域名服务器,它的地址是 XXX"。
Step 3.2:查询 "顶级域名服务器(TLD)"
顶级域名服务器负责管理 "顶级域名"(如.com
、.cn
、.org
)下的二级域名。
- 本地 DNS 服务器向
.com
顶级域名服务器发送查询:"请问www.baidu.com
的 IP 是多少?" - 顶级域名服务器知道 "
baidu.com
" 这个二级域名对应的 "权威域名服务器" 地址,因此返回:"我不知道,但你可以问baidu.com
的权威服务器,它的地址是 YYY"。
Step 3.3:查询 "权威域名服务器"
权威域名服务器是域名的 "所有者服务器",由企业或组织自己维护(或委托给服务商),直接存储该域名对应的 IP 地址(及其他 DNS 记录,如 MX、CNAME 等)。
- 本地 DNS 服务器向
baidu.com
的权威服务器发送查询:"请问www.baidu.com
的 IP 是多少?" - 权威服务器查询自身记录,返回对应的 IP 地址(如
180.101.50.242
)。
4. 结果返回与缓存
- 本地 DNS 服务器收到权威服务器返回的 IP 后,会将该记录缓存(按 TTL 时间保存),以便下次查询时直接使用。
- 本地 DNS 服务器将 IP 地址返回给用户的操作系统,操作系统再返回给浏览器。
5. 浏览器发起连接
浏览器拿到 IP 地址后,通过 TCP 协议与该 IP 对应的服务器建立连接(如三次握手),最终获取网页内容并展示。
关键概念补充
- 递归查询 vs 迭代查询:
- 递归:"你帮我查到结果为止"(用户设备 → 本地 DNS 服务器)。
- 迭代:"我不知道,但你可以去问 XXX"(本地 DNS 服务器 → 根 / TLD / 权威服务器)。
- TTL(生存时间):DNS 记录的缓存时长(单位秒),过期后会重新查询,保证 IP 地址变更时能及时更新。
- CNAME 记录 :如果
www.baidu.com
是一个别名(如指向baidu.com
),权威服务器会先返回 CNAME,本地 DNS 需要再查一次别名对应的 IP。
示例:查询www.baidu.com
的简化流程
- 浏览器 / 系统缓存:无记录。
- 本地 DNS(如
114.114.114.114
):无缓存,开始迭代查询。 - 根服务器 → 指引到
.com
TLD 服务器。 .com
TLD 服务器 → 指引到baidu.com
的权威服务器。baidu.com
权威服务器 → 返回www.baidu.com
的 IP(如180.101.50.242
)。- 本地 DNS 缓存该 IP,返回给浏览器,浏览器连接服务器。
整个过程看似复杂,但由于缓存机制和服务器优化(如根服务器、TLD 服务器的分布式部署),实际耗时通常在几十到几百毫秒,用户几乎无感知。
get与post的区别
在 HTTP 协议里,GET 和 POST 是两种常用的请求方法,二者存在诸多不同,下面为你详细介绍:
应用场景
- GET:主要用于获取服务器上的资源。比如,当你在浏览器中输入网址查看网页,或者通过搜索引擎进行搜索时,使用的就是 GET 请求。
- POST:常用于向服务器提交数据,像表单提交、文件上传等操作都会用到 POST 请求。
参数传递方式
- GET :请求参数会附加在 URL 的查询字符串里,例如
https://example.com/api?name=John&age=30
。 - POST:请求参数包含在请求体中,不会直接显示在 URL 里。
安全性
- GET:由于参数直接暴露在 URL 中,所以不太适合传输敏感信息,如密码、银行卡号等。而且,GET 请求容易被浏览器缓存,也可能被记录在日志中,存在一定的安全风险。
- POST:参数位于请求体中,相对更安全一些,适合传输敏感数据。不过,即便使用 POST 请求,在传输敏感信息时,也建议结合 HTTPS 协议来保障数据的安全性。
数据长度限制
- GET:URL 的长度是有限制的,不同浏览器和服务器的限制可能有所不同,一般在 2048 字节左右,所以 GET 请求能携带的参数长度也受到相应限制。
- POST:理论上对请求体的大小没有限制,但实际应用中,服务器通常会设置一个最大限制,以防止恶意攻击或者占用过多资源。
幂等性
- GET:是幂等的请求方法,也就是说,多次执行相同的 GET 请求,得到的结果都是一样的,不会对服务器上的资源造成改变。
- POST:不具备幂等性,多次提交 POST 请求可能会导致服务器上的资源发生多次改变,比如多次创建相同的记录。
缓存机制
- GET:请求会被浏览器缓存,如果你再次访问相同的 URL,浏览器可能会直接使用缓存的结果,而不会重新向服务器发送请求。
- POST:默认情况下,POST 请求不会被缓存。
编码方式
- GET :URL 只能使用 ASCII 字符集,所以非 ASCII 字符需要进行编码,通常采用 URL 编码(比如
%E4%B8%AD%E6%96%87
表示中文)。 - POST :请求体的编码方式由
Content-Type
头指定,常见的编码方式有application/x-www-form-urlencoded
、multipart/form-data
和application/json
等。
适用场景总结
场景 | GET | POST |
---|---|---|
获取数据 | ✅ 合适 | ❌ 不太合适 |
提交数据(如表单) | ❌ 不安全,参数会暴露在 URL 中 | ✅ 合适 |
传输敏感信息 | ❌ 不安全 | ✅ 相对安全 |
幂等操作 | ✅ 多次请求结果相同 | ❌ 多次请求可能产生不同结果 |
大数据传输 | ❌ 受 URL 长度限制 | ✅ 适合大数据传输 |
示例代码
下面是使用 Python 的 requests
库分别发送 GET 和 POST 请求的示例:
python
import requests
# GET 请求示例
response_get = requests.get('https://example.com/api', params={'name': 'John', 'age': 30})
print(response_get.url) # 输出: https://example.com/api?name=John&age=30
# POST 请求示例
response_post = requests.post('https://example.com/api', data={'name': 'John', 'age': 30})
print(response_post.text) # 输出服务器响应内容
总结
在实际开发过程中,要根据具体的业务需求来选择合适的请求方法。如果是获取数据,优先考虑使用 GET 请求;如果是提交数据或者执行会对服务器资源产生修改的操作,建议使用 POST 请求。同时,要特别注意敏感数据的传输安全问题,尽量使用 HTTPS 协议来保障通信安全。
http1 的队头堵塞
HTTP/1 中的队头阻塞(Head-of-Line Blocking,简称 HoLB) 是指在一个 TCP 连接上,前面的请求未完成时,后面的所有请求都必须排队等待,从而导致后续请求被阻塞的现象。这是 HTTP/1.x 协议设计的核心性能瓶颈之一。
为什么会产生队头阻塞?
HTTP/1.x 对请求的处理遵循「串行化 」原则,即使使用了 keep-alive
持久连接(复用 TCP 连接),同一连接上的请求也必须按顺序执行:
- 客户端必须等待前一个请求的响应完全接收后,才能发送下一个请求。
- 若前一个请求因网络延迟、服务器处理缓慢、丢包重传等原因被阻塞,后续所有请求都会被「卡」在队列中,无论它们本身是否准备好发送 / 处理。
举例说明
假设一个网页需要加载 5 个资源(如 CSS、JS、图片),且浏览器通过一个 TCP 连接请求这些资源:
- 客户端发送请求 1 → 等待服务器响应。
- 若请求 1 因服务器处理慢(比如需要复杂计算),耗时 3 秒才返回响应。
- 则请求 2、3、4、5 必须在队列中等待 3 秒,直到请求 1 完成后才能依次发送。
- 即使请求 2 的资源很小、服务器能瞬间处理,也必须等待请求 1 完成,这就是典型的「队头阻塞」。
队头阻塞的影响
- 页面加载延迟: 网页通常依赖多个资源(几十甚至上百个),队头阻塞会导致资源加载顺序混乱、整体耗时增加,尤其影响首屏渲染速度。
- 连接数膨胀 : 为缓解队头阻塞,浏览器会对同一域名开启多个并行 TCP 连接 (通常限制 6-8 个),试图让不同连接上的请求并行处理。但这会带来新问题:
- 多连接会重复经历 TCP 握手、慢启动等过程,增加额外开销。
- 过多连接可能触发 TCP 拥塞控制(如丢包时所有连接同时降速),反而降低整体效率。
- 资源利用率低: 即使服务器空闲,也无法处理被阻塞在队列后的请求,导致网络和服务器资源浪费。
为什么 HTTP/1 无法解决?
HTTP/1.x 基于「文本协议」传输,请求 / 响应的格式是明文且无结构化拆分,服务器必须完整接收一个请求后才能解析并处理下一个。这种设计导致无法在同一连接上交错处理多个请求,只能串行执行,从而根本上无法避免队头阻塞。
总结
HTTP/1 的队头阻塞本质是「同一 TCP 连接上的请求串行化处理」导致的连锁阻塞问题,是协议设计的固有缺陷。这一问题在 HTTP/2 中通过「二进制分帧 + 多路复用」机制得到解决(多个请求的帧可在同一连接上交错传输,互不阻塞),成为 HTTP/2 性能提升的核心原因之一。
对 http1的keep-alive 的了解。keep-alive 具体alive多久?
在 HTTP/1 中,keep-alive
是用于实现持久连接(Persistent Connections) 的机制,其核心作用是复用已建立的 TCP 连接,避免每次 HTTP 请求都重复进行 TCP 三次握手和四次挥手,从而减少网络开销、提升传输效率。
一、keep-alive
的基本原理
HTTP 协议最初是 "短连接" 模式:每个 HTTP 请求 / 响应完成后,底层的 TCP 连接会被立即关闭。这导致在加载一个包含多个资源(如图片、CSS、JS)的网页时,需要频繁创建和关闭 TCP 连接,而 TCP 握手、慢启动等过程会显著增加延迟。
keep-alive
机制通过以下方式优化:
- 客户端和服务器通过 HTTP 头协商,在一个 TCP 连接上连续发送多个 HTTP 请求(无需重复建立连接)。
- 当一段时间内没有新请求时,连接才会被关闭。
二、keep-alive
的协商方式
- HTTP/1.0 :默认不启用持久连接,需显式通过请求头
Connection: keep-alive
告知服务器 "希望复用连接";服务器若同意,会在响应头中同样返回Connection: keep-alive
。 - HTTP/1.1 :默认启用持久连接(无需显式设置
Connection: keep-alive
),除非通过Connection: close
明确关闭。
三、keep-alive
具体 "存活多久"?
keep-alive
的连接持续时间没有固定标准 ,由服务器配置 和客户端行为共同决定,核心依赖两个参数:
- 服务器端的超时时间(
timeout
)
服务器会通过响应头 Keep-Alive
中的 timeout
参数指定 "连接空闲超时时间",例如: Keep-Alive: timeout=5, max=100
timeout=5
:表示服务器在处理完一个请求后,会保持连接等待 5 秒。若 5 秒内客户端发送新请求,则复用该连接;超过 5 秒无新请求,服务器会主动关闭连接。max=100
:表示该连接最多可处理 100 个请求,即使未超时,处理完 100 个请求后也会关闭连接(避免连接长期占用资源)。
不同服务器的默认配置不同(可自定义):
- Nginx 默认
timeout=60s
(可通过keepalive_timeout
配置)。 - Apache 默认
timeout=5s
(可通过KeepAliveTimeout
配置)。
2. 客户端的超时策略
客户端(如浏览器、curl、应用程序)也会设置自己的 "连接空闲超时时间",可能比服务器的 timeout
更短:
- 例如,浏览器可能设置为 30 秒,若 30 秒内没有新请求,会主动关闭连接,无需等待服务器超时。
- 客户端也可能限制单个连接处理的最大请求数(如浏览器对同一域名的并发连接数限制,间接影响连接复用次数)。
3. 其他关闭条件
- 若服务器或客户端检测到网络异常(如丢包、断开),会立即关闭连接。
- 客户端或服务器主动发送
Connection: close
头,告知对方 "本次请求后关闭连接"。
四、注意:与 TCP keep-alive
的区别
HTTP 的 keep-alive
和 TCP 协议的 keep-alive
是完全不同的机制:
- HTTP
keep-alive
:属于应用层机制,目的是复用 TCP 连接,减少连接建立开销。 - TCP
keep-alive
:属于传输层机制,用于检测已建立的 TCP 连接是否 "存活"(如长时间无数据传输时,发送探测包确认对方是否在线),与 HTTP 复用无关。
总结
keep-alive
的 "存活时间" 由服务器的 timeout
配置(如 Nginx 默认 60 秒)和客户端的超时策略共同决定,核心是 "空闲多久后关闭连接"。其本质是通过复用 TCP 连接优化性能,但 HTTP/1 的 keep-alive
仍存在 "队头阻塞" 问题(同一连接上的请求需排队),这一缺陷在 HTTP/2 中被多路复用机制解决。
webScoket 和 http2 服务端推送的差异
WebSocket 和 HTTP/2 服务端推送(Server Push)虽然都能实现服务器向客户端主动发送数据,但它们的设计目标、通信模式、适用场景有本质区别,核心差异体现在 "双向实时通信" 与 "资源预加载优化" 的定位上。
1. 设计目标:解决的问题不同
- WebSocket 核心目标是打破 HTTP 协议 "请求 - 响应" 的单向通信限制,建立客户端与服务器之间的全双工(双向)实时通信通道。 解决的问题:HTTP 中服务器无法主动向客户端发送数据(除非客户端轮询,效率低),WebSocket 让双方可随时互相发送数据,适合实时交互场景。
- HTTP/2 服务端推送 核心目标是优化 HTTP 协议的 "资源请求效率",通过服务器主动推送客户端可能需要的关联资源,减少客户端的请求次数和延迟。 解决的问题:传统 HTTP 中,客户端请求一个主资源(如 HTML)后,需解析后再依次请求依赖资源(如 CSS、JS、图片),多次往返耗时;HTTP/2 推送让服务器在响应主请求时,主动 "预推送" 这些依赖资源,加速页面加载。
2. 通信模式:双向实时 vs 单向预推
-
WebSocket
- 连接方式 :需单独建立持久连接(握手阶段基于 HTTP,但之后使用独立的 WebSocket 协议帧),连接一旦建立,客户端和服务器可随时双向发送数据(全双工),无需等待对方请求。
- 数据类型:通常传输动态实时数据(如聊天消息、实时位置、游戏状态),数据格式灵活(文本、二进制)。
- 触发方式:服务器可主动推送数据,客户端也可主动发送数据,双方行为互不依赖。
例:聊天应用中,服务器可随时向客户端推送新消息,客户端也可随时发送消息给服务器,双方实时交互。
-
HTTP/2 服务端推送
- 连接方式 :基于 HTTP/2 现有的持久连接(复用同一 TCP 连接,利用多路复用特性),仅支持服务器向客户端单向推送,且推送行为必须依附于客户端的某个初始请求(不能无中生有地推送)。
- 数据类型:主要推送静态资源(如 CSS、JS、图片、字体),这些资源是客户端请求的主资源(如 HTML)的依赖项。
- 触发方式 :服务器只能在处理客户端的某个请求(如请求
index.html
)时,预判客户端接下来可能需要的资源(如style.css
),主动推送;若客户端没有任何请求,服务器无法主动推送。
例:客户端请求
index.html
时,服务器在返回 HTML 的同时,主动推送index.css
和main.js
,避免客户端解析 HTML 后再发起这两个请求的延迟。
3. 协议基础与生命周期
- WebSocket
- 是独立的应用层协议(RFC 6455),握手时使用 HTTP 协议(通过
Upgrade
头部升级连接),但握手成功后脱离 HTTP,使用 WebSocket 帧格式传输数据。 - 连接生命周期:一旦建立,会长期保持(除非主动关闭或超时),适合长时间的实时通信。
- 是独立的应用层协议(RFC 6455),握手时使用 HTTP 协议(通过
- HTTP/2 服务端推送
- 是 HTTP/2 协议(RFC 7540)的内置特性,完全基于 HTTP/2 的帧结构(如
PUSH_PROMISE
帧标记推送意图),属于 HTTP 协议的增强。 - 推送生命周期:依附于客户端的请求上下文,推送的资源是对该请求的 "补充",连接虽持久,但推送行为与具体请求强关联。
- 是 HTTP/2 协议(RFC 7540)的内置特性,完全基于 HTTP/2 的帧结构(如
4. 适用场景:实时交互 vs 性能优化
- WebSocket 适用场景 需实时双向交互 的场景,核心诉求是 "低延迟、高频次、双向通信":
- 即时通讯(如微信网页版、在线聊天)
- 实时数据展示(如股票行情、监控数据)
- 多人协作工具(如在线文档实时编辑)
- 在线游戏(如实时同步玩家操作)
- HTTP/2 服务端推送适用场景 需优化资源加载效率 的场景,核心诉求是 "减少请求次数、加速页面 / 应用加载":
- 网页加载(推送 HTML 依赖的 CSS、JS、图片)
- 移动端应用资源预加载(推送首页依赖的静态资源)
- API 响应时推送关联数据(如请求用户信息时,推送其常用设置数据)
5. 关键差异总结表
维度 | WebSocket | HTTP/2 服务端推送 |
---|---|---|
核心目标 | 建立双向实时通信通道 | 预推送关联资源,优化加载效率 |
通信方向 | 全双工(客户端↔服务器双向) | 单向(仅服务器→客户端) |
触发条件 | 连接建立后,双方可随时触发 | 必须依附于客户端的某个初始请求 |
传输内容 | 动态实时数据(消息、状态等) | 静态依赖资源(CSS、JS、图片等) |
协议基础 | 独立协议(基于 HTTP 握手) | HTTP/2 内置特性 |
典型场景 | 聊天、实时游戏、协作工具 | 网页加速、资源预加载 |
一句话总结
WebSocket 是 "实时对话的电话",支持双方随时互聊;HTTP/2 服务端推送是 "快递员主动送件",在你下单买了一件商品后,提前把你可能需要的配件一起送来,减少你再次下单的麻烦。两者定位不同,无法互相替代,但可结合使用(如一个应用用 WebSocket 做实时交互,同时用 HTTP/2 推送静态资源优化加载)。
websocket
WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议,它允许客户端和服务器之间进行实时数据传输,是现代 Web 实时应用(如聊天、实时通知、游戏、股票行情等)的核心技术。以下是关于 WebSocket 的详细解析:
一、核心概念与特点
- 与 HTTP 的对比
特性 | HTTP | WebSocket |
---|---|---|
通信模式 | 半双工(请求 - 响应) | 全双工(双向实时通信) |
连接特性 | 短连接(每次请求新建) | 长连接(一次握手,长期保持) |
实时性 | 低(需轮询或长轮询) | 高(消息即时推送) |
协议开销 | 高(每次请求带完整头) | 低(仅握手阶段开销大) |
端口 | 80/443 | 80(ws)/443(wss) |
-
协议握手过程
-
客户端发起升级请求: 客户端通过 HTTP 请求(带特殊头)向服务器请求升级协议:
httpGET /chat HTTP/1.1 Host: example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== Sec-WebSocket-Version: 13
-
服务器响应升级: 服务器验证请求合法后,返回 101 状态码,表示协议升级成功:
httpHTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
-
TCP 连接保持: 握手成功后,HTTP 连接升级为 WebSocket 连接,使用相同的 TCP 通道进行通信,后续数据传输不再需要 HTTP 头。
二、API 与使用示例
- 浏览器端 API
javascript
// 创建 WebSocket 实例
const ws = new WebSocket('ws://example.com/socket');
// 连接建立成功
ws.onopen = () => {
console.log('WebSocket 连接已建立');
ws.send('Hello, server!'); // 发送消息
};
// 接收消息
ws.onmessage = (event) => {
console.log('收到消息:', event.data);
};
// 连接关闭
ws.onclose = (event) => {
console.log('连接已关闭', event.code, event.reason);
};
// 错误处理
ws.onerror = (error) => {
console.error('WebSocket 错误:', error);
};
// 关闭连接
ws.close();
- 服务端实现(Node.js 示例)
使用 ws
库(需先安装:npm install ws
):
javascript
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
// 监听新连接
wss.on('connection', (ws) => {
console.log('客户端已连接');
// 接收客户端消息
ws.on('message', (message) => {
console.log('收到消息:', message);
ws.send(`服务器收到: ${message}`); // 回复消息
});
// 连接关闭
ws.on('close', () => {
console.log('客户端已断开');
});
});
三、高级特性
- 心跳机制
为保持长连接活跃(避免被中间代理断开),需定期发送心跳包:
javascript
// 客户端代码
let heartbeatInterval;
function startHeartbeat() {
heartbeatInterval = setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.send('ping'); // 发送心跳
}
}, 30000); // 每30秒发送一次
}
// 连接建立后启动心跳
ws.onopen = () => {
startHeartbeat();
};
// 关闭连接时清除心跳
ws.onclose = () => {
clearInterval(heartbeatInterval);
};
- 二进制数据支持
WebSocket 支持发送二进制数据(如图片、文件):
javascript
// 发送二进制数据(如 File 对象)
const file = document.querySelector('input[type="file"]').files[0];
ws.send(file);
// 接收二进制数据
ws.onmessage = (event) => {
if (event.data instanceof Blob) {
// 处理二进制数据
const reader = new FileReader();
reader.onload = () => {
// 使用 reader.result
};
reader.readAsArrayBuffer(event.data);
}
};
- 跨域与安全
- 同源策略 :WebSocket 默认不受同源策略限制,但服务器可通过验证
Origin
头控制允许的域名。 - 加密连接 :使用
wss://
协议(基于 TLS)替代ws://
,确保数据传输安全。
四、应用场景
- 实时通信:聊天应用(如 Slack、微信网页版)、在线客服。
- 实时数据推送:股票行情、体育比分、实时监控系统。
- 协同编辑:多人在线文档编辑(如 Google Docs)、实时协作绘图。
- 游戏:多人在线游戏(如实时对战、聊天室)。
- 实时通知:社交平台消息推送、邮件提醒。
五、与其他实时技术的对比
技术 | 实现方式 | 延迟 | 开销 | 适用场景 |
---|---|---|---|---|
WebSocket | 全双工长连接 | 极低 | 低 | 高频实时数据(如股票) |
轮询(Polling) | 定时发送 HTTP 请求 | 高 | 高 | 数据更新频率低的场景 |
长轮询(Long Polling) | 服务器保持请求直到有数据 | 中等 | 中等 | 实时性要求一般的场景 |
Server-Sent Events(SSE) | 服务器单向推送 | 低 | 低 | 仅需服务器到客户端的场景 |
六、常见问题与解决方案
- 连接断开处理 :
- 实现重连机制(如指数退避算法:
setTimeout(reconnect, Math.min(delay * 2, maxDelay))
)。 - 使用心跳包检测连接状态。
- 实现重连机制(如指数退避算法:
- 消息顺序保证 :
- 为消息添加序列号,客户端按序处理。
- 使用可靠传输协议(如 WebSocket 本身是可靠的,但需处理重连后的数据同步)。
- 安全性 :
- 使用
wss://
协议。 - 服务端验证客户端身份(如 JWT 令牌)。
- 限制消息大小,防止 DOS 攻击。
- 使用
- 跨域问题 :
- 服务端配置允许的域名(验证
Origin
头)。 - 使用代理服务器(如 Nginx)转发 WebSocket 请求。
- 服务端配置允许的域名(验证
七、服务端框架推荐
- Node.js :
ws
(轻量)、Socket.IO
(自动重连、心跳、跨浏览器兼容)、uWebSockets.js
(高性能)。 - Java:Spring WebSocket、Jetty WebSocket。
- Python :
websockets
、Django Channels。 - Go :
gorilla/websocket
。
WebSocket 凭借其低延迟、高实时性和双向通信能力,成为构建现代 Web 实时应用的首选技术。合理处理连接管理、消息协议和安全问题,可打造出高性能、稳定的实时系统
http2.0对服务端渲染有什么好处
HTTP/2.0 作为新一代 HTTP 协议,其核心特性(如多路复用、服务器推送、头部压缩等)对服务端渲染(SSR,Server-Side Rendering)的性能和用户体验有显著优化,主要好处体现在以下几个方面:
- 多路复用解决 "队头阻塞",加速资源并行加载
HTTP/1.x 中,单个 TCP 连接同一时间只能处理一个请求 / 响应,多资源加载时需建立多个连接(或排队等待),容易出现 "队头阻塞"(Head-of-Line Blocking)问题。而 SSR 页面通常需要加载大量关联资源(如 HTML 主文档、CSS、JS、图片等),资源加载效率直接影响首屏渲染速度。
HTTP/2.0 的多路复用通过二进制分帧技术,允许在单个 TCP 连接上同时传输多个请求 / 响应(每个请求被拆分为独立帧,通过流 ID 标识),无需等待前一个请求完成即可处理下一个。这对 SSR 的优势在于:
- 服务器生成的 HTML 主文档与它引用的 CSS、JS 等资源可通过同一个连接并行传输,减少连接建立和 TLS 握手的开销;
- 避免因某个资源加载缓慢(如大图片)阻塞其他关键资源(如 CSS)的传输,从而加快首屏渲染的 "关键路径"。
- 服务器推送(Server Push)减少请求延迟
SSR 的核心是服务器提前生成 HTML 并发送给客户端,但客户端解析 HTML 后仍需额外请求其引用的资源(如 <link rel="stylesheet">
或 <script>
),这会产生 "请求 - 响应" 的往返延迟。
HTTP/2.0 的服务器推送允许服务器在客户端显式请求前,主动推送关联资源。例如:
- 服务器生成 HTML 时,发现其中引用了
main.css
和app.js
,可在发送 HTML 的同时,主动将这些资源推送到客户端; - 客户端接收 HTML 时,所需的 CSS/JS 可能已缓存完成,无需再发起请求,直接用于渲染,显著减少首屏加载的总时间。
这对 SSR 尤为重要 ------SSR 本就是为了减少客户端渲染的延迟,而服务器推送进一步消除了 "资源依赖请求" 的额外开销。
- 头部压缩(HPACK)降低传输开销
HTTP 请求 / 响应的头部(Headers)在 HTTP/1.x 中以文本形式传输,且包含大量重复信息(如 User-Agent
、Cookie
、Accept
等),尤其在 SSR 场景中,多个资源请求的头部冗余会浪费带宽并增加延迟。
HTTP/2.0 采用HPACK 算法对头部进行压缩:
- 通过静态字典(预定义常见头部字段)和动态字典(记录当前连接中重复的头部),大幅减少头部的传输体积;
- 对 SSR 中多资源并行请求的场景,头部压缩可降低总数据传输量,间接提升资源加载速度,尤其在弱网环境下效果更明显。
- 连接复用减少 TCP 握手成本
HTTP/1.x 中,浏览器对同一域名的并发连接数有限制(通常 6-8 个),多资源加载时可能需要建立多个 TCP 连接,每个连接都需经历 "三次握手" 和 TLS 握手(若用 HTTPS),成本较高。
HTTP/2.0 基于单个 TCP 连接实现多路复用,所有请求 / 响应都在这一个连接上完成,避免了多连接的建立开销:
- 对 SSR 页面中包含大量图片、样式、脚本等资源的场景,减少了连接建立的时间成本;
- 降低服务器的连接管理压力,提升服务端处理 SSR 并发请求的效率。
总结
HTTP/2.0 通过多路复用消除队头阻塞、服务器推送减少请求延迟、头部压缩降低传输成本、连接复用减少握手开销等特性,直接优化了 SSR 场景中 "资源加载效率" 和 "首屏渲染速度" 这两个核心痛点。这些优化使得 SSR 页面能更快地将内容呈现给用户,进一步发挥 SSR 在首屏体验和 SEO 上的优势。
SSL/TLS实现得过程
对称加密和非对称加密的区别
对称加密和非对称加密是密码学中两种核心加密方式,它们在密钥管理、安全性、效率等方面存在显著区别,以下是具体对比:
1. 密钥使用方式
- 对称加密 加密和解密使用同一个密钥(称为 "对称密钥")。发送方用密钥加密数据,接收方必须使用相同的密钥才能解密。 例如:A 用密钥 K 加密信息发送给 B,B 必须用 K 才能解密。
- 非对称加密 加密和解密使用一对不同但关联的密钥 (公钥和私钥):
- 公钥(Public Key):可公开传播,用于加密数据或验证签名。
- 私钥(Private Key):必须保密,用于解密用公钥加密的数据,或生成签名。 例如:A 用 B 的公钥加密信息,只有 B 的私钥能解密;或 A 用自己的私钥签名,任何人可用 A 的公钥验证签名。
2. 密钥管理与分发
- 对称加密 密钥必须严格保密,且需要安全地传递给通信对方(即 "密钥分发问题")。如果密钥在传输中泄露,加密内容会被破解。 (例如:A 和 B 必须先通过安全渠道共享密钥 K,否则无法安全通信。)
- 非对称加密 公钥可公开传播,无需保密,只需确保私钥不泄露即可。解决了对称加密的密钥分发难题:接收方只需公开公钥,发送方用公钥加密,无需提前共享密钥。
3. 安全性
- 对称加密 安全性依赖于密钥的保密性和长度。密钥一旦泄露,所有加密数据都会被破解;但只要密钥安全,加密本身很难被攻破(如 AES 算法)。
- 非对称加密 安全性基于复杂的数学难题(如大整数分解、离散对数),破解难度极高。即使公钥公开,没有私钥也无法解密或伪造签名。但私钥一旦泄露,整个加密体系会失效。
4. 效率与适用场景
- 对称加密 算法简单(如 AES、DES),加密解密速度极快,适合处理大量数据(如文件加密、实时通信加密)。 缺点:密钥分发困难,不适合跨网络的陌生节点通信(如互联网用户之间)。
- 非对称加密 算法复杂(如 RSA、ECC),加密解密速度较慢,不适合大量数据加密。 优势:适合密钥交换 (如用非对称加密传递对称密钥)、数字签名 (验证身份和数据完整性)、身份认证等场景(如 HTTPS 协议中用 RSA 交换对称密钥)。
5. 典型算法
- 对称加密:AES(Advanced Encryption Standard)、DES、3DES、RC4 等。
- 非对称加密:RSA、ECC(Elliptic Curve Cryptography)、DSA(Digital Signature Algorithm)等。
总结
维度 | 对称加密 | 非对称加密 |
---|---|---|
密钥 | 单密钥(加密 / 解密共用) | 公钥(公开)+ 私钥(保密) |
密钥分发 | 需安全渠道,难度高 | 公钥可公开,无分发难题 |
效率 | 速度快,适合大量数据 | 速度慢,适合小数据 / 密钥交换 |
安全性基础 | 密钥保密性 | 数学难题(如大整数分解) |
典型应用 | 文件加密、实时通信 | 密钥交换、数字签名、HTTPS |
实际场景中,两者常结合使用:用非对称加密解决对称密钥的安全分发问题,再用对称加密高效处理大量数据传输(如 HTTPS 协议的工作原理)。
cdn怎么实现优化的?
CDN(内容分发网络)通过一系列技术手段和策略实现优化,旨在提升内容分发效率、降低延迟、提高可用性和用户体验。以下是 CDN 优化的关键实现方式:
1. 全球分布式节点部署
CDN 服务商在全球各地部署大量服务器节点(POP,Point of Presence),这些节点靠近用户地理位置。当用户请求内容时,CDN 会将请求导向最近的节点,减少数据传输距离,从而降低延迟。
- 智能 DNS 解析:根据用户 IP 地址,将请求路由到最近的 CDN 节点。
- Anycast 技术:多个节点使用相同 IP 地址,通过 BGP 协议选择最优路径。
2. 内容缓存策略
CDN 通过缓存静态内容(如图片、CSS、JS 文件、HTML 页面等)减少源站负载,并加速内容交付:
- 分层缓存
- 边缘节点缓存:最接近用户的节点,缓存热门内容。
- 区域节点缓存:负责服务多个边缘节点,缓存次热门内容。
- 源站:原始内容存储地,仅在缓存未命中时被访问。
- 缓存失效策略
- 时间驱动:设置 TTL(Time-To-Live),过期后重新从源站获取。
- 事件驱动:源站内容更新时主动推送刷新指令(Purge)到 CDN 节点。
3. 内容优化与压缩
CDN 会对内容进行预处理,提升传输效率:
- 文件压缩:对文本内容(如 HTML、CSS、JS)进行 gzip 或 Brotli 压缩,减少传输体积。
- 图片优化
- 自动转换为更高效的格式(如 WebP)。
- 按需调整图片质量和尺寸(如响应式图片)。
- 代码精简:去除不必要的空格、注释,缩短文件大小。
- 动态内容加速
针对动态内容(如 API 请求、用户生成内容),CDN 采用特殊优化技术:
- 动态内容缓存:缓存部分动态内容的片段(如导航栏、广告位)。
- 协议优化:使用 HTTP/3(基于 QUIC)减少连接延迟,支持多路复用。
- 边缘计算:在 CDN 节点运行轻量级代码(如 JavaScript),处理部分动态逻辑,减少源站负载。
5. 负载均衡与高可用性
CDN 通过以下方式确保服务稳定和高可用:
- 多节点冗余:同一区域部署多个节点,避免单点故障。
- 实时监控与自动切换:监控节点健康状态,自动将流量导向正常节点。
- DDoS 防护:利用分布式节点分散攻击流量,过滤恶意请求。
6. 预取与预热
- 内容预取:预测用户可能访问的内容,提前缓存到边缘节点。
- 内容预热:在流量高峰前,主动将热门内容推送到各节点。
7. 协议与传输优化
- HTTP/2/3 支持:提升并发性能,减少头部阻塞。
- TCP 优化:调整 TCP 参数(如初始窗口大小),加速连接建立。
- QUIC 协议:基于 UDP,减少握手延迟,优化移动网络性能。
8. 安全增强
CDN 通常集成安全功能:
- HTTPS 支持:提供免费 SSL 证书,加密数据传输。
- WAF(Web 应用防火墙):过滤 SQL 注入、XSS 等攻击。
- Bot 管理:识别并拦截恶意爬虫和自动化工具。
- 数据分析与智能调度
- 实时流量分析:根据用户行为和流量模式调整缓存策略。
- A/B 测试:对比不同节点或配置的性能,优化路由策略。
10. 与源站集成
CDN 与源站紧密协作,确保内容一致性和高效同步:
- 回源优化:控制回源请求的频率和方式,避免源站过载。
- 推送机制:源站内容更新时,主动通知 CDN 节点刷新缓存。
示例:CDN 优化前后对比
指标 | 未使用 CDN | 使用 CDN 后 |
---|---|---|
加载时间 | 5 秒 | 1.2 秒 |
全球可用性 | 98% | 99.9% |
源站负载 | 高 | 低 |
抗 DDoS 能力 | 弱 | 强 |
总结
CDN 通过分布式架构、缓存策略、内容优化、协议升级和安全增强等多种手段,全面提升内容分发效率和用户体验。选择合适的 CDN 服务商(如 Akamai、Cloudflare、阿里云 CDN 等)并结合业务需求配置优化参数,是实现高效 CDN 的关键。
相关内容
从初中级如何迈入中高级-其实技术只是"入门卷"-CSDN博客
前端梳理体系从常问问题去完善-基础篇(html,css,js,ts)-CSDN博客
前端梳理体系从常问问题去完善-工程篇(webpack,vite)_前端系统梳理-CSDN博客