Подтвердить что ты не робот

Как найти время ответа (латентность) клиента в NodeJS с сокетами (socket.io)?

Я пытаюсь создать многопользовательскую игру с NodeJS, и я хочу синхронизировать действие между клиентами.

Каким будет лучший способ найти задержку (время, когда запрос будет возвращен клиенту) между клиентом и сервером?

Моя первая идея заключалась в том, что клиент №1 мог отправить отметку времени с запросом, поэтому, когда клиент № 2 получит действие клиента №1, он будет корректировать скорость действия, чтобы удалить задержку запроса. Но проблема в том, что, возможно, время системной даты двух клиентов не одинаково, поэтому невозможно узнать задержку барабана по запросу клиента № 1.

Другим решением было использовать временную метку сервера, но теперь, как я могу узнать о латентности клиента?

4b9b3361

Ответ 1

Я предполагаю, что вы используете WebSockets или Socket.IO, так как вы реализуете игру, в которой есть латентность (и вы отметили ее как таковой).

Я думаю, сервер, вероятно, должен измерять и отслеживать это для каждого клиента.

Вероятно, вы хотите реализовать какое-то действие ping, которое сервер может запросить у клиента. Как только клиент получает запрос, он отправляет ответ на сервер. Затем сервер делит на 2 и обновляет задержку для этого клиента. Вероятно, вы хотите, чтобы сервер периодически делал это с каждым клиентом и, вероятно, усреднял последние несколько, чтобы вы не получали странное поведение от внезапных, но временных всплесков.

Затем, когда есть сообщение от одного клиента, который необходимо отправить (или передать) другому клиенту, сервер может добавить латентность client1 к задержке клиента2 и сообщить об этом как смещение задержки клиенту2 как часть сообщения. client2 тогда узнает, что событие на клиенте1 произошло так много миллисекунд назад.

Дополнительная причина для этого на сервере заключается в том, что некоторые временные метки JavaScript Javascript являются неточными: http://ejohn.org/blog/accuracy-of-javascript-time/. Я подозреваю, что временные метки node.js так же точны (или более того), чем V8 (что является одним из немногих точных).

Ответ 2

Обзор:

После того, как установлено соединение 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);
});

Сервер

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

Также доступен как Github Gist.

Ответ 3

Что я обычно делаю, чтобы отправить метку времени с запросом:

  • На клиенте создайте new Date() и отправьте timestamp: date.getTime() на сервер с каждым запросом JSON.
  • На сервере после получения запроса поместите объект processed: (new Date()).getTime() в объект.
  • Запрос на обращение.
  • В ответе положите timestamp из запроса и новое обработанное поле: processed: (new Date()).getTime() - req.processed, которое теперь содержит количество миллисекунд, необходимых для обработки запроса.
  • На клиенте, при получении ответа, возьмите timestamp (то же самое, что было отправлено на pt 1), и вычтите его из текущего времени и вычтите время обработки (processed), и есть ваше "реальное" время пинга в миллисекундах.

Я думаю, что вы всегда должны указывать время для запроса и ответа во время пинга, даже если есть односторонняя связь. Это связано с тем, что это стандартное значение для "времени пинга" и "латентности". И если это односторонняя связь, а латентность составляет лишь половину реального времени пинга, это просто "хорошая вещь".

Ответ 4

Вот мой очень быстрый и грязный script, чтобы проверить пинг... просто перейдите в http://yourserver:8080 в своем браузере и посмотрите console (ssh terminal для меня).

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

Мне очень любопытно, потому что кажется, что мои пингеры довольно высокие (200-400 м в оба конца) на больших vps-боксах с выделенными ресурсами как в калифорнии, так и в новой майке. (Я на восточном побережье). Я держу пари, что у них просто латентность на боксах vps b/c, они обслуживают столько трафика?

Вещь, которая меня достает, заключается в том, что регулярный пинг от терминала linux от одного и того же клиента к тому же серверу составляет 11 мс в среднем в 10 раз ниже... Я делаю что-то неправильно или что-то медленное с node.js/socket.io/WebSockets?

Ответ 5

Сначала прочитайте. Из-за повторяющихся вопросов, почему это должно работать, позвольте мне немного пояснить.

  • Функция обратного вызова клиента выполняется на клиенте, поэтому она имеет доступ к закрытию, включая start переменную, содержащую отметку времени. Это аргумент ack() в socket.io.
  • Сервер, естественно, не может вызвать произвольную функцию на клиенте и получить доступ к закрытию функций. Но socket.io позволяет определить функцию обратного вызова, которая, как представляется, выполняется сервером, но это фактически просто передает аргументы функции через веб-сокет, а затем клиент вызывает обратный вызов.

Что происходит ниже (пожалуйста, проверьте код примера!):

  1. Клиент сохраняет текущую 1453213686429 в start
  2. Клиент отправляет событие ping на сервер и ждет ответа
  3. Сервер отвечает на событие ping: "Пожалуйста, позвоните своему обратному вызову с пустыми аргументами"
  4. Клиент получает ответ и вызывает clientCallback с пустыми аргументами (проверьте демонстрационный код, если вы хотите видеть аргументы)
  5. clientCallback снова берет текущую clientCallback метку на клиенте, например 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

Ответ 6

После прочтения всех этих ответов...

... Я все еще не был удовлетворен. Я посетил официальные документы и хорошо, ну, хорошо - решение уже встроено.

Вам просто нужно его реализовать - проверьте мой:

Client

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