NodeJSでマルチプレイヤーゲームを作成しようとしていますが、クライアント間でアクションを同期させたいと思っています。
クライアントとサーバーの間の待ち時間(リクエストがクライアントに戻るまでにかかる時間)を見つけるための最良の方法は何でしょうか?
私の最初のアイデアは、クライアント#1がisリクエストでタイムスタンプを送信できるということでした。したがって、クライアント#2がクライアント#1のアクションを受信すると、アクション速度を調整してリクエストの遅延を取り除きます。しかし、問題は、2つのクライアントのシステム日時が同一ではない可能性があるため、クライアント#1の要求で2つがリール遅延を知ることができないことです。
もう1つの解決策は、サーバーのタイムスタンプを使用することでしたが、クライアントの待機時間をどのように知ることができますか?
レイテンシーが重要なゲームを実装している(そしてそのようにタグ付けしている)ので、WebSocketまたは Socket.IO を使用していると仮定します。
サーバーはおそらくクライアントごとにこれを測定して追跡する必要があると思います。
サーバーがクライアントに要求できる、ある種のpingアクションを実装することをお勧めします。クライアントは要求を受信するとすぐに、サーバーに応答を送り返します。次に、サーバーは2で除算し、そのクライアントの待機時間を更新します。サーバーが各クライアントでこれを定期的に実行し、最後のいくつかを平均して、突然の一時的なスパイクから奇妙な動作が発生しないようにする必要があります。
次に、あるクライアントから別のクライアントに送信(またはブロードキャスト)する必要のあるメッセージがある場合、サーバーはclient1のレイテンシーをclient2のレイテンシーに追加し、これをレイテンシーオフセットとしてクライアント2にメッセージの一部として伝達できます。 client2は、client1のイベントが数ミリ秒前に発生したことを認識します。
サーバーでこれを行うもう1つの理由は、一部のブラウザーのJavascriptタイムスタンプが不正確であるということです: http://ejohn.org/blog/accuracy-of-javascript-time/ 。 node.jsのタイムスタンプはV8(数少ない正確なものの1つ)と同じくらい正確(またはそれ以上)であると思います。
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 としても利用できます。
リクエストとともにタイムスタンプを送信するために私が通常行うこと:
new Date()
を作成し、timestamp: date.getTime()
をサーバーに送信します。JSONリクエストはすべて実行されます。processed: (new Date()).getTime()
を配置します。timestamp
と、リクエストの処理にかかったミリ秒数を含む新しい処理済みフィールドprocessed: (new Date()).getTime() - req.processed
を入力します。timestamp
(pt 1で送信されたものと同じ)を取得して現在の時刻から減算し、処理時間(processed
)を減算します。そして、ミリ秒単位の「実際の」ping時間があります。一方向の通信がある場合でも、ping時間には常に要求と応答の両方の時間を含める必要があると思います。これは、それが「ping時間」と「遅延」の背後にある標準的な意味であるためです。また、一方向の通信であり、遅延が実際のping時間の半分しかない場合、それは単なる「良いこと」です。
...私はまだ満足していませんでした。私は公式ドキュメントにアクセスしましたが、まあ、まあ、まあ、ソリューションはすでに組み込まれています。
あなたはそれを実装する必要があります-私のものをチェックしてください:
// (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});
これが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?
最初にお読みください—なぜこれが機能するのかという質問が繰り返されるため、少し明確にしておきます。
start
変数を含むクロージャにアクセスできる理由です。これは socket.ioのack()引数socket.io
を使用すると、サーバーによって実行されているように見えるコールバック関数を定義できますが、実際には、関数の引数をWebソケットに渡すだけです。次に、クライアントはコールバックを呼び出します。以下で何が起こりますか(サンプルコードを確認してください!):
1453213686429
をstart
に保存しますping
イベントをサーバーに送信し、応答を待っていますclientCallback
を呼び出します(引数を確認する場合は、デモコードを確認してください)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 に移動します。