javascript - 无法创建稳定的 websocket 实现
问题描述
用例:这在 Android 应用程序的服务器端 (Keystone) 上运行
- 应用程序使用用户的访问令牌连接到套接字
- 应用程序显示连接到套接字的所有其他用户的指示器
- 当用户更改应用程序中的某些数据时,会通过套接字向所有“在线”用户发送强制刷新,以便他们知道获取最新数据
主要问题:
- 它一直有效,直到客户端在间隔之间失去其互联网连接。然后套接字连接被关闭而不是重新打开。
不知道是我的实现有问题还是客户端实现有问题
实现用途:
- https://github.com/websockets/ws
- 更具体地说https://github.com/websockets/ws#how-to-detect-and-close-broken-connections
这是服务器上的实现:
const clients = {};
let wss = null;
const delimiter = '_';
/**
* Clients are stored as "companyId_deviceId"
*/
function getClients() {
return clients;
}
function sendMessage(companyId, msg) {
try {
const clientKey = Object.keys(clients).find((a) => a.split(delimiter)[0] === companyId.toString());
const socketForUser = clients[clientKey];
if (socketForUser && socketForUser.readyState === WebSocket.OPEN) {
socketForUser.send(JSON.stringify(msg));
} else {
console.info(`WEBSOCKET: could not send message to company ${companyId}`);
}
} catch (ex) {
console.error(`WEBSOCKET: could not send message to company ${companyId}: `, ex);
}
}
function noop() { }
function heartbeat() {
this.isAlive = true;
}
function deleteClient(clientInfo) {
delete clients[`${clientInfo.companyId}${delimiter}${clientInfo.deviceId}`];
// notify all clients
forceRefreshAllClients();
}
function createSocket(server) {
wss = new WebSocket.Server({ server });
wss.on('connection', async (ws, req) => {
try {
// verify socket connection
let { query: { accessToken } } = url.parse(req.url, true);
const decoded = await tokenHelper.decode(accessToken);
// add new websocket to clients store
ws.isAlive = true;
clients[`${decoded.companyId}${delimiter}${decoded.deviceId}`] = ws;
console.info(`WEBSOCKET: ➕ Added client for company ${decoded.companyId} and device ${decoded.deviceId}`);
await tokenHelper.verify(accessToken);
// notify all clients about new client coming up
// including the newly created socket client...
forceRefreshAllClients();
ws.on('pong', heartbeat);
} catch (ex) {
console.error('WEBSOCKET: WebSocket Error', ex);
ws.send(JSON.stringify({ type: 'ERROR', data: { status: 401, title: 'invalid token' } }));
}
ws.on('close', async () => {
const location = url.parse(req.url, true);
const decoded = await tokenHelper.decode(location.query.accessToken);
deleteClient({ companyId: decoded.companyId, deviceId: decoded.deviceId });
});
});
// Ping pong on interval will remove the client if the client has no internet connection
setInterval(() => {
Object.keys(clients).forEach((clientKey) => {
const ws = clients[clientKey];
if (ws.isAlive === false) return ws.terminate();
ws.isAlive = false;
ws.ping(noop);
});
}, 15000);
}
function forceRefreshAllClients() {
setTimeout(function () {
Object.keys(clients).forEach((key) => {
const companyId = key.split(delimiter)[0];
sendMessage(companyId, createForcedRefreshMessage());
});
}, 1000);
}
解决方案
推荐阅读
- nginx - Nginx上游和监听同一个端口
- jenkins - 如何在 Jenkins Scripted Pipeline 中设置多行参数化 cron 作业?
- python - 如何修复'错误:asyncio:任务已被破坏,但它正在等待处理!Python中的错误
- python - 在基于城市/位置将函数应用于熊猫时如何避免 20 多个 if 语句
- php - 如何获取网站的元属性并将值存储到变量中?
- colors - 是否有数字是彩色的 MNIST 数据集?
- spring-boot - 使用 pom.xml 禁用声纳规则?
- java - 具有可分页功能的 Spring 数据 jpa 自定义存储库
- excel - 转换为日期格式
- c# - 使用来自 db 的现有实体创建一个实体