在移动浏览器中接收socket.io或WebSockets消息时,无法预测的延迟/滞后

发布于 2025-01-27 16:52:50 字数 3159 浏览 3 评论 0原文

我正在制作类似于Snake的多人网络游戏。目标是每200ms向所有客户端广播游戏的更新,以响应客户立即绘制Gamestate,以大致达到恒定的5FPS更新率。我使用WebSocket实施了此功能,并且在连接到LAN的多个笔记本电脑上的预期工作,但是每当我加入移动浏览器(Chrome 101.0.4951.41)上的Android 11上的Chrome 11)时,Framerate不一致,并且游戏似乎滞后(例如平均阶段,仍将是200ms,但两个框架相距100毫米,然后是300ms,直到下一帧)。

最初,我的后端是使用。我验证了服务器相距199-201ms的发送消息,并尝试编写新的后端,以使用Node.js进行最低限度(每200ms广播)。我消除了所有客户端逻辑,除了记录经过的时间并计算最小值,平均值,最大和标准偏差。该问题持续存在,以移动设备上的高范围和标准偏差表示。我切换到使用socket.io,以防它是一个Websockets问题。问题仍然存在。

代码

完整的代码如下。我正在使用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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文