WebSocket简介
WebSocket广泛应用于需要实时双向通信的场景,如聊天室、实时通知系统等。在这些场景中,服务器需要主动向客户端推送数据,而客户端也需要及时向服务器发送数据。传统的HTTP协议无法实现这种实时双向通信,而WebSocket协议则能够很好地满足这些需求。
具体实现
在一个Vue3项目中,封装了一个WebSocket工具模块。此工具能够在WebSocket发生错误或意外中断的情况下,自动触发重连逻辑。这一重连过程会持续进行,直至成功恢复与服务器的连接为止,从而确保实时通信的连续性和稳定性。
//设置
interface SocketOptions {
//心跳间隔
heartbeatInterval?: number;
//超时重传
reconnectInterval?: number;
}
class Socket {
//路径
url: string;
ws: WebSocket | null = null;
opts: SocketOptions;
listeners: { [key: string]: Function[] } = {};
//心跳间隔
heartbeatInterval: number | null = null;
// 重连间隔
reconnectInterval: number | null = null;
isCloseSoket: Boolean = false; // 是否是手动关闭webSoket
//构造函数
constructor(url: string, opts: SocketOptions = {}) {
this.url = url;
this.opts = {
//心跳间隔
heartbeatInterval: 30000,
//超时重传
reconnectInterval: 5000,
...opts
};
this.init();
}
//初始化
init() {
this.ws = new WebSocket(this.url);
this.ws.onopen = this.onOpen.bind(this);
this.ws.onmessage = this.onMessage.bind(this);
this.ws.onerror = this.onError.bind(this);
this.ws.onclose = this.onClose.bind(this);
}
//打开
onOpen(event: Event) {
console.log('WebSocket opened:', event);
this.reconnectInterval && clearInterval(this.reconnectInterval);
this.isCloseSoket = false;
this.startHeartbeat();
this.emit('open', event);
}
//收到的WebSocket消息
onMessage(event: MessageEvent) {
// console.log('WebSocket message received:', event.data);
this.emit('message', event.data);
}
//错误
onError(event: Event) {
console.error('WebSocket error:', event);
this.emit('error', event);
}
//重连逻辑中,在连接失败后自动重新连接
onClose(event: CloseEvent) {
console.log('WebSocket closed:', event);
this.stopHeartbeat();
this.emit('close', event);
this.reconnectInterval && clearInterval(this.reconnectInterval);
this.reconnectInterval = null;
// 手动关闭不触发重连
if (this.isCloseSoket) {
return;
}
//重连逻辑中,只要没连上就一直重连
this.reconnectInterval = setInterval(() => {
console.log('WebSocket正在重连');
this.init();
}, this.opts.reconnectInterval);
}
//开始心跳检测
startHeartbeat() {
if (!this.opts.heartbeatInterval) return;
this.heartbeatInterval = window.setInterval(() => {
if (this.ws?.readyState === WebSocket.OPEN) {
this.ws.send('heartbeat');
}
}, this.opts.heartbeatInterval);
}
//停止心跳检测
stopHeartbeat() {
if (this.heartbeatInterval) {
clearInterval(this.heartbeatInterval);
this.heartbeatInterval = null;
}
}
//发送消息
send(data: string) {
console.log('send', data);
if (this.ws?.readyState === WebSocket.OPEN) {
this.ws.send(data);
} else {
console.error('WebSocket is not open. Cannot send:', data);
}
}
//事件监听器注册功能的实现
on(event: string, callback: Function) {
if (!this.listeners[event]) {
this.listeners[event] = [];
}
this.listeners[event].push(callback);
}
//从事件监听器中移除
off(event: string) {
if (this.listeners[event]) {
delete this.listeners[event];
}
}
//在事件监听器中触发一个指定的事件
emit(event: string, data: any) {
this.listeners[event]?.forEach(callback => callback(data));
}
}
export function useSocket(url: string, opts?: SocketOptions) {
const socket = new Socket(url, opts);
// 清除所有监听并断开连接
function clearSocket() {
socket.off('open');
socket.off('message');
socket.off('error');
socket.off('close');
socket.stopHeartbeat();
socket.isCloseSoket = true;
socket.ws?.close(); // 关闭WebSocket连接
console.log('clearSocket-断开连接');
}
return {
socket,
send: socket.send.bind(socket),
on: socket.on.bind(socket),
off: socket.off.bind(socket),
clearSocket
};
}
export function getSocketUrl(url: string) {
// 获取webSocket基地址
let host = location.host;
let env = import.meta.env.VITE_APP_ENV;
let devSocketBaseUrl = 'ws:// XXXXXXXX'; // 开发环境soket基地址
let socketBaseUrl = env == 'dev' ? devSocketBaseUrl : 'ws://' + host;
let socketUrl = socketBaseUrl + url;
return socketUrl;
}
在VUE3中的应用
<script setup lang="ts">
import { useSocket, getSocketUrl } from '@/utils/webSocket';
let webSocketRef: any = ref<WebSocket | null>(null);
// 初始化webSocket
function initWebSocket() {
clearWebSocket();
let socketUrl = getSocketUrl(`/XXX/XXX`);
webSocketRef.value = useSocket(socketUrl);
let { socket, send } = webSocketRef.value;
socket.on('message', (msg: any) => {
console.log('msg:', msg)
});
// 发送数据
send('发送内容')
}
<script/>