HTTP

HTTPS 是 HTTP 的安全版本,在 HTTP 和 TCP 之间加入了 SSL/TLS 加密层,保证:
HTTPS作为一个加密层,可以与所有版本的HTTP结合使用
加密:防止数据被窃听。
完整性:防止数据被篡改。
身份验证:验证服务器身份(通过证书)。
在生产环境中,务必使用 HTTPS 保护用户数据(如密码、Token)
协议是:浏览器 ↔ 服务器 每次建立连接时「自动协商」出来的,它是灵活、动态、可同时支持多种的

HTTP协议版本并不是一个网站(域名)固定不变的,而是动态协商、灵活多变的。它既不是绑定在域名上,也不是根据具体的接口(URL路径)来定,而是由客户端(浏览器)和服务器在连接建立时共同协商决定的。同一个域名下的不同请求,甚至在不同时间、不同网络环境下,完全可能使用不同的HTTP版本。HTTP协议的设计目标之一是向后兼容和平滑升级

HTTP版本进阶
HTTP/1.0:每个请求/响应后关闭连接。
HTTP/1.1:引入持久连接(Keep-Alive)、管道化(Pipelining)、Host 头等
HTTP/2:二进制分帧、多路复用、头部压缩,大幅提升性能。(基于TLS的ALPN)
HTTP/3:基于 QUIC(UDP),减少连接延迟,提高弱网环境表现(基于Alt-Svc)

在Network面板里随便点开一个请求,看看它的 Alt-Svc 头。如果看到了,就说明这个网站已经为你准备好了更快的HTTP/3连接
HTTP 协议不是绑定域名,也不是绑定接口
是浏览器和服务器「每次连接自动协商」的
服务器可以同时开多种协议,自动兼容
对前端开发者来说:基本不用管,自动适配

HTTP 轮询

信道(Channel):在网络通信中,通常指一个TCP连接。每个TCP连接都会占用服务器端的文件描述符、内存等资源。并发连接数过高可能导致服务器资源耗尽。
HTTP 轮询是一种基于传统 HTTP 请求-响应模式的伪实时通信技术
短轮询(夺命连环call):客户端按照一定的时间间隔,反复向服务器发送 HTTP 请求,询问是否有新数据,服务器收到请求后立即返回响应。
长轮询:服务器接收到请求后,如果没有新数据,会保持连接挂起,直到有新数据或超时才返回,这可以减少无效响应。
实时性低,长轮训较高一点;资源消耗短轮询较高,长轮训较低
短轮询虽然对并发连接数友好,但它会带来巨大的请求频率,适合客户端数量不大的场景
长轮询的每个请求都会长期占用一个连接,不利于高并发
例子:网页扫描二维码

SSE

Server-Sent Events 是一种允许服务器主动向浏览器推送事件的技术。它基于纯 HTTP 协议,客户端通过 EventSource API 建立连接,服务器保持连接打开,并以特定格式 (text/event-stream) 发送数据。SSE 具有以下特点:

  • 单向通信:仅服务器可推送数据,客户端不能通过该连接发送数据(适合通知类场景)。
  • 自动重连:连接断开后浏览器会自动尝试重新连接。
  • 轻量简单:无需 WebSocket 的复杂握手和协议升级。

例子:餐厅等位叫号、股票报价、新闻推送

  • 注意:SSE 的限制: 浏览器对同一个域名的 SSE 连接数通常有限制(Chrome 默认 6 个),如果开了太多标签页可能会失效

应用:社区在线人数统计功能

目标:实时向所有在线用户显示当前社区在线人数。
实现步骤

  1. 客户端发起 SSE 连接
    前端通过 new EventSource(‘/api/community/events?token=…’) 建立连接,并在 URL 中携带 JWT token 用于身份验证。
  2. 服务端接收连接并验证
    接口 streamCommunityEvents 解析 token,验证用户身份。
    设置 SSE 响应头,保持连接打开。
    生成唯一 clientId,将客户端信息(id、userId、response 对象)存入全局 clients Map 中(调用 addCommunityClient)。
  3. 广播当前在线人数
    addCommunityClient 内部调用 broadcastOnlineCount。
    broadcastOnlineCount 遍历 clients Map,向每个客户端发送 event: online 事件,数据为 { count: clients.size }。
  4. 维护心跳
    服务端每隔 20 秒向每个客户端发送注释行 :keep-alive\n\n,防止代理或浏览器因长时间无数据而超时关闭连接。
    客户端断开处理
    当客户端关闭页面或网络中断时,触发 res 对象的 close 事件。
    在 close 回调中清理心跳定时器,并调用 removeCommunityClient(clientId) 从 clients Map 中移除该客户端。
    removeCommunityClient 再次触发 broadcastOnlineCount,更新所有客户端的在线人数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    graph TD
    A[客户端打开社区页面] --> B[创建 EventSource 连接]
    B --> C[服务端接收 /events 请求]
    C --> D{验证 token}
    D -- 无效 --> E[返回 401]
    D -- 有效 --> F[设置 SSE 响应头]
    F --> G[生成 clientId, 存入 clients Map]
    G --> H[调用 broadcastOnlineCount]
    H --> I[遍历 clients, 发送 online 事件]
    I --> J[启动心跳定时器]
    J --> K[客户端接收 online 事件, 更新在线人数]

    K --> L{客户端断开?}
    L -- 是 --> M[触发 close 事件]
    M --> N[清除心跳, 调用 removeCommunityClient]
    N --> O[从 clients 删除该 client]
    O --> P[再次 broadcastOnlineCount]
    P --> Q[其他客户端收到更新后在线人数减1]
    L -- 否 --> K

应用:新帖实时推送功能

目标:当有新帖子发布并审核通过后,立即向所有在线用户推送通知。
实现步骤

  1. 用户发布帖子
    前端提交发帖表单,调用 createCommunityPost 接口。
    服务端进行内容验证、敏感词检查、权限判断,若通过且无需审核(status = 1),则帖子立即生效。
  2. 触发推送
    在帖子创建成功后,如果 status === 1 且 publish_status === 1,则调用 broadcastNewPost 函数。
    broadcastNewPost 遍历当前所有活跃客户端(clients Map),向每个客户端写入 event: new_post 事件,数据包含帖子 ID、标题、作者等关键信息。
  3. 客户端接收并展示
    前端 EventSource 监听 new_post 事件,提取数据后可在页面右上角或列表中动态显示“有新帖”的提示,或直接插入新帖卡片。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    graph TD
    A[用户提交发帖] --> B[服务端 createCommunityPost]
    B --> C{验证审核是否通过}
    C -- 否 --> D[返回结果, 不推送]
    C -- 是 --> E[帖子保存成功, status=1]
    E --> F[调用 broadcastNewPost]
    F --> G[遍历 clients Map]
    G --> H[对每个客户端发送 new_post 事件]
    H --> I[客户端监听 new_post 事件]
    I --> J[前端更新 UI, 显示新帖通知]

关键技术点总结

技术点 说明
SSE 连接管理 使用 Map 存储所有活跃连接,key 为唯一 ID,value 为包含 res 对象的 client 信息。
广播机制 遍历 Map,通过每个 client 的 res.write() 发送格式化消息。
心跳维持 定时发送注释行,防止连接因空闲被关闭。
连接清理 监听 res.on(‘close’) 事件,及时移除断开的客户端,并更新在线人数。
事件格式 event: 事件名\n + data: JSON字符串\n\n,符合 SSE 规范。
身份验证 连接 URL 中携带 JWT token,服务端验证后建立连接,确保安全性。

心跳检测

心跳检测是一种机制,定期在通信双方(客户端和服务器)之间发送一个小数据包(心跳包),用来确认对方是否还“活着”(即连接是否正常)。如果一方在指定时间内没有收到心跳响应,就认为连接已经断开,从而主动关闭连接并尝试重连。
比喻:两个人打电话,如果长时间不说话,其中一人可能会说“喂,你还在吗?”对方回应“在呢”。如果没回应,就知道电话可能断了。

  • 心跳间隔设置注意事项
    间隔不宜过短:太频繁会增加网络和服务器负担。一般 3060 秒是比较合理的范围。
    超时时间应略大于间隔:例如心跳间隔 30 秒,超时时间可设为 5
    10 秒,考虑到网络延迟,避免误判。
    考虑移动网络:在移动端,网络切换或休眠可能导致心跳中断,可以适当缩短间隔,但也要注意省电。

