web-dev-qa-db-ja.com

javaでwebSocket接続アライブのPing / Pongリクエストを実装する方法は?

JavaScriptでwebsocketを使用しています。しかし、接続は1分後に閉じます。

私は何か疑問に思っています:

1- Websocketは、接続を閉じないようにPing/Pongメッセージを自然に提供していませんか?しなければならないと思います。それ以外の場合、websocketとTCP接続の違いは何ですか?

2- ping/pongメッセージを送信する必要がある場合、pingメッセージはどのように送信されますか?何をする必要がありますか? WebSocketオブジェクトはpingメソッドを提供しますか?または、websocket.send( "ping")としてメソッドを呼び出す必要がありますか?私はjavasciptで自然にWebSocketオブジェクトを使用しています。

3-サーバーはPongでPing要求に応答する必要がありますか?これはサーバー側で個別に実装する必要がありますか?

注:私の英語は申し訳ありません。

6
Qwer Sense

この時点で、ハートビートは通常サーバー側で実装されます。クライアント側からできることはあまりありません。

ただし、サーバーがソケット接続を強制終了し続け、それを制御できない場合、クライアントは任意のデータを一定の間隔でwebsocketに送信できます。

let socket = null;

function connect_socket() {
  socket = new WebSocket(ws_url);
  socket.on("close", connect_socket); // <- rise from your Grave!
  heartbeat();
}

function heartbeat() {
  if (!socket) return;
  if (socket.readyState !== 1) return;
  socket.send("heartbeat");
  setTimeout(heartbeat, 500);
}

connect_socket();

クライアントでそれを回避しようとするのではなく、サーバー側で何が起こっているかを整理することを強くお勧めします。

4
ouni

はい、websocketにはping/pongフレームがあります。サーバーがping要求を開始しているwsモジュールを使用した例を次に示します。

const http = require('http');
const ws = require('ws');

const server = http.createServer(function(req_stream_in, res_stream_out) {
  // handle regular HTTP requests here
});
const webSocketServer = new ws.Server({
  path: "/websocket",
  server: server
});

const connected_clients = new Map();

webSocketServer.on('connection', function connection(ws_client_stream) {
  // NOTE: only for demonstration, will cause collisions.  Use a UUID or some other identifier that's actually unique.
  const this_stream_id = Array.from(connected_clients.values()).length;

  // Keep track of the stream, so that we can send all of them messages.
  connected_clients.set(this_stream_id, ws_client_stream);

  // Attach event handler to mark this client as alive when pinged.
  ws_client_stream.is_alive = true;
  ws_client_stream.on('pong', () => { ws_client_stream.is_alive = true; });

  // When the stream is closed, clean up the stream reference.
  ws_client_stream.on('close', function() {
    connected_clients.delete(this_stream_id);
  });
});

setInterval(function ping() {
  Array.from(connected_clients.values()).forEach(function each(client_stream) {
    if (!client_stream.is_alive) { client_stream.terminate(); return; }
    client_stream.is_alive = false;
    client_stream.ping();
  });
}, 1000);
4
ouni

Mozillaは、ping/pong専用の規約を文書化しています。

ハンドシェイク後の任意の時点で、クライアントまたはサーバーのいずれかが、相手にpingを送信することを選択できます。 pingを受信すると、受信者はできるだけ早くポンを返送する必要があります。これを使用して、たとえば、クライアントがまだ接続されていることを確認できます。

PingまたはPongは単なる通常のフレームですが、制御フレームです。 Pingのオペコードは0x9で、pongのオペコードは0xAです。 pingを取得したら、pingとまったく同じペイロードデータでポンを送り返します(pingとポンの場合、最大ペイロード長は125です)。また、pingを送信せずにピンポンを取得する場合もあります。発生した場合はこれを無視してください。

ピンポンを送信する機会が得られる前に複数のpingを取得した場合は、1つのピンポンのみを送信します。

参照: https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers#Pings_and_Pongs_The_Heartbeat_of_WebSockets

ブラウザ側からのping/pongについての詳細な議論は、ここにあります: ブラウザからのwebsocket ping/pongフレームの送信

より具体的には、Websocket RFC 6455 ping/pongについてお読みください。

1
Pim Heijden

Ouniのソリューションでは、heartbeat()は作動していませんでした。次のようなopenイベントに入れたときに機能します。

let socket = null;

function connect_socket() {
  socket = new WebSocket(ws_url);
  socket.on("close", connect_socket); // <- rise from your Grave!
  socket.on("open", heartbeat); // heartbeat when the socket is open
}

function heartbeat() {
  if (!socket) return;
  if (socket.readyState !== 1) return;
  socket.send("heartbeat");
  setTimeout(heartbeat, 500);
}

connect_socket();
0
upteryx