WebSocket介绍
项目使用场景
开发uniapp项目时,需要进行实时通信,比如聊天等。需要封装一个工具类,统一对socket进行管理。
uniapp websocket官方文档:https://uniapp.dcloud.net.cn/api/request/websocket.html
代码解释
- 采用单例模式,业务直接引入。
- 主要是通过事件触发的形式,即收到特定事件,触发对应触发器,前端引入后,只需要添加相对应事件监听器即可。
注意:在收到数据时,已经默认使用JSON去解析了一次
代码
主要通过消息通信进行操作,这里定义了一个消息实体模板socketMessageTemplate
功能不齐全,大家按需使用,欢迎补充和讨论
// websocket.js
// 基础消息模板
const socketMessageTemplate = {
type: "", // 消息类型
timestamp: null, // 时间戳
data: null, // 数据
from: "", // 发送者
to: "", // 接收者
msg: "", // 消息内容
};
// WebSocket 管理类
class WebSocketManager {
constructor({
// 最大重连次数
maxReconnectAttempts = 3,
// 重连间隔(毫秒)
reconnectInterval = 5000,
// 心跳间隔(毫秒)
heartbeatInterval = 60000,
} = {}) {
this.socket = null; // WebSocket 实例
this.listeners = {}; // 事件监听器
this.status = "closed"; // 连接状态:closed, connecting, connected
this.heartbeatTimer = null; // 心跳定时器
this.reconnectAttempts = 0; // 当前重连次数
this.maxReconnectAttempts = maxReconnectAttempts; // 最大重连次数
this.reconnectInterval = reconnectInterval; // 重连间隔(毫秒)
this.heartbeatInterval = heartbeatInterval; // 心跳间隔(毫秒)
this.url = null; // WebSocket 地址
this.isManualClose = false; // 是否为用户主动关闭
}
/**
* 连接 WebSocket
* @param {string} url WebSocket 地址
* @param {function} successCallback 连接成功回调函数
*/
connect(url, successCallback = () => { }) {
if (!url) {
console.error("WebSocket 地址不能为空");
return;
}
/* if (this.status === "connecting") {
console.warn("WebSocket 正在连接中,请稍后再试");
return;
} */
if (this.status === "connected") {
console.warn("WebSocket 已连接,请勿重复连接");
return;
}
this.url = url;
this.status = "connecting";
this.isManualClose = false; // 重置手动关闭标志
this.socket = uni.connectSocket({
url,
success: () => {
successCallback();
console.log("WebSocket 连接请求已发送");
},
fail: (error) => {
console.error("WebSocket 连接失败", error);
this.status = "closed";
this.handleReconnect();
},
});
this.initEventHandlers();
}
/**
* 初始化 WebSocket 事件
*/
initEventHandlers() {
uni.onSocketOpen(() => {
if (this.status === "connected") return; // 避免重复发送初始化消息
console.log("WebSocket 连接成功");
this.status = "connected";
this.reconnectAttempts = 0;
this.startHeartbeat();
this.triggerEvent("open");
});
uni.onSocketMessage(({ data }) => {
try {
const message = JSON.parse(decodeURIComponent(data));
this.triggerEvent("message", message);
} catch (err) {
console.error("消息解析失败", err);
}
});
uni.onSocketError((err) => {
console.error("WebSocket 错误", err);
this.triggerEvent("error", err);
});
uni.onSocketClose(() => {
console.log("WebSocket 已关闭");
this.status = "closed";
this.stopHeartbeat();
this.triggerEvent("close");
if (!this.isManualClose) {
this.handleReconnect();
}
});
}
/**
* 处理重连逻辑
*/
handleReconnect() {
if (this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnectAttempts++;
console.log(`尝试第 ${this.reconnectAttempts} 次重连...`);
this.status = "connecting";
setTimeout(() => this.connect(this.url), this.reconnectInterval);
} else {
this.status = "fail";
console.error("重连次数已达上限,停止重连");
}
}
/**
* 启动心跳机制
*/
startHeartbeat() {
this.stopHeartbeat(); // 避免重复启动
this.heartbeatTimer = setInterval(() => {
if (this.status === "connected") {
this.sendMessage({ type: "HEARTBEAT" });
// console.log('心跳检测中...');
}
}, this.heartbeatInterval);
}
/**
* 停止心跳机制
*/
stopHeartbeat() {
if (this.heartbeatTimer) {
clearInterval(this.heartbeatTimer);
this.heartbeatTimer = null;
}
}
/**
* 添加事件监听器
* @param {string} event 事件类型
* @param {function} callback 回调函数
*/
addListener(event, callback) {
if (!this.listeners[event]) {
this.listeners[event] = [];
}
this.listeners[event].push(callback);
}
/**
* 移除事件监听器
* @param {string} event 事件类型
* @param {function} callback 回调函数
*/
removeListener(event, callback) {
if (this.listeners[event]) {
this.listeners[event] = this.listeners[event].filter(
(cb) => cb !== callback
);
}
}
/**
* 移除所有事件监听器
*/
removeAllListeners() {
this.listeners = {}; // 清空所有事件监听器
}
/**
* 触发事件
* @param {string} event 事件类型
* @param {any} data 回调数据
*/
triggerEvent(event, data = null) {
(this.listeners[event] || []).forEach((callback) => callback(data));
}
/**
* 发送消息 异步
* @param {object} message 消息内容
*/
async sendMessageWithRetry(message, retryCount = 3, delayMs = 1000) {
for (let attempt = 0; attempt < retryCount; attempt++) {
if (this.status === "connected") {
// 处理type类型全部改为大写
message.type = message.type.toUpperCase();
const payload = JSON.stringify({
...socketMessageTemplate,
...message,
timestamp: Date.now(),
});
uni.sendSocketMessage({
data: payload,
success: () => {
console.log("消息发送成功", payload)
},
fail: (err) => console.error("消息发送失败", err),
});
return;
}
console.warn(`WebSocket 未连接,重试中 (${attempt + 1}/${retryCount})...`);
await new Promise((resolve) => setTimeout(resolve, delayMs));
}
console.error("多次尝试发送消息失败,WebSocket 未连接");
}
/**
* 发送消息
* @param {object} message 消息内容
*/
sendMessage(message) {
if (this.status === "connecting") {
console.error("WebSocket 正在连接中,无法发送消息");
return;
}
if (this.status === "fail") {
console.error("WebSocket 连接已失败,无法发送消息");
return;
}
if (this.status !== "connected") {
console.error("WebSocket 未连接,无法发送消息");
this.handleReconnect(); // 如果未连接,尝试重连
return;
}
message.type = message.type.toUpperCase();
const payload = JSON.stringify({
...socketMessageTemplate,
...message,
timestamp: Date.now(),
});
uni.sendSocketMessage({
data: payload,
success: () => {
// console.log("消息发送成功", payload)
},
fail: (err) => {
console.error("消息发送失败", err);
},
});
}
/**
* 关闭 WebSocket 连接
*/
close() {
if (this.status === "closed") {
console.warn("WebSocket 未连接,无需关闭");
return;
}
if (this.status === "fail" || this.status === "connecting") {
console.warn("WebSocket 当前状态无法正常关闭,重置状态");
this.status = "closed"; // 强制重置状态
this.isManualClose = true;
return;
}
this.isManualClose = true; // 设置为手动关闭
uni.closeSocket({
success: () => {
console.log("WebSocket 连接已关闭");
this.status = "closed";
this.stopHeartbeat();
this.removeAllListeners(); // 关闭时移除所有事件监听器
},
fail: (err) => console.error("关闭 WebSocket 失败", err),
});
}
}
// 单例模式
const WebSocketInstance = new WebSocketManager();
export default WebSocketInstance;
举例
这里用uniapp 举例
<template>
<view class="container">
<view>
<text>WebSocket 通信示例</text>
</view>
<view>
<button @click="connectWebSocket">连接 WebSocket</button>
<button @click="sendMessage">发送消息</button>
<button @click="closeWebSocket">关闭 WebSocket</button>
</view>
<view>
<text>接收到的消息:</text>
<text>{
{ receivedMessage }}</text>
</view>
</view>
</template>
<script setup>
import { ref } from "vue";
import WebSocketInstance from "@/utils/websocket"; // 引入封装的 WebSocket 管理类
import { useWebSocket } from '@/utils/common';
const { url } = useWebSocket(12345);
// 接收到的消息
const receivedMessage = ref("");
// 连接 WebSocket
const connectWebSocket = () => {
WebSocketInstance.connect(url);
// 添加事件监听
WebSocketInstance.addListener("open", () => {
// console.log("WebSocket 已连接");
});
WebSocketInstance.addListener("message", (data) => {
// console.log("接收到消息:", data);
receivedMessage.value = JSON.stringify(data);
});
WebSocketInstance.addListener("close", () => {
// console.log("WebSocket 已关闭");
});
WebSocketInstance.addListener("error", (error) => {
console.error("WebSocket 错误", error);
});
};
// 发送消息
const sendMessage = () => {
WebSocketInstance.sendMessageWithRetry({
type: "CHAT",
from: "user1",
to: "SYSTEM",
msg: "你好,这是一条测试消息!",
});
};
// 关闭 WebSocket
const closeWebSocket = () => {
WebSocketInstance.close();
};
</script>
<style>
.container {
padding: 20px;
}
button {
margin: 10px 0;
}
</style>
WebSocket介绍
WebSocket是一种在Web浏览器和Web服务器之间进行实时双向通信的协议。与HTTP协议不同的是,WebSocket在建立连接后,不需要通过发起HTTP请求来获取数据,而是可以直接在连接上发送和接收数据。
WebSocket的特点包括以下几个方面:
- 实时性:WebSocket可以在客户端和服务器之间实时地发送消息,而不需要等待服务器响应或轮询。
- 双向通信:WebSocket支持客户端和服务器之间的双向通信,可以在任一方向上发送或接收消息。
- 高效性:WebSocket使用TCP协议作为传输层协议,相较于HTTP协议,可以减少通信的开销和延迟。
- 跨域支持:WebSocket通过在HTTP握手过程中发送特定的头部信息来支持跨域通信。
- 推送功能:WebSocket支持服务器主动向客户端推送消息,不再需要客户端主动发送请求。
WebSocket的工作原理是通过建立一个持久化的连接,使用HTTP协议完成初始的握手过程,然后通过升级协议从HTTP协议切换到WebSocket协议。一旦建立连接,客户端和服务器可以通过发送消息来进行通信,而不需要再次发送HTTP请求。
WebSocket在实时聊天、实时数据更新、实时游戏等场景中具有广泛应用。由于其高效的双向通信特性,WebSocket也在移动应用和物联网等领域得到了广泛的应用。
后端Websocket配合使用参考我博客其他文章,好久没写博文了