SSE、WebSocket、WebRTC:从 HTTP 轮询到实时通信

发布时间:2026/10/21 9:17:42
SSE、WebSocket、WebRTC:从 HTTP 轮询到实时通信 摘要在学习前端实时通信时我接触到了 SSE、WebSocket 和 WebRTC 三种技术。它们都可以用于“实时通信”但解决的问题并不相同SSE 主要用于服务器向客户端推送文本事件WebSocket 适合客户端与服务器之间的双向消息通信WebRTC 则主要用于实时音视频和数据通道。本文从传统 HTTP 轮询开始依次介绍 SSE、WebSocket 和 WebRTC 的通信模型、核心 API、基本代码、连接管理方式和适用场景并总结三种技术之间的区别以及实际选型思路。一、为什么需要实时通信传统 HTTP 通常采用请求—响应模式客户端发起请求 → 服务器处理 → 服务器返回响应例如客户端想知道一个任务是否完成需要主动请求服务器客户端任务完成了吗 服务器还没有。 客户端任务完成了吗 服务器已经完成。如果客户端需要不断获取最新状态就可以使用轮询setInterval(async () { const response await fetch(/api/task/status); const data await response.json(); console.log(data.status); }, 5000);轮询的优点是实现简单适合低频、非即时的状态检查。但它也有明显问题没有变化时仍然会发送请求轮询间隔越长实时性越差轮询间隔越短请求次数越多需要自己处理失败、重试和状态同步。当服务器需要主动通知浏览器时可以考虑 SSE。当客户端和服务器都需要持续发送消息时可以考虑 WebSocket。如果业务涉及实时音视频则需要进一步了解 WebRTC。二、SSE服务器发送事件2.1 SSE 是什么SSE 的全称是 Server-Sent Events中文通常叫服务器发送事件。它基于 HTTP让服务器通过一个持续存在的 HTTP 响应不断向浏览器发送文本事件。通信模型如下客户端 ── HTTP 请求 ── 服务器 客户端 ── 事件流 ──── 服务器SSE 的特点是建立方式仍然是 HTTP主要通信方向是服务器到客户端数据通常是 UTF-8 文本响应类型是text/event-stream浏览器可以使用EventSource接收事件。SSE 适合服务器主动推送通知但不适合客户端和服务器之间频繁地双向交换消息。2.2 SSE 的事件格式最简单的 SSE 消息如下data: hello事件末尾通常需要一个空行用于表示本条事件结束。带 JSON 的事件data: {message:任务完成,code:200}带事件名称的消息event: notification data: {title:收到一条通知}带事件编号的消息id: 1001 event: notification data: {title:任务完成}常用字段包括字段作用data事件内容event事件名称id事件编号retry建议客户端重连的等待时间2.3 浏览器端使用 EventSourceconst source new EventSource(/api/events); source.onopen () { console.log(SSE 连接建立); }; source.onmessage (event) { const data JSON.parse(event.data); console.log(收到默认事件, data); }; source.addEventListener(notification, (event) { const data JSON.parse(event.data); console.log(收到通知, data); }; source.onerror (error) { console.error(SSE 连接异常, error); };如果服务器发送的是默认事件可以使用source.onmessage handler;如果服务器发送了自定义事件则可以使用source.addEventListener(事件名称, handler);关闭连接source.close();2.4 服务端返回 SSE以 Spring Boot 为例GetMapping( value /api/events, produces MediaType.TEXT_EVENT_STREAM_VALUE ) public SseEmitter events() { SseEmitter emitter new SseEmitter(0L); try { emitter.send( SseEmitter.event() .name(connected) .data(Map.of(message, 连接成功)) ); } catch (IOException exception) { emitter.completeWithError(exception); } return emitter; }关键配置是produces MediaType.TEXT_EVENT_STREAM_VALUE它表示这个接口返回的是持续的事件流而不是一次性返回的普通 JSON。实际项目中还需要考虑如何保存在线连接如何向指定用户发送事件客户端断开后如何清理连接是否需要定期发送心跳代理服务器是否会缓存响应断线后是否需要补发事件如何处理重复事件。2.5 SSE 的断线重连浏览器的EventSource通常会在连接断开后尝试重新连接。服务器可以发送retry: 5000表示建议客户端等待一段时间后再重新连接。服务器也可以发送事件 IDid: 1001 data: {message:第一条消息} id: 1002 data: {message:第二条消息}客户端重新连接时可能携带Last-Event-ID: 1002服务器可以根据这个 ID 判断客户端已经收到哪些消息并从后续事件开始补发。不过需要注意SSE 的自动重连不等于可靠消息队列也不等于自动保证消息不丢失。如果服务器没有保存历史事件即使浏览器重新连接也无法恢复断线期间错过的消息。完整的断线恢复通常需要事件 ID ↓ 服务端保存事件 ↓ 客户端重连并携带 Last-Event-ID ↓ 服务端补发事件 ↓ 客户端去重2.6 SSE 的典型应用SSE 适合AI 流式输出新闻或日志流后台任务进度作业批改完成通知服务端状态更新文件处理结果通知。例如 AI 流式输出可以拆成多条事件data: {delta:你好} data: {delta:这是} data: {delta:一段} data: {delta:回答} data: {done:true}浏览器收到一条就更新一次页面不需要等待完整内容生成。三、WebSocket双向实时消息通信3.1 WebSocket 是什么WebSocket 是一种支持客户端与服务器双向通信的协议。通信建立后双方都可以主动发送消息客户端 ───────── 服务器 客户端 ───────── 服务器与 SSE 相比WebSocket 的主要特点是支持双向通信支持文本消息支持二进制消息支持持续连接不再局限于普通 HTTP 的请求—响应模型。3.2 浏览器端使用 WebSocketconst socket new WebSocket(wss://example.com/chat); socket.addEventListener(open, () { console.log(WebSocket 连接成功); socket.send(JSON.stringify({ type: join, roomId: room-1 })); }); socket.addEventListener(message, (event) { const message JSON.parse(event.data); console.log(收到消息, message); }); socket.addEventListener(close, (event) { console.log(连接关闭, event.code, event.reason); }); socket.addEventListener(error, (error) { console.error(连接错误, error); });常见事件包括事件作用open连接成功message收到消息close连接关闭error连接异常生产环境通常使用wss://而不是ws://因为wss://基于 TLS可以保护通信内容。3.3 WebSocket 支持文本和二进制发送文本socket.send(hello);发送 JSONsocket.send(JSON.stringify({ type: chat, content: 你好 }));发送二进制const buffer new ArrayBuffer(16); socket.send(buffer);WebSocket 可以传输文本JSON图片文件音频片段自定义二进制协议。因此不能简单说 WebSocket 只能传文本或只能传二进制。3.4 WebSocket 消息格式设计WebSocket 只提供通信通道业务消息格式需要自己设计。比较常见的消息结构是{ id: message-1001, type: chat.message, timestamp: 1720000000, payload: { content: 你好 } }其中id表示消息编号type表示消息类型timestamp表示时间payload表示业务数据。消息类型可以包括chat.message user.join user.leave typing.start typing.stop notification error统一的消息结构有助于区分不同业务事件关联请求和响应实现幂等和去重进行日志记录兼容后续协议升级。3.5 WebSocket 心跳长时间没有业务消息时某些代理或网络设备可能关闭连接。因此通常需要设计心跳let heartbeatTimer; function startHeartbeat(socket) { heartbeatTimer setInterval(() { if (socket.readyState WebSocket.OPEN) { socket.send(JSON.stringify({ type: ping, timestamp: Date.now() })); } }, 30000); }服务端收到后返回{ type: pong, timestamp: 1720000000 }实际系统还应该记录最后一次收到 pong 的时间如果长时间没有响应就可以认为连接失效。3.6 WebSocket 重连WebSocket 断开后通常需要应用自己处理重连。let reconnectDelay 1000; function connect() { const socket new WebSocket(wss://example.com/ws); socket.addEventListener(open, () { console.log(连接成功); reconnectDelay 1000; }); socket.addEventListener(close, () { console.log(连接断开); setTimeout(() { reconnectDelay Math.min(reconnectDelay * 2, 30000); connect(); }, reconnectDelay); }); } connect();这个例子使用了指数退避1 秒 → 2 秒 → 4 秒 → 8 秒 → ...实际项目中还可以加入随机等待避免大量客户端同时重连造成重连风暴。3.7 WebSocket 服务端需要处理的问题WebSocket 服务端不仅要处理消息还要管理连接生命周期。常见问题包括连接管理用户 ID → WebSocket 连接房间管理房间 ID → 多个连接断开清理客户端断开后需要清除在线状态房间成员订阅关系临时资源用户连接记录。多实例部署如果 WebSocket 服务有多个实例客户端 A → 实例 1 客户端 B → 实例 2实例 1 产生的消息可能需要发送给实例 2 上的客户端。这时通常需要额外考虑Redis Pub/Sub消息队列独立 WebSocket 网关粘性会话连接路由。3.8 WebSocket 的典型应用WebSocket 适合在线聊天在线客服协同编辑实时通知实时控制实时游戏状态在线用户状态同步。简单判断如果客户端和服务器都需要主动发送消息可以优先考虑 WebSocket。四、WebRTC实时音视频与数据通道4.1 WebRTC 是什么WebRTC 主要用于浏览器或应用之间的实时通信包括音频视频屏幕共享数据通道。典型流程是媒体采集 ↓ 连接协商 ↓ 寻找网络路径 ↓ 建立媒体或数据通道 ↓ 实时传输WebRTC 不是简单的“浏览器长连接”它涉及媒体采集、会话协商、NAT 穿透、加密和传输拓扑。4.2 获取摄像头和麦克风const localStream await navigator.mediaDevices.getUserMedia({ audio: true, video: true });将本地视频显示在页面上const localVideo document.querySelector(#localVideo); localVideo.srcObject localStream;HTMLvideo idlocalVideo autoplay muted playsinline/video浏览器通常会要求用户授权摄像头和麦克风权限。4.3 创建 RTCPeerConnectionconst peerConnection new RTCPeerConnection({ iceServers: [ { urls: stun:stun.example.com } ] });RTCPeerConnection负责SDP 协商ICE Candidate媒体轨道连接状态远端媒体接收。添加本地媒体轨道for (const track of localStream.getTracks()) { peerConnection.addTrack(track, localStream); }接收远端媒体peerConnection.addEventListener(track, (event) { const remoteStream event.streams[0]; document.querySelector(#remoteVideo).srcObject remoteStream; });4.4 WebRTC 为什么需要信令WebRTC 本身不规定信令协议。建立连接前双方需要交换SDP OfferSDP AnswerICE Candidate。这些信息可以通过以下方式传递WebSocketHTTP其他消息系统。常见结构是客户端 A ── WebSocket ── 信令服务器 客户端 B ── WebSocket ── 信令服务器 客户端 A ── 信令消息转发 ── 客户端 B 客户端 A ──── WebRTC 媒体 ──── 客户端 B这里需要明确WebSocket 可以用于 WebRTC 信令但 WebSocket 不负责传输 WebRTC 的音视频。4.5 创建 Offer 和 Answer发起方创建 Offerconst offer await peerConnection.createOffer(); await peerConnection.setLocalDescription(offer); signaling.send({ type: offer, sdp: peerConnection.localDescription });另一方收到 Offer 后创建 Answerawait peerConnection.setRemoteDescription(message.sdp); const answer await peerConnection.createAnswer(); await peerConnection.setLocalDescription(answer); signaling.send({ type: answer, sdp: peerConnection.localDescription });发起方收到 Answer 后await peerConnection.setRemoteDescription(message.sdp);这样双方就完成了基本的会话描述协商。4.6 ICE CandidateWebRTC 需要寻找双方之间可用的网络路径。本地发现 Candidate 时peerConnection.addEventListener( icecandidate, (event) { if (event.candidate) { signaling.send({ type: ice-candidate, candidate: event.candidate }); } } );收到远端 Candidate 后await peerConnection.addIceCandidate( message.candidate );ICE 会尝试寻找可用连接路径。4.7 STUN 和 TURNSTUNSTUN 服务器可以帮助客户端发现自己的公网映射地址。const peerConnection new RTCPeerConnection({ iceServers: [ { urls: stun:stun.example.com } ] });但 STUN 只负责辅助发现地址并不保证双方一定可以直连。TURN如果双方无法直连可以通过 TURN 服务器中继客户端 A → TURN 服务器 → 客户端 BTURN 可以提高连接成功率但会增加服务器带宽成本。因此WebRTC 不一定是纯 P2P网络条件允许时可能直连无法直连时可能通过 TURN多人会议中可能通过 SFU 转发某些系统可能使用 MCU 混流。4.8 RTCDataChannelWebRTC 还可以建立数据通道const channel peerConnection.createDataChannel(chat);发送数据channel.addEventListener(open, () { channel.send(JSON.stringify({ type: message, content: 你好 })); });接收数据channel.addEventListener(message, (event) { const message JSON.parse(event.data); console.log(收到数据, message); });DataChannel 可以用于聊天消息游戏状态实时位置光标位置文件分片控制指令。五、三种技术如何选择需求可以考虑的技术服务端向浏览器推送文本通知SSEAI 输出逐步返回SSE客户端和服务端双向聊天WebSocket协同编辑和实时状态同步WebSocket一对一视频通话WebRTC多人视频会议WebRTC SFU 等媒体架构WebRTC 的信令交换WebSocket、HTTP 等不要求即时性的状态查询普通 HTTP 或轮询可以用下面的思路快速判断只有服务器主动推送 是 → 考虑 SSE 客户端和服务器都要主动发消息 是 → 考虑 WebSocket 需要音频、视频或屏幕共享 是 → 考虑 WebRTC六、一个作业管理系统的例子假设系统包含学生查看作业学生提交作业老师批改作业学生收到批改完成通知。可以这样设计查看作业HTTP GET 提交作业HTTP POST 老师批改HTTP POST 批改完成通知SSE整体结构学生浏览器 ── HTTP ── 作业服务 学生浏览器 ── SSE ─── 作业服务 老师浏览器 ── HTTP ── 作业服务 作业服务 ── 产生通知 ── SSE 连接这个场景没有音视频需求因此不需要 WebRTC。如果客户端和服务器之间也没有高频双向消息WebSocket 也可能不是必要的。这只是根据通信方向和数据类型做出的工程建议。实际项目还需要结合在线连接数消息大小消息频率部署方式代理配置网络环境压测结果。我会在后续的文章中讲解如何使用。七、常见误区误区一SSE 自动保证消息不丢失不准确。SSE 可以自动重连也可以使用事件 ID但可靠恢复仍然需要服务端保存事件并支持补发。误区二WebSocket 只能传二进制错误。WebSocket 同时支持文本消息、二进制消息和控制帧。误区三WebRTC 必须使用 WebSocket错误。WebRTC 不规定信令协议。WebSocket 只是常见的信令传输方式。误区四WebRTC 一定不占服务器带宽错误。WebRTC 可能使用 TURN 中继也可能通过 SFU 或其他媒体服务器转发。误区五自动重连等于数据恢复不论是 SSE 还是 WebSocket自动重连 ≠ 自动补发消息如果业务不能接受消息丢失就需要设计消息 ID消息持久化重放去重状态同步失败重试。误区六只根据 CPU 和带宽选择协议不建议仅凭“几核 CPU、几兆带宽”直接判断协议能不能使用。还需要了解实际在线人数连接数量消息频率音视频码率是否经过中继是否需要编码或混流实际运行和压测结果。八、我的学习总结通过对 SSE、WebSocket 和 WebRTC 的学习我的理解是SSE 解决的是服务器向客户端持续推送文本事件的问题。WebSocket 解决的是客户端与服务器双向交换消息的问题。WebRTC 主要解决实时音视频和端到端数据传输的问题。WebRTC 通常需要信令服务但信令不一定必须使用 WebSocket。自动重连不代表消息一定不会丢失。协议选型不能只看“实时”两个字还要分析通信方向、数据类型和网络拓扑。可以用三句话记忆通知和流式输出SSE 双向业务消息WebSocket 实时音视频WebRTC如果继续深入学习建议按照下面的顺序实践1. 使用 SSE 实现任务进度推送 2. 使用 WebSocket 实现聊天室 3. 使用 WebSocket 作为信令实现 WebRTC 双人视频通话 4. 继续学习 TURN、SFU、断线恢复和多实例部署

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询