跳到主要内容

实时通信方案整理

· 阅读需 9 分钟

参考文章:WebSockets vs Server-Sent-Events vs Long-Polling vs WebRTC vs WebTransport

0 轮询

数据流向:client <= server

基于传统的 HTTP 请求-响应方案,定时频繁地向服务器请求最新数据,实现伪实时通信

优点:

  • 实现简单,不需要特殊的服务器端支持。

  • 兼容性好,几乎所有的环境都支持。

缺点:

  • 高频请求会增加服务器和网络的负载。

  • 存在明显的延迟,无法真正实现实时通信。

适用场景:数据更新频率低且实时性要求不高的场景,如定期数据同步。

1 长轮询(Long-Polling)

数据流向:client <= server

客户端建立与服务器的连接,该连接在新数据可用之前保持打开状态。一旦服务器获得新信息,它就会将响应发送到客户端,并关闭连接。在收到服务器的响应后,客户端会立即启动一个新请求,并重复该过程

优点:

  • 比传统轮询更高效,减少不必要的网络流量和服务器负载。

  • 实时性更高,能够更及时地获取数据更新。

缺点:

  • 仍然会导致一定的通信延迟。效率低于 WebSockets 等其他实时技术

  • 在高并发情况下,服务器需要处理大量的打开和关闭连接的操作。

示例:

// js 客户端长轮询
function longPoll() {
fetch('http://example.com/poll')
.then(response => response.json())
.then(data => {
console.log("Received data:", data);
longPoll(); // 马上建立一个新的长轮询
})
.catch(error => {
/**
* 当连接超时或客户端离线,请求会抛出异常,此时可以尝试一段时间后再次发起长轮询
*/
setTimeout(longPoll, 10000);
});
}
longPoll(); // 执行长轮询,建立初始连接

适用场景:中等实时性要求的场景,如通知系统、消息推送等。

2 WebSocket

数据流向:client <=> server

WebSocket 为客户端和服务器之间的单一长期连接提供全双工通信通道,允许双方在建立连接后独立互相发送数据

示例:

// js 客户端 websocket 连接
const socket = new WebSocket('ws://example.com');

socket.onopen = function(event) {
console.log('Connection established');
// 向服务器发送一条消息
socket.send('Hello Server!');
};

// 接收服务器响应的消息
socket.onmessage = function(event) {
console.log('Message from server:', event.data);
};

优点:

  • 实时性高,低延迟。
  • 全双工通信,支持双向数据传输。
  • WebSocket API 简单易用。

缺点:

  • 连接不稳定时需要处理重新连接和断线重连。
  • 需要额外的心跳检测(ping-pong)来保持连接的活跃性。

适用场景:

  • 需要低延迟和高频数据更新的场景,如实时聊天、游戏、金融交易平台等。

推荐使用 Socket.IO 这样的 WebSockets 之上的库,不仅可以处理以上这些复杂情况,甚至在需要时提供对长轮询的回退。

3 Server-Sent-Events

数据流向:client <= server

服务器发送事件(SSE)提供了一种通过 HTTP 将服务器更新推送到客户端的标准方法,保持连接并持续发送数据。可以将 SSE 视为单个 HTTP 请求,其中服务端不会一次发送整个正文,而是保持连接,然后像水流一样一点一点返回响应数据

优点:

  • 简单易用,浏览器原生支持。
  • 自动处理重新连接。
  • 适用于单向数据流的场景。

缺点:

  • 仅支持服务器到客户端的单向通信。
  • 不如 WebSocket 灵活,不能进行双向通信。

适用场景:

  • 需要实时更新客户端而无需向服务器发送数据的场景,如实时新闻提要、体育比分等。
  • 与 WebSocket 不同,SSE 专为服务器到客户端的单向通信而设计,非常适合实时新闻提要、体育比分、AI 聊天等需要实时更新客户端而无需向服务器发送数据的场景。

示例:

  1. EventSource

在浏览器上可以使用 URL 初始化 EventSource 实例

// 连接服务端 sse 接口
const evtSource = new EventSource("https://example.com/events");

// 处理数据
evtSource.onmessage = event => {
console.log('got message: ' + event.data);
};

与 WebSocket 不同,EventSource 将在连接丢失时自动重新连接。

  1. fetch
async function getAiResponse() {
const res = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
content: '这是我的问题'
})
})

const reader = res.body.getReader()
const textDecoder = new TextDecoder()
while(1) {
const {done, value} = await reader.read()
if(done) {
break
}
const str = textDecoder.decode(value)
console.log(str)
}
}
  1. 服务端

在服务器端,您的脚本必须将 Content-Type 标头设置为 text/event-stream,并根据 SSE 规范设置每条消息的格式。这包括指定事件类型、数据有效负载和可选字段,如事件 ID 和重试时间。

下面以 Express 为例,介绍如何设置简单的 SSE 接口:

import express from 'express';
const app = express();
const PORT = process.env.PORT || 3000;

app.get('/events', (req, res) => {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
});

const sendEvent = (data) => {
// 全部 message 必须加上 'data:'
const formattedData = `data: ${JSON.stringify(data)}\n\n`;
res.write(formattedData);
};

// 没两秒发送一次事件,模拟流式数据
const intervalId = setInterval(() => {
const message = {
time: new Date().toTimeString(),
message: 'Hello from the server!',
};
sendEvent(message);
}, 2000);

// 当连接关闭后,做些清理工作
req.on('close', () => {
clearInterval(intervalId);
res.end();
});
});
app.listen(PORT, () => console.log(`Server running on http://localhost:${PORT}`));

4 WebTransport API

数据流向:client <=> server

WebTransport 利用 HTTP/3 QUIC 协议实现 Web 客户端和服务器之间的高效、低延迟通信,例如以可靠和不可靠的方式通过多个流发送数据,甚至允许数据无序发送。

优点:

  • 高效的低延迟通信。
  • 支持多流和无序数据传输。

缺点:

  • API 较复杂,开发难度较大。可以等第三方库
  • 浏览器支持不广泛,目前 Safari 和 Node.js 不支持。

适用场景:

  • 实时游戏、直播、协作平台等需要高效低延迟通信的场景。

5 WebRTC

数据流向:client <=> client

WebRTC(Web Real-Time Communication)是一个开源项目和 API 标准,可在 Web 浏览器和移动应用程序中启用实时通信(RTC)功能,支持点对点连接

优点:

  • 支持音频、视频和数据流的点对点通信。
  • 实现低延迟的实时通信。

缺点:

  • 需要信令服务器来建立连接。
  • 复杂度高,需要处理 NAT 穿透和防火墙问题。

适用场景:

  • 视频会议、实时音视频聊天、P2P 文件传输等需要实时点对点通信的场景。