如何使用套接字 (socket.io) 在 NodeJS 中找到客户端的响应时间(延迟)?

How can I find the response time (latency) of a client in NodeJS with sockets (socket.io)?

提问人:FR6 提问时间:11/2/2010 最后编辑:Pooya EstakhriFR6 更新时间:7/5/2018 访问量:50895

问:

我正在尝试使用 NodeJS 创建一个多人游戏,并且我想在客户端之间同步操作。

查找客户端和服务器之间的延迟(请求返回客户端所需的时间)的最佳方法是什么?

我的第一个想法是客户端 #1 可以发送带有 is request 的时间戳,因此当客户端 #2 收到客户端 #1 的操作时,他将调整 is 操作速度以消除请求的延迟。 但问题是,两个客户端的系统日期时间可能不相同,因此不可能知道客户端 #1 请求的卷轴延迟。

另一个解决方案是使用服务器的时间戳,但现在我如何知道客户端的延迟?

节点.js websocket socket.io 延迟

评论


答:

27赞 kanaka 11/2/2010 #1

我假设您正在使用 WebSockets 或 Socket.IO,因为您正在实现延迟很重要的游戏(并且您将其标记为如此)。

我认为服务器可能应该为每个客户端测量和跟踪这一点。

您可能希望实现服务器可以请求客户端的某种 ping 操作。客户端收到请求后,会立即向服务器发送响应。然后,服务器除以 2 并更新该客户端的延迟。您可能希望服务器定期对每个客户端执行此操作,并且可能对最后几个客户端进行平均,这样您就不会因突然但暂时的峰值而出现奇怪的行为。

然后,当需要将来自一个客户端的消息发送(或广播)到另一个客户端时,服务器可以将 client1 的延迟添加到客户端 2 的延迟中,并将其作为延迟偏移量作为消息的一部分传达给客户端 2。然后,client2 将知道 client1 上的事件发生在许多毫秒前。

在服务器上执行此操作的另一个原因是某些浏览器 Javascript 时间戳不准确:http://ejohn.org/blog/accuracy-of-javascript-time/。我怀疑 node.js 时间戳与 V8(这是为数不多的准确时间戳之一)一样准确(或更准确)。

评论

3赞 FR6 11/2/2010
是的,我正在使用“socket.io”。这正是我所实现的,我忘记的是将延迟除以 2!感谢您的建议!
0赞 Travis 11/3/2010
嘿 FR6,我实际上刚刚实现了一个 socket.io ping 测试,因为我也对使用 node.js 作为服务器制作 Web 浏览器游戏感兴趣。我会写一篇关于它的博客文章并链接到这里,希望它会有所帮助。出于某种原因,我的 websocket ping(往返时间)的平均 ping 时间约为 220 毫秒,但是
0赞 kanaka 11/3/2010
@FR6,@Travis:为此开源代码(和博客)会很棒。我相信很多人会对此感兴趣。
0赞 DeaconDesperado 5/6/2013
+1 对此进行探索。很想看到一些延迟指标,并确定是否最终可以在客户端 js 中实现响应关键应用程序(我的想法像按钮捣碎器、街头霸王风格的游戏等等)
6赞 Tor Valamo 11/3/2010 #2

我通常做什么来发送带有请求的时间戳:

  1. 在客户端上,创建一个并发送到服务器,其中包含每个 JSON 请求。new Date()timestamp: date.getTime()
  2. 在服务器上,收到请求后,将 放入对象中。processed: (new Date()).getTime()
  3. 处理请求。
  4. 在响应中,输入 from 请求和一个新的已处理字段:,该字段现在包含处理请求所需的毫秒数。timestampprocessed: (new Date()).getTime() - req.processed
  5. 在客户端上,当收到响应时,取 (与第 1 部分发送的相同)并从当前时间中减去它,然后减去处理时间 (),这就是您的“真实”ping 时间(以毫秒为单位)。timestampprocessed

我认为您应该始终在 ping 时间中包含请求和响应的时间,即使存在单向通信。这是因为这是“ping 时间”和“延迟”背后的标准含义。如果是单向通信,延迟只有实际ping时间的一半,那只是一件“好事”。

评论

0赞 FR6 11/3/2010
这是我第一次做的。但不起作用的是客户端 #1 和客户端 #2 可能没有相同的日期时间(在不同的时区)或不同步。就像 kanaka 所说的那样,JavaScript 日期时间在客户端可能不准确。
0赞 Tor Valamo 11/3/2010
它是否准确与任何事情无关。每个客户端(或服务器)仅使用该实例创建的日期。根据请求发送的时间戳将完全按原样返回给客户端。因此,客户端可以在自己的时间内查看自发送请求以来的时间。
0赞 backdesk 12/8/2014
对我来说很有意义。它使您不必在其他呼叫之间进行单独的“ping”呼叫。有没有办法让 socket.io 自动包含它?
1赞 Travis 11/3/2010 #3

这是我非常快速和肮脏的脚本来测试 ping ...只需在浏览器中 http://yourserver:8080 并观看控制台(对我来说是SSH终端)。

var http = require('http');
var io = require('socket.io');

server = http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  res.write('<html>\n');
  res.write('  <head>\n');
  res.write('    <title>Node Ping</title>\n');
  res.write('    <script src="/socket.io/socket.io.js"></script>\n');
  res.write('    <script>\n');
  res.write('        var socket = new io.Socket();\n');
  res.write('        socket.on("connect",function(){ });\n');
  res.write('        socket.on("message",function(){ socket.send(1); });\n');
  res.write('        socket.connect();\n');
  res.write('    </script>\n');
  res.write('  </head>\n');
  res.write('  <body>\n');
  res.write('    <h1>Node Ping</h1>\n');
  res.write('  </body>\n');
  res.write('</html>\n');
  res.end();
});
server.listen(8080);

console.log('Server running at http://127.0.0.1:8080/');

var socket = io.listen(server);

