157 lines
3.8 KiB
JavaScript
157 lines
3.8 KiB
JavaScript
/**
|
|
* ws-client.js — WebSocket 客户端单例
|
|
* 全站共享一个 WS 连接,提供自动重连、消息分发、状态通知
|
|
*/
|
|
var WsClient = (function() {
|
|
var ws = null;
|
|
var url = '';
|
|
var msgCallbacks = [];
|
|
var statusCallbacks = [];
|
|
var reconnectTimer = null;
|
|
var reconnectDelay = 3000;
|
|
var status = 'disconnected'; // 'connected' | 'disconnected' | 'connecting'
|
|
|
|
function setStatus(s) {
|
|
if (status !== s) {
|
|
status = s;
|
|
for (var i = 0; i < statusCallbacks.length; i++) {
|
|
statusCallbacks[i](s);
|
|
}
|
|
}
|
|
}
|
|
|
|
function connect(wsUrl) {
|
|
if (ws && (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING)) {
|
|
return;
|
|
}
|
|
|
|
url = wsUrl;
|
|
setStatus('connecting');
|
|
|
|
try {
|
|
ws = new WebSocket(url);
|
|
} catch (e) {
|
|
setStatus('disconnected');
|
|
scheduleReconnect();
|
|
return;
|
|
}
|
|
|
|
ws.onopen = function() {
|
|
setStatus('connected');
|
|
if (reconnectTimer) {
|
|
clearTimeout(reconnectTimer);
|
|
reconnectTimer = null;
|
|
}
|
|
};
|
|
|
|
ws.onclose = function() {
|
|
setStatus('disconnected');
|
|
ws = null;
|
|
scheduleReconnect();
|
|
};
|
|
|
|
ws.onerror = function() {
|
|
// onclose will fire after onerror
|
|
};
|
|
|
|
ws.onmessage = function(e) {
|
|
var raw = e.data;
|
|
|
|
// 写入监控日志
|
|
if (typeof MonitorRing !== 'undefined') {
|
|
MonitorRing.push('↓', raw);
|
|
}
|
|
|
|
// 尝试解析 JSON
|
|
var data = null;
|
|
try {
|
|
data = JSON.parse(raw);
|
|
} catch (ex) {
|
|
// 非 JSON 消息也分发原始数据
|
|
}
|
|
|
|
// 分发给所有注册的回调
|
|
for (var i = 0; i < msgCallbacks.length; i++) {
|
|
try {
|
|
msgCallbacks[i](data || raw, raw);
|
|
} catch (ex) {
|
|
console.error('WsClient msg callback error:', ex);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
function scheduleReconnect() {
|
|
if (reconnectTimer) return;
|
|
reconnectTimer = setTimeout(function() {
|
|
reconnectTimer = null;
|
|
if (status === 'disconnected') {
|
|
connect(url);
|
|
}
|
|
}, reconnectDelay);
|
|
}
|
|
|
|
function send(data) {
|
|
var raw = '';
|
|
if (typeof data === 'object') {
|
|
raw = JSON.stringify(data);
|
|
} else {
|
|
raw = String(data);
|
|
}
|
|
|
|
// 写入监控日志
|
|
if (typeof MonitorRing !== 'undefined') {
|
|
MonitorRing.push('↑', raw);
|
|
}
|
|
|
|
if (!ws || ws.readyState !== WebSocket.OPEN) {
|
|
console.error('WsClient: not connected, cannot send');
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
ws.send(raw);
|
|
return true;
|
|
} catch (e) {
|
|
console.error('WsClient: send failed', e);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function onMessage(cb) {
|
|
msgCallbacks.push(cb);
|
|
}
|
|
|
|
function onStatusChange(cb) {
|
|
statusCallbacks.push(cb);
|
|
// 立即通知当前状态
|
|
cb(status);
|
|
}
|
|
|
|
function getStatus() {
|
|
return status;
|
|
}
|
|
|
|
function disconnect() {
|
|
if (reconnectTimer) {
|
|
clearTimeout(reconnectTimer);
|
|
reconnectTimer = null;
|
|
}
|
|
if (ws) {
|
|
ws.onclose = null; // 阻止自动重连
|
|
ws.close();
|
|
ws = null;
|
|
}
|
|
setStatus('disconnected');
|
|
}
|
|
|
|
return {
|
|
connect: connect,
|
|
send: send,
|
|
onMessage: onMessage,
|
|
onStatusChange: onStatusChange,
|
|
getStatus: getStatus,
|
|
disconnect: disconnect
|
|
};
|
|
})();
|