web-dev-qa-db-ja.com

ソケット(socket.io)を使用するNodeJSでクライアントの応答時間(待機時間)を見つけるにはどうすればよいですか?

NodeJSでマルチプレイヤーゲームを作成しようとしていますが、クライアント間でアクションを同期させたいと思っています。

クライアントとサーバーの間の待ち時間(リクエストがクライアントに戻るまでにかかる時間)を見つけるための最良の方法は何でしょうか?

私の最初のアイデアは、クライアント#1がisリクエストでタイムスタンプを送信できるということでした。したがって、クライアント#2がクライアント#1のアクションを受信すると、アクション速度を調整してリクエストの遅延を取り除きます。しかし、問題は、2つのクライアントのシステム日時が同一ではない可能性があるため、クライアント#1の要求で2つがリール遅延を知ることができないことです。

もう1つの解決策は、サーバーのタイムスタンプを使用することでしたが、クライアントの待機時間をどのように知ることができますか?

21
FR6

レイテンシーが重要なゲームを実装している(そしてそのようにタグ付けしている)ので、WebSocketまたは Socket.IO を使用していると仮定します。

サーバーはおそらくクライアントごとにこれを測定して追跡する必要があると思います。

サーバーがクライアントに要求できる、ある種のpingアクションを実装することをお勧めします。クライアントは要求を受信するとすぐに、サーバーに応答を送り返します。次に、サーバーは2で除算し、そのクライアントの待機時間を更新します。サーバーが各クライアントでこれを定期的に実行し、最後のいくつかを平均して、突然の一時的なスパイクから奇妙な動作が発生しないようにする必要があります。

次に、あるクライアントから別のクライアントに送信(またはブロードキャスト)する必要のあるメッセージがある場合、サーバーはclient1のレイテンシーをclient2のレイテンシーに追加し、これをレイテンシーオフセットとしてクライアント2にメッセージの一部として伝達できます。 client2は、client1のイベントが数ミリ秒前に発生したことを認識します。

サーバーでこれを行うもう1つの理由は、一部のブラウザーのJavascriptタイムスタンプが不正確であるということです: http://ejohn.org/blog/accuracy-of-javascript-time/ 。 node.jsのタイムスタンプはV8(数少ない正確​​なものの1つ)と同じくらい正確(またはそれ以上)であると思います。

23
kanaka

概要:

Socket.io接続が確立されたら、クライアント上に新しいDateオブジェクトを作成します。これをstartTimeと呼びましょう。これは、サーバーにリクエストを送信する前の初期時間です。次に、クライアントからpingイベントを発行します。命名規則は完全にあなた次第です。その間、サーバーはpingイベントをリッスンする必要があり、pingを受信すると、すぐにpongイベントを発行します。次に、クライアントはpongイベントをキャッチします。この時点で、Date.now()を表す別の日付オブジェクトを作成します。したがって、この時点で2つの日付オブジェクトがあります。サーバーにリクエストを送信する前の最初の日付と、サーバーにリクエストを送信して応答した後の別の日付オブジェクトです。現在の時刻から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 としても利用できます。

20
Sahat Yalkabov

リクエストとともにタイムスタンプを送信するために私が通常行うこと:

  1. クライアントで、new Date()を作成し、timestamp: date.getTime()をサーバーに送信します。JSONリクエストはすべて実行されます。
  2. サーバー上で、リクエストを受信したら、オブジェクトにprocessed: (new Date()).getTime()を配置します。
  3. リクエストを処理します。
  4. 応答に、リクエストからのtimestampと、リクエストの処理にかかったミリ秒数を含む新しい処理済みフィールドprocessed: (new Date()).getTime() - req.processedを入力します。
  5. クライアントで、応答を受信したら、timestamp(pt 1で送信されたものと同じ)を取得して現在の時刻から減算し、処理時間(processed)を減算します。そして、ミリ秒単位の「実際の」ping時間があります。

一方向の通信がある場合でも、ping時間には常に要求と応答の両方の時間を含める必要があると思います。これは、それが「ping時間」と「遅延」の背後にある標準的な意味であるためです。また、一方向の通信であり、遅延が実際のping時間の半分しかない場合、それは単なる「良いこと」です。

5
Tor Valamo

これらすべての答えを読んだ後...

...私はまだ満足していませんでした。私は公式ドキュメントにアクセスしましたが、まあ、まあ、まあ、ソリューションはすでに組み込まれています。

あなたはそれを実装する必要があります-私のものをチェックしてください:

クライアント

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

これがpingをテストするための本当に速くて汚いスクリプトです...ブラウザで http:// yourserver:808 に行き、コンソール(私にとっては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)ように見えるので、これについて非常に興味があります。 (私は東海岸にいます)私は彼らが非常に多くのトラフィックを提供しているb/cのvpsボックスにたくさんの待ち時間があると確信していますか?

同じクライアントから同じサーバーへのLinuxターミナルからの通常のpingは、平均で10分の1になります...何か間違ったことをしているのか、node.js/socketで何か遅いのか。 io/websockets?

1
Travis

最初にお読みください—なぜこれが機能するのかという質問が繰り返されるため、少し明確にしておきます。

  • クライアントコールバック関数はクライアントで実行されますこれが、タイムスタンプを含むstart変数を含むクロージャにアクセスできる理由です。これは socket.ioのack()引数
  • サーバーは当然、クライアント上の任意の関数を呼び出して、関数のクロージャにアクセスすることはできません。ただし、socket.ioを使用すると、サーバーによって実行されているように見えるコールバック関数を定義できますが、実際には、関数の引数をWebソケットに渡すだけです。次に、クライアントはコールバックを呼び出します。

以下で何が起こりますか(サンプルコードを確認してください!):

  1. クライアントは現在のタイムスタンプ1453213686429startに保存します
  2. クライアントはpingイベントをサーバーに送信し、応答を待っています
  3. サーバーはpingイベントに「空の引数でコールバックを呼び出してください」と応答します
  4. クライアントは応答を受信し、空の引数を使用してclientCallbackを呼び出します(引数を確認する場合は、デモコードを確認してください)
  5. clientCallbackは再び現在のタイムスタンプを取得しますクライアント上例: 1453213686449であり、リクエストを送信してから20 msが経過したことを認識しています。

ドルイド僧(client)メッセンジャー(event)が実行を開始したときに、ストップウォッチを持ってボタンを押すと想像してみてください。メッセンジャーがスクロール(関数の引数)を持って到着したら、もう一度押します。次にドルイド僧は巻物を読み、ポーションのレシピに材料名を追加してポーションを醸造します。(コールバック)

さて、前の段落を忘れてください、私はあなたがポイントを得たと思います。


質問はすでに回答済みですが、ここでは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:506 に移動します。

0