socket.on('connection',function(client){
  var start = new Date().getTime();
  client.send(1);
  client.on('message',function(message){ client.send(1);  console.log( new Date$
  client.on('disconnect',function(){});
});

我对此非常好奇,因为在加利福尼亚和新泽西州都有专用资源的大型 vps 盒子上,我的 ping 值似乎非常高(往返 200-400 毫秒)。(我在东海岸)我敢打赌,vps 盒子 b/c 上有很多延迟,它们提供如此多的流量?

让我感到困惑的是,从同一客户端到同一服务器的 linux 终端的常规 ping 平均为 11 毫秒,低了 10 倍......是我做错了什么,还是 node.js/socket.io/websockets 有点慢?

评论

0赞 FR6 11/3/2010
您在什么浏览器上进行测试?因为当我在 Chrome 上进行一些测试时,PING 约为 40 毫秒,而在 Firefox 3.6.x 中约为 350 毫秒。我想差异主要是因为 Firefox 3.6 本身不支持 websockets,因此模块“socket.io”将使用 Flash 在服务器和客户端之间创建连接。也许延迟是由浏览器使用 JavaScript 与 Flash 通信所需的时间造成的。另一方面,当浏览器原生支持 websocket 时,他不需要使用 Flash。
0赞 Travis 11/3/2010
我正在使用支持 websockets 传输选项的 chrome 最新夜报之一。我明白你在说什么关于firefox / flash减速,甚至还没有开始担心。
0赞 Travis 11/14/2010
在最近一次更新 socket.io 之后,服务器响应时间似乎已骤降至 ~10ms!
24赞 Sahat Yalkabov 1/11/2014 #4

概述:

建立 socket.io 连接后,在客户端上创建一个新对象,我们将其称为 。这是您向服务器发出请求之前的初始时间。然后,从客户端发出一个事件。命名约定完全取决于您。同时,服务器应该正在监听一个事件,当它收到 时,它会立即发出一个事件。然后,客户端捕获事件。此时,您要创建另一个表示 的日期对象。因此,此时您有两个日期对象 - 向服务器发出请求之前的初始日期,以及向服务器发出请求并回复后的另一个日期对象。从当前时间中减去 ,你得到 .DatestartTimepingpingpingpongpongDate.now()startTimelatency

客户

var socket = io.connect('http://localhost');
var startTime;

setInterval(function() {
  startTime = Date.now();
  socket.emit('ping');
}, 2000);

socket.on('pong', function() {
  latency = Date.now() - startTime;
  console.log(latency);
});

服务器

io.sockets.on('connection', function (socket) {
  socket.on('ping', function() {
    socket.emit('pong');
  });
});

也可作为 Github Gist 使用。

评论

2赞 PanMan 4/30/2015
这将每 2 秒在“pong”上添加另一个回调。socket.on('pong' 的东西应该在 setInterval 之外。
0赞 Sahat Yalkabov 7/8/2015
@PanMan我的愚蠢错误,谢谢你抓住了它。
1赞 NiCk Newman 10/3/2015
startTime代码需要全局才能工作,仅供参考:)谢谢你。
2赞 Chris Scott 3/1/2016
对我来说,使用“ping”这个词是行不通的。我认为它已经被node.js使用。我把它改成不同的东西,它起作用了。
1赞 Leschge 8/11/2020
注意:ping 和 pong 作为消息名称已被 socket.io 本身使用 socket.io/docs/emit-cheatsheet
0赞 Simon A. Eugster 3/14/2014 #5

先读——由于反复询问为什么这应该起作用,让我澄清一下。

  • 客户端回调函数在客户端上执行,这就是为什么它可以访问闭包,包括包含时间戳的变量。这是 socket.io 中的 ack() 参数。start
  • 服务器自然不能在客户端上调用任意函数并访问函数的闭包。但允许定义一个回调功能,它似乎是由服务器执行的,但这实际上只是通过 Web 套接字传递函数参数,然后客户端调用回调。socket.io

下面会发生什么(请检查示例代码!

  1. 客户端将当前时间戳存储在1453213686429start
  2. 客户端向服务器发送事件并等待应答ping
  3. 服务器使用 “Please call your callback with empty arguments” 响应 ping 事件
  4. 客户端接收响应并使用空参数调用(如果您想查看参数,请查看演示代码)clientCallback
  5. clientCallbackagain 获取客户端上的当前时间戳,例如 ,并知道自发送请求以来已经过去了时间戳。145321368644920 ms

想象一下,德鲁伊(客户端)拿着秒表,当信使(事件)开始运行时按下按钮,当信使带着他的卷轴函数参数)到达时再次按下它。然后德鲁伊阅读卷轴并将成分名称添加到他的魔药配方中并酿造药水。(回调)

好吧,忘记上一段,我想你明白了。


虽然这个问题已经得到解答,但这里有一个简短的实现,用于检查 RTT:socket.io

客户

var start = Date.now();
this.socket.emit( 'ping', function clientCallback() {
    console.log( 'Websocket RTT: ' + (Date.now() - start) + ' ms' );
} );

服务器

socket.on( 'ping', function ( fn ) {
    fn(); // Simply execute the callback on the client
} );

演示代码

作为节点模块的演示代码:socketIO-callback.tgz 设置它并使用它运行它

npm install
node callback.js

,然后导航到 http://localhost:5060

评论

0赞 jfriend00 10/15/2014
你真的认为你可以将回调引用作为数据发送到服务器,然后让它在服务器上执行,并以某种方式让该回调甚至能够访问客户端上的闭包变量吗?这甚至无法接近工作。
2赞 japrescott 11/12/2014
他没有传递要调用的 FN,而是调用客户端回调说“我明白了”。这是一件 socket.io 的事情。
0赞 backdesk 12/8/2014
我真的很喜欢这种方法。我们使用执行回调的点作为检查时间的地方。
0赞 iamawebgeek 1/18/2016
这是行不通的!您正在客户端上获取日期时间,即 pc/client 的日期时间,因此如果客户端将其 pc 时间设置为 01/01/2004,则 ping 将超过十年。想想看。
1赞 Coder Gautam YT 6/23/2022
我可以确认这有效,在生产中使用它
33赞 Luka 7/20/2017 #6

看完所有这些答案后...

...我仍然不满意。我访问了官方文档,好吧,好吧,好吧 - 解决方案已经内置了。

你只需要实现它 - 看看我的:

客户

// (Connect to socket).

var latency = 0;

socket.on('pong', function(ms) {
    latency = ms;

    console.log(latency);
});

// Do cool things, knowing the latency...

服务器

var server = require('http').Server(app);

// "socket.io": "^1.7.1"
// Set pingInterval to whatever you want - 'pong' gets emitted for you!
var io = require('socket.io')(server, {pingInterval: 5000});

评论

3赞 Epic Speedy 12/14/2020
这是最好和最简单的答案。
6赞 Itay Ganor 2/20/2021
请注意,socket.io v3.0 中已弃用。更多信息请点击此处pong
2赞 Vardan Betikyan 5/1/2021
@ItayGanor省去了我的头痛——谢谢