在移动浏览器中接收socket.io或WebSockets消息时,无法预测的延迟/滞后
我正在制作类似于Snake的多人网络游戏。目标是每200ms向所有客户端广播游戏的更新,以响应客户立即绘制Gamestate,以大致达到恒定的5FPS更新率。我使用WebSocket实施了此功能,并且在连接到LAN的多个笔记本电脑上的预期工作,但是每当我加入移动浏览器(Chrome 101.0.4951.41)上的Android 11上的Chrome 11)时,Framerate不一致,并且游戏似乎滞后(例如平均阶段,仍将是200ms,但两个框架相距100毫米,然后是300ms,直到下一帧)。
代码
完整的代码如下。我正在使用Node.js V18.1.0在Arch Linux 5.17-1-Arch1-1上运行后端。
服务器node.js代码(文件:send-socketio.js)
const express = require('express');
const app = express();
const http = require('http');
const server = http.createServer(app);
const { Server } = require("socket.io");
const io = new Server(server);
app.get('/', (req, res) => {
res.sendFile(__dirname + '/index.html');
});
setInterval(() => { io.emit('update', {}) }, 200);
server.listen(3000, () => {
console.log('listening on *:3000');
});
客户端html和javaScript(文件:index.html)
<!DOCTYPE html>
<html lang="en">
<head>
</head>
<body>
<div id="msgs"></div>
<script src="/socket.io/socket.io.js"></script>
<script>
var dts = [];
const socket = io();
var last_msg_time = null;
function getStandardDeviation(arr) {
const n = arr.length;
if (n == 0) {
return 0;
}
const mean = arr.reduce((a, b) => a + b) / n;
return Math.sqrt(arr.map(x => Math.pow(x - mean, 2)).reduce((a, b) => a + b) / n);
}
function getMean(arr) {
const n = arr.length
if (n == 0) {
return 0;
}
return arr.reduce((a, b) => a + b) / n
}
socket.on('update', function(msg) {
var now = new Date();
if (last_msg_time != null) {
var dt = now - last_msg_time;
dts.push(dt);
let ele = document.getElementById('msgs');
ele.innerHTML = `<ul><li>min:${Math.min(...dts)}</li>
<li>avg:${getMean(dts)}</li>
<li>max:${Math.max(...dts)}</li>
<li>std:${getStandardDeviation(dts)}</li>
</ul>`;
}
last_msg_time = now;
});
</script>
</body>
</html>
结果,
所有三个设备同时在类似的时间内同时运行,显示的结果如下(具有200ms更新频率, ,MS中的所有单元):
客户端1:Local -Host
min:152
avg:200.23478260869564
max:202
std:2.3427239247345715
客户端2:LAN客户端上的其他笔记本电脑
min:167
avg:200
max:212
std:4.78
3:LAN上的手机,Chrome浏览器(勇敢的类似结果)
min:55
avg:200.7
max:374
std:52.14
注意,特别是在移动浏览器上的Delta范围和STDDEV的时间更大。 。所有设备都连接到同一房间中的同一无线LAN,并且非常近。
我认为这不是在处理客户端或服务器端处理消息的瓶颈,因为每个消息都是带有空白的“更新”,并且每200ms每200ms只发送消息。当我将服务器广播时间降低到20ms时,所有设备的平均DT也将降低到约20ms,但是在移动浏览器上,标准偏差保持较高(〜10ms而不是〜1ms)。
如果有人知道这里发生了什么或考虑到可能的解决方案,我将很高兴听到它。目前,我想到的唯一解决方案是向每个客户介绍消息(例如每20ms而不是200ms广播一次,以将标准偏差降低到10ms而不是50ms),但这非常效率低下和黑客。
谢谢!
I'm making a multiplayer web game similar to Snake. The goal is to broadcast a gamestate update from the server to all clients every 200ms, in response to which the clients immediately draw the gamestate to achieve roughly a constant 5fps update rate. I implemented this using Websockets and it worked as expected on multiple laptops connected to my LAN, but whenever I join on my mobile browser (Chrome 101.0.4951.41 on Android 11) the framerate is inconsistent and the game appears to lag (e.g. the average period will still be 200ms, but two frames will be 100ms apart followed by 300ms until the next frame).
Initially, my backend was written in Go using the gobwas/ws package with wsutil
. I verified that the server is sending messages 199-201ms apart, and tried writing a new backend to do the bare minimum (broadcast every 200ms) using node.js. I eliminated all client-side logic except logging the elapsed time and calculating the min, mean, max, and standard deviation. The problem persisted, indicated by high range and standard deviation on the mobile device. I switched to using socket.io in case it was a Websockets problem. The problem persisted.
Code
The complete code is below. I am running the backend using node.js v18.1.0 on Arch Linux 5.17-1-arch1-1.
Server Node.js Code (file: send-socketio.js)
const express = require('express');
const app = express();
const http = require('http');
const server = http.createServer(app);
const { Server } = require("socket.io");
const io = new Server(server);
app.get('/', (req, res) => {
res.sendFile(__dirname + '/index.html');
});
setInterval(() => { io.emit('update', {}) }, 200);
server.listen(3000, () => {
console.log('listening on *:3000');
});
Client HTML and Javascript (file: index.html)
<!DOCTYPE html>
<html lang="en">
<head>
</head>
<body>
<div id="msgs"></div>
<script src="/socket.io/socket.io.js"></script>
<script>
var dts = [];
const socket = io();
var last_msg_time = null;
function getStandardDeviation(arr) {
const n = arr.length;
if (n == 0) {
return 0;
}
const mean = arr.reduce((a, b) => a + b) / n;
return Math.sqrt(arr.map(x => Math.pow(x - mean, 2)).reduce((a, b) => a + b) / n);
}
function getMean(arr) {
const n = arr.length
if (n == 0) {
return 0;
}
return arr.reduce((a, b) => a + b) / n
}
socket.on('update', function(msg) {
var now = new Date();
if (last_msg_time != null) {
var dt = now - last_msg_time;
dts.push(dt);
let ele = document.getElementById('msgs');
ele.innerHTML = `<ul><li>min:${Math.min(...dts)}</li>
<li>avg:${getMean(dts)}</li>
<li>max:${Math.max(...dts)}</li>
<li>std:${getStandardDeviation(dts)}</li>
</ul>`;
}
last_msg_time = now;
});
</script>
</body>
</html>
Results
With all three devices running simultaneously for a similar duration of time, the displayed results were as follows (with 200ms update frequency, all units in ms):
Client 1: localhost
min:152
avg:200.23478260869564
max:202
std:2.3427239247345715
Client 2: other laptop on LAN
min:167
avg:200
max:212
std:4.78
Client 3: mobile phone on LAN, Chrome browser (similar results in Brave)
min:55
avg:200.7
max:374
std:52.14
Note especially how much larger the time delta range and stddev is on the mobile browser. All devices are connected to the same wireless LAN, in the same room and very close together.
I don't think this is a bottleneck in processing the messages on either the client or the server side, since each message is just 'update' with an empty body, and messages are only sent every 200ms. When I decrease the server broadcast period to 20ms, the mean dt also decreases to ~20ms on all devices, but the standard deviation remains comparably high on the mobile browser (~10ms instead of ~1ms).
If anyone has an idea what is going on here or a possible solution in mind, I would be grateful to hear it. Right now the only solution I have in mind is to flood each client with messages (e.g. broadcast every 20ms instead of 200ms to reduce the standard deviation to 10ms instead of 50ms) but this is very inefficient and hacky.
Thanks!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论