提问人:FR6 提问时间:11/2/2010 最后编辑:Pooya EstakhriFR6 更新时间:7/5/2018 访问量:50895
如何使用套接字 (socket.io) 在 NodeJS 中找到客户端的响应时间(延迟)?
How can I find the response time (latency) of a client in NodeJS with sockets (socket.io)?
问:
我正在尝试使用 NodeJS 创建一个多人游戏,并且我想在客户端之间同步操作。
查找客户端和服务器之间的延迟(请求返回客户端所需的时间)的最佳方法是什么?
我的第一个想法是客户端 #1 可以发送带有 is request 的时间戳,因此当客户端 #2 收到客户端 #1 的操作时,他将调整 is 操作速度以消除请求的延迟。 但问题是,两个客户端的系统日期时间可能不相同,因此不可能知道客户端 #1 请求的卷轴延迟。
另一个解决方案是使用服务器的时间戳,但现在我如何知道客户端的延迟?
答:
我假设您正在使用 WebSockets 或 Socket.IO,因为您正在实现延迟很重要的游戏(并且您将其标记为如此)。
我认为服务器可能应该为每个客户端测量和跟踪这一点。
您可能希望实现服务器可以请求客户端的某种 ping 操作。客户端收到请求后,会立即向服务器发送响应。然后,服务器除以 2 并更新该客户端的延迟。您可能希望服务器定期对每个客户端执行此操作,并且可能对最后几个客户端进行平均,这样您就不会因突然但暂时的峰值而出现奇怪的行为。
然后,当需要将来自一个客户端的消息发送(或广播)到另一个客户端时,服务器可以将 client1 的延迟添加到客户端 2 的延迟中,并将其作为延迟偏移量作为消息的一部分传达给客户端 2。然后,client2 将知道 client1 上的事件发生在许多毫秒前。
在服务器上执行此操作的另一个原因是某些浏览器 Javascript 时间戳不准确:http://ejohn.org/blog/accuracy-of-javascript-time/。我怀疑 node.js 时间戳与 V8(这是为数不多的准确时间戳之一)一样准确(或更准确)。
评论
我通常做什么来发送带有请求的时间戳:
- 在客户端上,创建一个并发送到服务器,其中包含每个 JSON 请求。
new Date()
timestamp: date.getTime()
- 在服务器上,收到请求后,将 放入对象中。
processed: (new Date()).getTime()
- 处理请求。
- 在响应中,输入 from 请求和一个新的已处理字段:,该字段现在包含处理请求所需的毫秒数。
timestamp
processed: (new Date()).getTime() - req.processed
- 在客户端上,当收到响应时,取 (与第 1 部分发送的相同)并从当前时间中减去它,然后减去处理时间 (),这就是您的“真实”ping 时间(以毫秒为单位)。
timestamp
processed
我认为您应该始终在 ping 时间中包含请求和响应的时间,即使存在单向通信。这是因为这是“ping 时间”和“延迟”背后的标准含义。如果是单向通信,延迟只有实际ping时间的一半,那只是一件“好事”。
评论
这是我非常快速和肮脏的脚本来测试 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 有点慢?
评论
概述:
建立 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 使用。
评论
startTime
代码需要全局才能工作,仅供参考:)谢谢你。
先读——由于反复询问为什么这应该起作用,让我澄清一下。
- 客户端回调函数在客户端上执行,这就是为什么它可以访问闭包,包括包含时间戳的变量。这是 socket.io 中的 ack() 参数。
start
- 服务器自然不能在客户端上调用任意函数并访问函数的闭包。但允许定义一个回调功能,它似乎是由服务器执行的,但这实际上只是通过 Web 套接字传递函数参数,然后客户端调用回调。
socket.io
下面会发生什么(请检查示例代码!
- 客户端将当前时间戳存储在
1453213686429
start
- 客户端向服务器发送事件并等待应答
ping
- 服务器使用 “Please call your callback with empty arguments” 响应 ping 事件
- 客户端接收响应并使用空参数调用(如果您想查看参数,请查看演示代码)
clientCallback
clientCallback
again 获取客户端上的当前时间戳,例如 ,并知道自发送请求以来已经过去了时间戳。1453213686449
20 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
评论
看完所有这些答案后...
...我仍然不满意。我访问了官方文档,好吧,好吧,好吧 - 解决方案已经内置了。
你只需要实现它 - 看看我的:
客户
// (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});
评论