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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4071258/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 13:43:08  来源:igfitidea点击:

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

node.jswebsocketsocket.iolatency

提问by FR6

I'm trying to create a multiplayer game with NodeJS and I want to synchronize the action between clients.

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

What would be the best way to find the latency (the time that a request take to come back to the client) between the client and the server?

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

My first idea was that the client #1 could send a timestamp with is request, so when client #2 will receive the action of the client #1 he will adjust is action speed to remove the delay of the request. But the problem is that maybe the system date time of the two clients are not identical so it is not possible two know the reel delay on the request of client #1.

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

The other solution was to use the timestamp of the server, but now how can I know the latency of a client?

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

回答by kanaka

I'm going to assume you are using WebSockets or Socket.IOsince you are implementing a game where latency matters (and you tagged it as such).

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

I would think the server should probably measure and keep track of this for each client.

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

You probably want to implement some sort of ping action that the server can request of the client. As soon as the client receives the request, it sends back a response to the server. The server then divides by 2 and updates the latency for that client. You probably want the server to do this periodically with each client and probably average the last several so that you don't get strange behavior from sudden but temporary spikes.

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

Then, when there is a message from one client that needs to be sent (or broadcast) to another client, the server can add client1's latency to client2's latency and communicate this as the latency offset to client2 as part of the message. client2 will then know that the event on client1 happened that many milliseconds ago.

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

An additional reason to do this on the server is that some browser Javascript timestamps are inaccurate: http://ejohn.org/blog/accuracy-of-javascript-time/. I suspect node.js timestamps are just as accurate (or more so) than V8 (which is one of the few accurate ones).

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

回答by Sahat Yalkabov

Overview:

概述:

After socket.io connection has been established, you create a new Dateobject on the client, let's call it startTime. This is your initial timebefore making a request to the server. You then emit a pingevent from the client. Naming convention is totally up to you. Meanwhile server should be listening for a pingevent, and when it receives the ping, it immediately emits a pongevent. Client then catches the pongevent. At this time you want to create another date object that represents Date.now(). So at this point you have two date objects - initial date before making a request to the server, and another date object after you make a request to the server and it replied. Subtract the startTimefrom current time and you have the latency.

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

Client

客户

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

Server

服务器

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

Also available as a Github Gist.

也可作为Github Gist 使用

回答by Luka

After reading all these answers...

看完这些答案后...

...I still wasn't satisfied. I visited the official docs and well, well, well - the solution is already built-in.

……我还是不满意。我访问了官方文档,好吧,好吧 - 解决方案已经内置了。

You just need to implement it - check out mine:

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

Client

客户

// (Connect to socket).

var latency = 0;

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

    console.log(latency);
});

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

?Server

?服务器

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

回答by Tor Valamo

What I usually do to send timestamp with request:

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

  1. On the client, create a new Date()and send timestamp: date.getTime()to the server, with every JSON request.
  2. On the server, upon receiving a request, put a processed: (new Date()).getTime()in the object.
  3. Handle request.
  4. On the response, put the timestampfrom the request, and a new processed field: processed: (new Date()).getTime() - req.processedthat now contains the number of milliseconds it took to process the request.
  5. On the client, when receiving a response, take the timestamp(which is the same that was sent on pt 1) and subtract it from the current time, and subtract processing time (processed), and there is your "real" ping time in milliseconds.
  1. 在客户端,创建一个new Date()并发timestamp: date.getTime()送到服务器,每个 JSON 请求。
  2. 在服务器上,收到请求后,将 aprocessed: (new Date()).getTime()放入对象中。
  3. 处理请求。
  4. 在响应中,放置timestamp来自请求和一个新的处理字段:processed: (new Date()).getTime() - req.processed现在包含处理请求所花费的毫秒数。
  5. 在客户端上,当收到响应时,获取timestamp(与 pt 1 上发送的相同)并将其从当前时间中减去,再减去处理时间 ( processed),这就是您的“真实”ping 时间(以毫秒为单位)。

I think you should always include the time for both request and response in the ping time, even if there is one-way communication. This is because that is the standard meaning behind "ping time" and "latency". And if it is one-way communication and the latency is only half of the real ping time, that's just a "good thing".

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

回答by Travis

Heres my really quick and dirty script to test the ping ... just head to http://yourserver:8080in your browser and watch the console (ssh terminal for me).

这是我用来测试 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(){});
});

I'm very curious about this because it seems like my pings are pretty high(200-400ms round trip) on large vps boxes w/ dedicated resources both in california and new jersey. (I'm on the east coast) I'm betting theres just a lot of latency on the vps boxes b/c they're serving so much traffic?

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

The thing that gets me is that a regular ping from the linux terminal from the same client to the same server is 11ms on average a factor of 10 lower ... am I doing something wrong or is something slow with node.js/socket.io/websockets?

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

回答by Simon A. Eugster

Read first —Due to repeated questions why this is supposed to work, let me clarify a bit.

首先阅读 -由于重复的问题为什么这应该起作用,让我澄清一下。

  • The client callback function is executed on the client,which is why it has access to the closure, including the startvariable containing the time stamp. This is the ack() argument in socket.io.
  • The server naturally cannot call an arbitrary function on the client and access the function's closure. But socket.ioallows to define a callback funtion, which appears to be executedby the server, but this actually just passes the function arguments through the web socket, and the client then calls the callback.
  • 客户端回调函数在客户端上执行,这就是为什么它可以访问闭包,包括start包含时间戳的变量。这是socket.io 中的 ack() 参数。
  • 服务器自然不能调用客户端上的任意函数并访问该函数的闭包。但是socket.io允许定义一个回调函数,它看起来是由服务器执行的,但这实际上只是通过 web socket 传递函数参数,然后客户端调用回调。

What happens below (please do check the example code!):

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

  1. Client stores current timestamp 1453213686429in start
  2. Client sends a pingevent to the server and is waiting for an answer
  3. Server responds to the ping event with “Please call your callback with empty arguments”
  4. Client receives the response and calls clientCallbackwith empty arguments (Check the demo code if you want to see arguments)
  5. clientCallbackagain takes the current timestamp on the client, e.g. 1453213686449, and knows that 20 mshave passed since it sent the request.
  1. 客户端将当前时间戳存储1453213686429start
  2. 客户端向ping服务器发送事件并等待响应
  3. 服务器以“请用空参数调用您的回调”来响应 ping 事件
  4. 客户端接收响应并clientCallback使用空参数调用(如果您想查看参数,请查看演示代码)
  5. clientCallback再次获取客户端上的当前时间戳,例如1453213686449,并且知道20 ms自从它发送请求以来已经过去了。

Imagine the druid (client)holding a stopwatch and pushing the button when the messenger (event)starts running, and pushing it again when the messenger arrives with his scroll (function arguments). The druid then reads the scroll and adds the ingredient names to his potion recipe and brews the potion. (callback)

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

Okay, forget the previous paragraph, I guess you got the point.

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



Although the question has already been answered, here a short implementation for checking the RTT with socket.io:

虽然这个问题已经得到了回答,但这里有一个简短的实现来检查 RTT socket.io

Client

客户

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

Server

服务器

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


Demo Code

演示代码

Demo code as node module: socketIO-callback.tgzSet it up and run it with

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

npm install
node callback.js

and then navigate to http://localhost:5060

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