功能
- websocket基本的常用功能(连接、断开连接、发送/接收消息)
- 心跳检测
- websocket断开重连
前端代码:WebSocket.js
class WebSocketClient {
constructor(options = {}) {
this.url = options.url || '';
this.protocols = options.protocols || [];
this.heartbeatInterval = options.heartbeatInterval || 30000; // 心跳间隔,默认30秒
this.reconnectInterval = options.reconnectInterval || 5000; // 重连间隔,默认5秒
this.reconnectAttempts = options.reconnectAttempts || 5; // 重连尝试次数
this.connectTimeout = options.connectTimeout || 10000; // 连接超时时间,默认10秒
this.heartbeatTimer = null;
this.reconnectTimer = null;
this.reconnectCount = 0;
this.ws = null;
this.status = 'closed'; // closed, connecting, connected
this.messageCallbacks = [];
this.connectPromise = null;
}
// 连接WebSocket
connect() {
if (this.status === 'connecting') {
return this.connectPromise;
}
this.status = 'connecting';
this.connectPromise = new Promise((resolve, reject) => {
this.ws = uni.connectSocket({
url: this.url,
protocols: this.protocols,
success: () => {
console.log('WebSocket连接创建成功');
},
fail: (error) => {
console.error('WebSocket连接创建失败:', error);
this.reconnect().then(resolve).catch(reject);
},
});
this.ws.onOpen(() => {
console.log('WebSocket连接已打开');
this.status = 'connected';
this.reconnectCount = 0;
this.startHeartbeat();
resolve();
});
this.ws.onClose(() => {
console.log('WebSocket连接关闭');
const wasConnecting = this.status === 'connecting';
this.status = 'closed';
this.stopHeartbeat();
this.reconnect().then(resolve).catch(reject);
});
this.ws.onError((error) => {
console.error('WebSocket错误:', error);
const wasConnecting = this.status === 'connecting';
this.status = 'closed';
this.reconnect().then(resolve).catch(reject);
});
this.ws.onMessage((res) => {
console.log('收到消息:', res);
// 处理接收到的消息
const data = res.data;
// 忽略心跳检测的响应
if (!data.includes('心跳检测 pong')) {
this.messageCallbacks.forEach((callback) => callback(data));
}
});
// 设置连接超时
setTimeout(() => {
if (this.status === 'connecting') {
const timeoutError = new Error('WebSocket连接超时');
if (this.reconnectCount >= this.reconnectAttempts) {
reject(timeoutError);
} else {
this.reconnect().then(resolve).catch(reject);
}
}
}, this.connectTimeout);
});
return this.connectPromise;
}
// 发送消息
send(data) {
if (this.status !== 'connected') {
console.warn('WebSocket未连接');
return;
}
this.ws.send({
data: typeof data === 'string' ? data : JSON.stringify(data),
fail: (error) => {
console.error('消息发送失败:', error);
},
});
}
// 监听消息
onMessage(callback) {
if (typeof callback === 'function') {
this.messageCallbacks.push(callback);
}
}
// 开始心跳
startHeartbeat() {
this.stopHeartbeat();
this.heartbeatTimer = setInterval(() => {
this.send(`心跳检测 ping ${new Date().toLocaleString()}`);
}, this.heartbeatInterval);
}
// 停止心跳
stopHeartbeat() {
if (this.heartbeatTimer) {
clearInterval(this.heartbeatTimer);
this.heartbeatTimer = null;
}
}
// 重连
reconnect() {
// 检查是否达到最大重连次数
if (this.reconnectCount >= this.reconnectAttempts) {
console.log('达到最大重连次数,停止重连');
const error = new Error('WebSocket连接失败,已达到最大重连次数');
return Promise.reject(error);
}
if (this.reconnectTimer) {
clearTimeout(this.reconnectTimer);
}
return new Promise((resolve, reject) => {
this.reconnectTimer = setTimeout(() => {
console.log(`第${this.reconnectCount + 1}次重连尝试`);
this.reconnectCount++;
this.connect()
.then(resolve)
.catch((error) => {
// 只有在达到最大重连次数时才reject
if (this.reconnectCount >= this.reconnectAttempts) {
reject(new Error('WebSocket连接失败,已达到最大重连次数'));
} else {
console.log(
`重连失败,剩余${this.reconnectAttempts - this.reconnectCount}次重试机会`
);
// 继续尝试重连
this.reconnect().then(resolve).catch(reject);
}
});
}, this.reconnectInterval);
});
}
// 关闭连接
close() {
this.status = 'closed';
this.stopHeartbeat();
if (this.reconnectTimer) {
clearTimeout(this.reconnectTimer);
this.reconnectTimer = null;
}
if (this.ws) {
this.ws.close({
success: () => {
console.log('WebSocket已手动关闭');
},
});
}
}
}
export default WebSocketClient;
参数
url (String)
- 必填项
- WebSocket 服务器的完整地址
- 例如:'ws://example.com' 或 'wss://example.com'(安全连接)
protocols (Array)
- 可选,默认 []
- WebSocket 协议数组
- 用于指定子协议,服务器可以根据协议选择合适的处理方式
heartbeatInterval (Number)
- 可选,默认 30000(30秒)
- 心跳检测的时间间隔,单位:毫秒
- 用于保持连接活跃,防止连接因超时被关闭
reconnectInterval (Number)
- 可选,默认 5000(5秒)
- 重连尝试的时间间隔,单位:毫秒
- 连接断开后,每次重连之间的等待时间
reconnectAttempts (Number)
- 可选,默认 5
- 最大重连尝试次数
- 达到最大次数后将停止重连并抛出错误
connectTimeout (Number)
- 可选,默认 10000(10秒)
- 连接超时时间,单位:毫秒
- 如果在指定时间内未建立连接,将触发超时错误
前端使用
<template>
<div> </div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
import WebSocketClient from '@/utils/WebSocket';
const ws = ref(null);
async function connectWebSocket() {
try {
// 创建 WebSocket 实例
ws.value = new WebSocketClient({
url: 'ws://192.168.1.4:4500',
protocols: [],
heartbeatInterval: 1000 * 2,
reconnectInterval: 5000,
reconnectAttempts: 2,
connectTimeout: 10000,
});
// 连接
await ws.value.connect();
console.log('WebSocket连接成功');
// 连接成功后注册消息处理
ws.value.onMessage((data) => {
console.log('页面收到消息:', data);
});
// 发送测试消息
ws.value.send('Hello Server!');
} catch (error) {
console.error('WebSocket连接失败:', error);
uni.showToast({
title: error.message || '连接失败,请检查网络',
icon: 'none',
duration: 3000,
});
}
}
// 在组件挂载时连接
onMounted(() => {
connectWebSocket();
});
// 在组件卸载时清理连接
onUnmounted(() => {
if (ws.value) {
ws.value.close();
ws.value = null;
}
});
</script>
<style lang="scss" scoped></style>
后端代码
nodejs示例:
const WebSocket = require('ws');
const os = require('os');
// 获取本机IP地址
function getLocalIP() {
const interfaces = os.networkInterfaces();
for (let devName in interfaces) {
const iface = interfaces[devName];
for (let i = 0; i < iface.length; i++) {
const alias = iface[i];
if (alias.family === 'IPv4' && alias.address !== '127.0.0.1' && !alias.internal) {
return alias.address;
}
}
}
return 'localhost';
}
const PORT = 4500;
const HOST = getLocalIP();
// 创建 WebSocket 服务器
const wss = new WebSocket.Server({ port: PORT });
// 监听连接事件
wss.on('connection', function connection(ws) {
console.log('新的客户端已连接');
// 监听消息事件
ws.on('message', function incoming(message) {
// console.log('收到消息:', message.toString());
let msg = message.toString();
console.log('收到消息:', msg);
// 和前端约定好的心跳包,如果消息是ping,则返回pong
if (msg.includes('心跳检测 ping')) {
ws.send(`心跳检测 pong ${new Date().toLocaleString()}`);
} else {
// 向客户端发送消息
ws.send(`服务器收到消息:${msg}`);
}
});
// 监听关闭事件
ws.on('close', function close() {
console.log('客户端已断开连接');
});
// 发送欢迎消息
ws.send('欢迎连接到 WebSocket 服务器!');
});
console.log(`WebSocket 服务器已启动,连接地址: ws://${HOST}:${PORT}`);