SSE需要心跳检测(保持连接活跃,目的是保持连接不被网络中间件关闭)

SSE 需要心跳,但目的是保持连接不被网络中间件关闭,而不是检测对方是否存活(因为 SSE 是单向的,服务器不会关心客户端是否还活着)
SSE 是基于 HTTP 的长连接,服务器向客户端持续发送数据。但网络中间件(如 Nginx、路由器、防火墙)可能会因为连接长时间空闲而自动断开。例如,Nginx 默认 proxy_read_timeout 为 60 秒,如果 60 秒内没有数据传输,它会关闭连接。这样客户端就收不到后续消息了。
因此,我们需要定期发送“心跳”来告诉中间件“连接还在使用”,防止超时断开

SSE 的心跳如何实现

SSE 协议本身没有规定心跳机制,实现方法简单:服务器定期发送注释行(:\n\n)
相比 WebSocket 的双向心跳,SSE 的心跳是单向的(仅服务器→客户端),且不需要客户端响应
服务器可以定期发送注释行(以冒号 : 开头)作为心跳。
浏览器收到注释行会直接忽略,不会触发 onmessage 事件,但连接上的数据流动足以重置网络设备的超时计时器。

WebSocket 需要心跳检测

WebSocket 连接虽然是长连接,但网络环境复杂,可能会遇到以下问题:
中间设备超时断开:网络中的路由器、防火墙、代理服务器等可能会自动关闭长时间没有数据传输的连接(例如 Nginx 默认 60 秒无数据就断开)。
客户端或服务器异常崩溃:如果一方突然崩溃,另一方可能无法立刻感知,导致连接变成“僵尸连接”,占用资源。
网络闪断:短暂的网络故障可能导致连接中断,但双方没有立即感知。
保持连接活跃:有些移动网络或浏览器对空闲连接有超时限制,定期发送心跳可以“续命”。
心跳检测就是为了解决这些问题,让双方能够及时知道连接状态,并在断开后迅速恢复。

WebSocket 协议本身提供了两种心跳相关的机制:

  1. 协议级 Ping/Pong 帧(推荐)
    WebSocket 协议定义了两种控制帧:Ping 和 Pong。
    一方可以发送 Ping 帧,对方收到后必须自动回复 Pong 帧(协议规定)。
    这些帧非常轻量,不包含应用数据,只用于连接健康检查。
    浏览器原生 WebSocket API 没有暴露发送 Ping 帧的方法(出于安全考虑),但服务器可以主动发送 Ping,浏览器会自动回复 Pong。前端可以通过监听 onmessage 来接收自定义的 Pong,但无法直接操作 Ping/Pong 帧。

  2. 应用层心跳(自定义消息)
    如果无法使用协议级 Ping/Pong(例如前端需要主动检测),可以在应用层实现心跳:
    客户端定期发送一个特定格式的消息(如 { type: ‘heartbeat’ })。
    服务器收到后回复一个心跳响应(如 { type: ‘heartbeat_ack’ })。
    如果客户端在超时时间内未收到响应,则认为连接断开。
    这种方式更灵活,但需要自己处理消息解析和超时逻辑。

WebSocket

原理:通过一次 HTTP 握手(Upgrade 头),将协议从 HTTP 切换为 WebSocket 协议,之后客户端和服务器之间可以双向自由传输数据(文本或二进制)

握手过程(理解即可):
客户端发 GET 请求,带上 Upgrade: websocket、Connection: Upgrade 以及 Sec-WebSocket-Key。
服务器返回 101 Switching Protocols,同时返回 Sec-WebSocket-Accept。
之后协议切换为 WebSocket,通信不再走 HTTP。

特点:
全双工,双向实时通信。
支持文本和二进制数据。
无同源策略限制

例子:实时聊天室、在线多人游戏、协同编辑(如飞书文档)