前端实时通信:轮询、WebSocket、SSE长链接
前言
前端常用实时通讯解决方案、及其代码使用,包括短轮询、长轮询、WebSocket、SSE
class="table-box">方式 | 特点 | 链接模式 |
---|---|---|
轮询 | 频繁调用接口,宽带服务器资源浪费 | 前端调用接口获取 |
webSocket(全双工) | 客服端和服务端可互传数据 | 服务器可主动向客户端发送数据 |
SSE长链接 | 用于服务端向客服端发送数据(可利用url传递参数) | 服务器主动向客户端发送数据 |
前端常用实时通讯解决方案、及其代码使用,包括短轮询、长轮询、WebSocket、SSE
class="table-box">方式 | 特点 | 链接模式 |
---|---|---|
轮询 | 频繁调用接口,宽带服务器资源浪费 | 前端调用接口获取 |
webSocket(全双工) | 客服端和服务端可互传数据 | 服务器可主动向客户端发送数据 |
SSE长链接 | 用于服务端向客服端发送数据(可利用url传递参数) | 服务器主动向客户端发送数据 |
在一定时间内,由前端主动调用接口获取数据
提示:需要前端主动每次发起请求
优点:由纯JS实现,兼容所有浏览器
缺点:
效率低,浪费带宽和服务器资源,实时性差;数据更新延迟会受定时器影响。
客户端需要不断发送请求,数据可能并没有变化。
服务器长时间没有数据返回,可能会导致连接超时,需要重新建立连接。(长轮询)
利用定时器,前端每隔一段时间向服务端发起一次请求获取数据
import { APIFun } from "./api";
const useAPI = () => {
const intervalHandler = setInterval(async () => {
let res = await APIFun();
// 处理 res 的逻辑
// ...
clearInterval(intervalHandler);
}, 3000);
};
useAPI();
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
在每次成功处理请求后,继续调用该接口再次处理数据
const useAPI1 = async () => {
let res = await APIFun();
// 处理res数据
// ...
useAPI1();
};
useAPI1();
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
WebSocket 基于TCP协议的双向通信协议,支持服务器主动向客户端推送数据。
通过HTTP协议的101状态码进行握手,握手成功后,客户端和服务器之间的通信就不再使用HTTP协议,而是使用WebSocket协议
特点:客服端和服务端可以进行数据交换
使用场景:客服端和服务端频繁的交换数据
方法名 | 方法 |
---|---|
打开(建立)连接 | onopen |
项服务端发送数据 | send |
接受服务器数据 | onmessage |
webSocket错误处理 | onerror |
关闭连接 | onclose |
提示:以下只是Websocket的使用方法,在实际运用中需要把方法放到组件/页面的生命周期中
例如:onopen 在 onMounted、onclose 在 beforeDestroy
// 建立webSocket链接
// webSocket使用的是WebSocket协议,特征“ws://”
const socket = new WebSocket("ws://webSocket接口地址");
// 打开WebSocket链接,使服务器能够主动向客户端推送消息
socket.onopen = () => {
console.log("WebSocket连接已建立");
};
socket.send("发送的数据"); // 向服务器发送数据
// 处理服务器推送的数据
socket.onmessage = (event) => {
const message = event.data;
// 处理数据,更新页面渲染
};
socket.onerror = (error) => {
// webSocket错误处理
console.error("WebSocket error:", error);
};
socket.onclose(); // 关闭webSocket链接
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
心跳机制:定期发送心跳(ping/pong)消息,检测连接是否仍然有效,防止连接超时。
let heartCheckInterval = 30 * 1000;// 心跳间隔设置为30秒
const setupHeartbeat = () => {
var heartCheck = {
timeout: 60 * 1000, // 60秒无响应,视为断开
serverTimeoutVar: null,
start: function () {
this.serverTimeoutVar = setInterval(() => {
ws.send("heartbeat"); // 发送心跳
}, heartCheckInterval);
},
reset: function () {
clearTimeout(this.serverTimeoutVar);
this.start();
},
};
ws.onclose = () => {
heartCheck.start(); // 关闭时执行相应的操作,例如重连
};
ws.onmessage = (e) => {
if (e.data === "heartbeat") return; // 忽略心跳响应
heartCheck.reset(); // 收到消息,重置心跳
};
heartCheck.start(); // 开始心跳
};
setupHeartbeat()
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">
自动重连:当连接断开时,客户端可以尝试自动重连,通常通过设置重试间隔(比如指数退避策略)来避免频繁重连。
let ws;
let reconnectInterval = 1000; // 初始重连间隔
let maxReconnectInterval = 10000; // 最大重连间隔
let reconnectAttempts = 0; // 重连尝试计数
function startWebSocket() {
ws = new WebSocket("ws://接口地址");
ws.onopen = function (e) {
console.log("Connection established");
reconnectAttempts = 0;// 连接成功,进行其他操作
};
ws.onclose = function (e) {
if (e.code == 1000 || reconnectAttempts >= 3) {
return;// 正常关闭或重连尝试超过3次,不再重连
}
// 使用递增的退避策略重连
reconnectInterval = Math.min(maxReconnectInterval, reconnectInterval * 1.5);
setTimeout(startWebSocket, reconnectInterval);
reconnectAttempts++;
};
ws.onerror = function (e) {
console.error("WebSocket error observed:", e); // 处理错误,可能需要重连
};
}
startWebSocket(); // 启动WebSocket连接
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">
由于请求是异步的,网络问题或高并发场景可能会造车数据丢失和顺序紊乱
1、数据丢失
在接受数据后,向服务器发送一条数据,告知已接受该信息;
服务端再判断数据是否被客户端成功接受,若没有再发送一次该数据
socket.onmessage = (data) => {
// 接收到服务器data数据
//告知服务端接收到了该消息
socket.send({ newsId: "12134124" });
// 对数据进行处理
...
};
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
2、数据顺序
socket.onmessage = (data) => {
// 遍历data数据,对里面的数据排序
// oldData 已有数据
// 若需要添加到 oldData 中,则对比 oldData、data的前后数据时间戳
// 若两个数据符合前后关系,直接插入
const oldData = [...oldData, ...data];
// 若不满足上述条件,则遍历两个数据排序
...
};
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
ws 协议没有内建加密机制,若需要可以使用 wss 协议 ;
其使用TLS/SSL协议来保证客户端和服务器之间的通信安全,需要一个有效的SSL/TLS证书
let wss = new WebSocket("wss://接口地址");
wss.onopen = () => {
// 打开websocket链接时,需要做身份验证
wss.send({ token: "" });
};
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
SSE是一种基于HTTP协议的服务器推送技术,允许服务器向客户端推送文本数据或事件数据,而无需客户端发起请求。通过HTTP的长连接机制实现服务器向客户端的推送,客户端通过EventSource API接口接收服务器推送的数据。
带参数请求
1、url携带参数(get形式)
2、打开SSE链接 通过普通http请求向服务器发送数据,服务端通过SSE传回(考虑webSocket)
const eventSource = new EventSource('http://example.com/events?param1=value1¶m2=value2');
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
let eventSource;
const initSSE = () => {
// 创建SSE链接
eventSource = new EventSource("http://地址");
// 接受服务器消息
eventSource.onmessage = (event) => {
console.log("收到消息内容是:", event.data);
};
eventSource.onerror = (event) => {
console.error("SSE 连接出错:", event.error);
if (event.readyState === EventSource.CLOSED) {
// 连接已关闭,尝试重新连接
// 使用指数退避算法逐渐增加重连的时间间隔
let reconnectInterval = 3000; // 初始重连间隔为3秒
reconnectInterval *= 2; // 指数增加重连间隔
setTimeout(function () {
initSSE(url);
}, reconnectInterval);
}
};
};
// 关闭链接
eventSource && eventSource.close();
initSSE();
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">
评论记录:
回复评论: