Как я могу найти время ответа (задержку) клиента в NodeJS с сокетами (socket.io)? - PullRequest
20 голосов
/ 01 ноября 2010

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

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

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

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

Ответы [ 6 ]

20 голосов
/ 01 ноября 2010

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

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

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

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

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

18 голосов
/ 11 января 2014

Обзор:

После того как соединение с 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 .

4 голосов
/ 03 ноября 2010

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

  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) ваше "реальное" время пинга в миллисекундах.

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

1 голос
/ 03 ноября 2010

Вот мой действительно быстрый и грязный скрипт для проверки пинга ... просто зайдите на 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(){});
});

Мне очень любопытно по этому поводу, потому что кажется, что мои пинги довольно высокие (200-400ms в оба конца) на больших VPS-боксах с выделенными ресурсами как в Калифорнии, так и в Нью-Джерси. (Я на восточном побережье) Бьюсь об заклад, просто много задержек на VPS-боксах, потому что они обслуживают так много трафика?

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

0 голосов
/ 20 июля 2017

Прочитав все эти ответы ...

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

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

Клиент

// (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});
0 голосов
/ 14 марта 2014

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...