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

时间:2010-11-01 17:12:38

标签: node.js websocket socket.io latency

我正在尝试使用NodeJS创建一个多人游戏,我希望在客户端之间同步动作。

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

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

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

6 个答案:

答案 0 :(得分:20)

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

我认为服务器应该测量并跟踪每个客户端的信息。

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

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

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

答案 1 :(得分:18)

概述:

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

客户端

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 :(得分:4)

我通常会发送带有请求的时间戳:

  1. 在客户端上,创建new Date()并将timestamp: date.getTime()发送到服务器,并附上每个JSON请求。
  2. 在服务器上,收到请求后,在对象中放置processed: (new Date()).getTime()
  3. 处理请求。
  4. 在响应中,放置请求中的timestamp和新处理的字段:processed: (new Date()).getTime() - req.processed,现在包含处理请求所用的毫秒数。
  5. 在客户端上,收到回复时,取timestamp(与第1页上发送的相同)并从当前时间减去它,然后减去处理时间(processed) ,并且有你的“真实”ping时间,以毫秒为单位。
  6. 我认为您应该始终在ping时间中包含请求和响应的时间,即使存在单向通信。这是因为这是“ping time”和“latency”背后的标准含义。如果它是单向通信并且延迟只是实际ping时间的一半,那只是一件“好事”。

答案 3 :(得分:1)

继续我真正快速而又脏的脚本来测试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-400ms往返)非常高。 (我在东海岸)我打赌在vps盒子上只有很多延迟b / c他们服务这么多的流量?

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

答案 4 :(得分:0)

先阅读 - 由于反复提出这个问题的原因,请允许我澄清一下。

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

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

  1. 客户端将当前时间戳1453213686429存储在start
  2. 客户端向服务器发送ping事件并等待回答
  3. 服务器通过“请使用空参数调用回调”
  4. 来响应ping事件
  5. 客户端收到响应并使用空参数调用clientCallback(如果要查看参数,请检查演示代码)
  6. clientCallback再次获取客户端上的当前时间戳,例如1453213686449,并且知道自发送请求后20 ms已经过去了。
  7. 想象一下,当信使(事件)开始跑步时,德鲁伊(客户)拿着秒表并按下按钮,当信使到达他的滚动时再按一下(函数参数)。然后德鲁伊读取卷轴并将成分名称添加到他的药水配方中并酿造药水。 (回调)

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


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

    客户端

    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

答案 5 :(得分:0)

阅读完所有这些答案后......

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

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

客户端

// (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});