用于实时多人游戏的 Node.js UDP

Posted

技术标签:

【中文标题】用于实时多人游戏的 Node.js UDP【英文标题】:Node.js UDP for realtime multi-player game 【发布时间】:2012-07-09 22:22:44 【问题描述】:

我正在使用 node.js 构建一个基于浏览器的实时多人游戏。目前,我让客户端通过 socket.io 将用户输入发送到游戏逻辑服务器,并将游戏世界的快照发送回客户端进行渲染。

以下是代码的简化版本。是否可以使用 UDP 将数据从基于浏览器的客户端发送到服务器,反之亦然?我知道 Node.js 有一个UDP module,但我不确定如何以这种方式实现它。

任何帮助将不胜感激。谢谢。

服务器:

var server = http.createServer(handler).listen(8888);
var iosocket = io.listen(server);

// request/response handler
function handler(req, res)

    ...


iosocket.sockets.on('connection', function(socket)
    console.log("[x] Socket listener started");

    socket.on('msg', function(data)
        console.log( " [-] incoming message);
    );
);

...

iosocket.sockets.emit("message", msg);

客户:

<!DOCTYPE html>
<html>
    <head>
        <title>Test</title>
        <script type="text/javascript" src="/socket.io/socket.io.js"></script>
        <script type="text/javascript">
            socket.on('connect', function()
             console.log("connected to server");
            );

            socket.on('message', function(message)
            console.log('received message');
            );

        </script>
    </head>

    <body>
        <canvas id='canvas'  >Canvas</canvas>
    </body>

</html>

【问题讨论】:

你不能。浏览器仅支持 HTTP。其中一些也支持 WebSockets(其他可以用 Flash XMLSocket 模拟 WebSockets)。就是这样。顺便说一句:为什么是 UDP?使用 TCP(即 WebSockets)更可靠。 @freakish 在阅读了有关 UDP 与 TCP 主题的各种文章后,我追求的是速度而不是可靠性......我猜他们当时不是在谈论基于浏览器的游戏? 嗯,这取决于您实际构建的游戏类型。如果您打算在浏览器中制作另一个 Quake,那么也许您应该忘记它。 :) 另一方面,如果您不需要每秒制作大量快照,那么 TCP 就足够了。最后请注意,一些非常棒的游戏仍然使用 TCP,例如:魔兽世界。 @freakish 魔兽世界几乎不能被视为实时游戏,老实说。如果你正在设计一个带有射弹或移动碰撞盒的游戏,那么你几乎肯定想要某种形式的 UDP,而 TCP 则用于对时间不敏感的东西。如果您要制作一款基于面向方向、按下一个键并等待一秒钟(WoW)进行攻击的游戏,那么 TCP 可能就可以了。 【参考方案1】:

一般来说,浏览器不支持 UDP 连接。具体来说,某些浏览器会这样做。谷歌浏览器有一个套接字 api:

http://developer.chrome.com/trunk/apps/socket.html

[2012/10/29 编辑为套接字不再是实验性的 - PhilH]

您也可以使用来自本机客户端接口的套接字 API(不确定,只是猜测)。

如果您打算在不久的将来尝试在浏览器上实时执行任何操作,Websockets 可能是您最好的选择,但这些只是 TCP。

关于 UDP 与 TCP 速度的 cmets,UDP 总是更快。 TCP 提供排序和传递保证(这意味着可能会重试并保留其他数据包,直到丢失的数据包最终到达其目的地),而 UDP 只承诺发送一次,而不关心它之后会发生什么。 UDP只会发送一次它的数据包,你需要弄清楚它是否丢失了。当/如果您收到大量 UDP 数据包时,如果顺序很重要,您需要在数据负载中对其进行编码以便能够弄清楚。

一般来说,UDP 非常适合丢失一些通常无关紧要的数据包,而只有最新的数据包才是真正重要的数据包。游戏通常可以使用 TCP 流,其中订购和保证交付很重要(重要的东西),以及 UDP 流用于对象移动等(只有最新位置才真正重要,如果一个更新丢失,只要每个更新都没有关系包包含完整位置[而不是所有数据包都重要的增量])。

对于你自己的游戏,我建议先在 TCP 上实现它,然后当你有更多经验时,你可以尝试将时间关键的东西(顺序和丢失的数据包不太重要)移动到单独的 UDP 流中。有很多项目都失败了,因为人们首先从 UDP 开始,然后尝试在其之上添加订购和交付保证,有效地尝试重新实现 TCP。这就是为什么反其道而行之更有意义的原因。

【讨论】:

一般来说是一个很好的答案,但请注意 TCP 不是建立在 UDP 之上的:两者都建立在 IP 数据报之上。除了端口号之外,UDP 仅在 IP 之上添加了很少的内容。这种区别很重要,因为 TCP 端口和 UDP 端口位于单独的数字空间中。例如,TCP 80 端口与 UDP 80 端口完全不同且无关。 @SethNoble & Marius。如果我以每秒 20 个的速率从服务器向客户端发送游戏世界快照,TCP 会在客户端引起问题吗?像entity interpolation, client-side prediction and lag compensation 这样的技术是否仍然适用于 TCP,就像它们适用于 UDP 一样?谢谢。 Yoshima,这取决于很多事情,比如发送方和接收方之间的带宽和延迟。除非您在 LAN 上,否则您需要将视图更新帧率与“网络帧率”分离。高级技术将尝试检测并适应任何条件(减少更新以适应带宽和延迟)。 除了 Marius 所说的,请记住,如果 TCP 因数据包重传或流量控制而延迟,所有延迟的消息都会堆积起来,并一次全部发送出去。因此,您可能需要一种机制来“追赶”并丢弃过时的信息。这甚至适用于 UDP,但使用 TCP 会更明显。 切勿将 TCP 用于网络游戏,除非它是回合制 RPG。即使那样......重新考虑。 gafferongames.com/networking-for-game-programmers/udp-vs-tcp【参考方案2】:

实时应用程序通常是接收至少 30 Hz 且抖动小于 10% 的数据更新的应用程序。 TCP/IP 是可靠的,但无法以这种速率发送定期更新,而抖动偶尔会超出范围。这是因为 TCP 正在握手和确认以确保可靠传输,这妨碍了发送快速定期平滑更新。 UDP 是一个更简单的协议,其中套接字数据是触发后忘记的。这本身就是一个问题,但这个问题比 TCP/IP 的不良抖动更容易克服。以我的经验,UDP 是唯一的前进方向,也是实时应用程序在诸如 VoIP 中使用的 RTP 等协议中使用它的原因。

Web Sockets 也是错误的希望,因为它是基于 TCP 的协议。我使用自定义 UDP 套接字,其中发送方和接收方维护一个序列号,它可以告诉您数据包是否丢失、重复或乱序,以及 WAN 网络上的所有问题。寻找使用 UDP 和检测入站数据包来衡量性能的方法。

【讨论】:

【参考方案3】:
var server = require('http').createServer();

require('dgram-browserify').listen(server);

server.listen(8080);

【讨论】:

以上是关于用于实时多人游戏的 Node.js UDP的主要内容,如果未能解决你的问题,请参考以下文章

WebRTC 是正确的吗? (实时多人游戏)

如何使用 websockets 进行实时游戏

Node.js 多人游戏多线程和多核

使用 Node.JS 构建的多人 JavaScript 游戏 - 分隔玩家

node.js 中的多人游戏架构 - 与 Redux 的状态管理和同步

带有 JavaScript 后端和前端的多人游戏。最佳实践是啥?