export default {
|
connection: null, // SignalR 连接实例
|
hubProxy: null, // 缓存 Hub 代理(避免重复创建)
|
connectionStatus: 0, // 连接状态:0-断开 1-连接中 2-已连接 3-重连中
|
retryCount: 0, // 重连计数
|
MAX_RETRY: 5, // 最大重连次数
|
heartbeatTimer: null, // 心跳定时器
|
uni$: null, // 缓存 uni-app 的 $,避免冲突
|
|
// 初始化 SignalR 连接(修复重连、状态、冲突问题)
|
async initSignalR(options = {}) {
|
const {
|
onLog = () => {},
|
onConnected = () => {},
|
onDisconnected = () => {},
|
onMessage = () => {},
|
hubName = 'myhub1', // 默认 Hub 名称
|
serverUrl = 'http://192.168.0.121:10000' // 默认服务器地址
|
} = options;
|
|
// 防止重复初始化
|
if (this.connectionStatus === 1 || this.connectionStatus === 3) {
|
onLog('已有连接/重连操作中,跳过重复初始化');
|
return;
|
}
|
|
// 标记为连接中状态
|
this.connectionStatus = 1;
|
this.uni$ = window.$; // 缓存 uni-app 的 $
|
|
try {
|
// 加载 jQuery + SignalR
|
await this.loadScript("/static/js/jquery-1.12.4.min.js", 'jq-script', onLog);
|
if (!window.$) throw new Error('jQuery 未挂载到 window.$');
|
|
await this.loadScript("/static/js/jquery.signalR.min.js", 'sr-script', onLog);
|
if (!window.$.hubConnection) throw new Error('SignalR 未挂载');
|
|
// 4. 避免 $ 冲突:临时重命名 jQuery 的 $ 为 jq$
|
window.jq$ = window.$;
|
window.$ = this.uni$; // 恢复 uni-app 的 $
|
|
// 创建连接实例
|
if (this.connection) {
|
try { this.connection.stop(); } catch (e) {}
|
this.connection = null;
|
}
|
this.connection = window.jq$.hubConnection(serverUrl);
|
|
this.connection.clientProtocol = '1.5';
|
this.connection.disconnectTimeout = 30000;
|
this.connection.keepAliveWarnAt = 20000;
|
|
// 缓存 Hub 代理
|
this.hubProxy = this.connection.createHubProxy(hubName);
|
onLog(`创建 Hub 代理:${hubName}`);
|
|
// 监听服务端消息
|
this.hubProxy.on('receiveMessage', (user, message) => {
|
onLog(`收到服务端消息:${user} - ${message}`);
|
onMessage({ user, message });
|
});
|
|
// 监听连接状态变化
|
this.connection.stateChanged((state) => {
|
const stateNames = { 0: '断开', 1: '连接中', 2: '已连接', 3: '重连中' };
|
onLog(`连接状态:${stateNames[state.oldState]} → ${stateNames[state.newState]}`);
|
|
this.connectionStatus = state.newState;
|
|
// 连接成功
|
if (state.newState === 2) {
|
this.retryCount = 0; // 重置重连计数
|
onConnected(this.connection.id);
|
this.startHeartbeat(onLog, hubName); // 启动心跳
|
}
|
// 连接断开(处理重连)
|
else if (state.newState === 0) {
|
this.stopHeartbeat(); // 停止心跳
|
onDisconnected();
|
|
// 自动重连(未超过最大次数)
|
if (this.retryCount < this.MAX_RETRY) {
|
this.retryCount++;
|
const delay = this.retryCount * 5000; // 指数退避:5s,10s,15s...
|
onLog(`连接断开,${delay/1000}秒后自动重连(${this.retryCount}/${this.MAX_RETRY})`);
|
setTimeout(() => {
|
this.initSignalR(options); // 重连时复用原配置
|
}, delay);
|
} else {
|
onLog(`重连次数耗尽(${this.MAX_RETRY}次),请手动重连`);
|
this.connectionStatus = 0;
|
}
|
}
|
});
|
|
// 启动连接
|
this.connection.start({
|
transport: ['webSocket', 'serverSentEvents' ,'longPolling'], // 2.2.2 版本正确写法
|
jsonp: false // 关闭 JSONP,避免跨域问题
|
}).done(() => {
|
onLog(`连接成功!ID:${this.connection.id},传输方式:${this.connection.transport.name || this.connection.transport}`);
|
}).fail((error) => {
|
this.connectionStatus = 0;
|
onLog(`连接失败:${error.message}`, 'error');
|
|
// 首次连接失败也重连
|
if (this.retryCount < this.MAX_RETRY) {
|
this.retryCount++;
|
setTimeout(() => this.initSignalR(options), 5000);
|
}
|
});
|
|
} catch (e) {
|
this.connectionStatus = 0;
|
onLog(`初始化失败:${e.message}`, 'error');
|
}
|
},
|
|
// 启动心跳(避免连接被服务器断开)
|
startHeartbeat(onLog, hubName) {
|
// 清除旧定时器
|
this.stopHeartbeat();
|
|
// 每20秒发送心跳
|
this.heartbeatTimer = setInterval(() => {
|
if (this.connectionStatus !== 2) return; // 仅在已连接时发送
|
|
this.hubProxy.invoke('KeepAlive')
|
.done(() => onLog('心跳发送成功'))
|
.fail(err => {
|
onLog(`心跳失败:${err.message}`, 'error');
|
// 心跳失败触发重连
|
if (err.message.includes('Connection has not been fully initialized')) {
|
this.retryCount = 0;
|
this.initSignalR({ onLog, hubName });
|
}
|
});
|
}, 20000);
|
},
|
|
// 停止心跳定时器
|
stopHeartbeat() {
|
if (this.heartbeatTimer) {
|
clearInterval(this.heartbeatTimer);
|
this.heartbeatTimer = null;
|
}
|
},
|
|
// 调用服务端方法
|
sendMessage(user, message, hubName = 'myhub1') {
|
return new Promise((resolve, reject) => {
|
// 状态校验(
|
if (!this.connection || this.connectionStatus !== 2) {
|
const errMsg = '未连接到服务器(当前状态:' + this.connectionStatus + ')';
|
onLog(`发送失败:${errMsg}`);
|
resolve({ success: false, msg: errMsg });
|
return;
|
}
|
|
// 使用缓存的 Hub 代理
|
const targetProxy = this.hubProxy || this.connection.createHubProxy(hubName);
|
|
// 发送消息 (只有WebSocket模式可用)
|
targetProxy.invoke('SendMessage', user, message)
|
.done(() => {
|
resolve({ success: true, msg: '消息发送成功' });
|
})
|
.fail((error) => {
|
const errMsg = '发送失败:' + error.message;
|
onLog(`${errMsg}`);
|
// 若因连接未初始化失败,触发重连
|
if (error.message.includes('Connection has not been fully initialized')) {
|
this.retryCount = 0;
|
this.initSignalR({ onLog: console.log });
|
}
|
resolve({ success: false, msg: errMsg });
|
});
|
});
|
},
|
|
// 加载脚本
|
loadScript(path, id, onLog) {
|
return new Promise((resolve, reject) => {
|
// 绝对路径
|
const fullPath = path.startsWith('/') ? path : `/${path}`;
|
|
// 移除旧脚本
|
const oldScript = document.getElementById(id);
|
if (oldScript) oldScript.remove();
|
|
const script = document.createElement('script');
|
script.id = id;
|
script.src = fullPath;
|
script.async = false; // 同步加载,确保执行顺序
|
script.onload = () => {
|
onLog(`${id} 脚本加载成功(路径:${fullPath})`);
|
resolve();
|
};
|
script.onerror = (err) => {
|
const errMsg = `${id} 加载失败:${err.message}(路径:${fullPath})`;
|
onLog(`${errMsg}`);
|
reject(new Error(errMsg));
|
};
|
document.body.appendChild(script);
|
});
|
},
|
|
// 断开连接
|
disconnect(onLog = () => {}) {
|
this.stopHeartbeat(); // 停止心跳
|
if (this.connection) {
|
try {
|
this.connection.stop();
|
onLog('手动断开连接成功');
|
} catch (e) {
|
onLog(`断开连接失败:${e.message}`);
|
}
|
this.connection = null;
|
this.hubProxy = null;
|
this.connectionStatus = 0;
|
this.retryCount = 0;
|
}
|
}
|
}
|