導入:なぜ onclose が重要なのか
リアルタイム通信を行うアプリケーションにおいて、WebSocketの接続はネットワークの不安定さやサーバーの再起動など、様々な要因で切断されます。多くの開発者が「接続する処理」には注力しますが、「切断された後の処理」をおろそかにすると、ユーザー体験の低下やメモリリークを招きます。`WebSocket.prototype.onclose` を適切にハンドリングすることは、堅牢なフロントエンド実装における必須項目です。
基礎知識:onclose の仕組み
`onclose` は、WebSocket接続が閉じた時に呼び出されるイベントハンドラです。接続が正常に終了したのか、あるいはエラーによって強制終了されたのかを判別するための「CloseEvent」オブジェクトが引数として渡されます。
重要なプロパティは以下の2点です。
・wasClean: 接続が正常に閉じられた場合(closeフレームが交換された場合)に true となります。
・code: 終了コード(1000は正常終了、1006は異常終了など)を示します。
実装:堅牢な再接続ロジックの実装
単に切断を検知するだけでなく、自動再接続(Reconnection)を実装するのが実務での定石です。その際、指数バックオフ(間隔を徐々に広げる手法)を用いることで、サーバーへの負荷を軽減します。
サンプルプログラム
以下のコードは、切断時に自動再接続を試みるクラスベースの実装例です。
class ReconnectableWebSocket {
constructor(url) {
this.url = url;
this.retryCount = 0;
this.connect();
}
connect() {
this.ws = new WebSocket(this.url);
this.ws.onopen = () => {
console.log('接続成功');
this.retryCount = 0; // 接続成功時にリトライ回数をリセット
};
this.ws.onclose = (event) => {
// wasCleanがfalseなら異常終了と判断
if (!event.wasClean) {
console.warn(`接続切断: コード ${event.code}。再接続を試みます...`);
this.attemptReconnect();
}
};
}
attemptReconnect() {
// 指数バックオフ: 1秒, 2秒, 4秒...と間隔を空ける(最大30秒)
const delay = Math.min(Math.pow(2, this.retryCount) 1000, 30000);
setTimeout(() => {
this.retryCount++;
this.connect();
}, delay);
}
}
// 利用例
const socket = new ReconnectableWebSocket('wss://example.com/socket');
応用・注意点:現場で陥りやすい罠
1. 無限ループの回避: サーバー側がメンテナンス中で常に接続を拒否している場合、クライアントが高速で再接続を繰り返すと「接続の嵐」を引き起こします。上記のサンプルにあるような「指数バックオフ」や「最大リトライ回数の制限」を必ず設けてください。
2. クリーンアップの徹底: コンポーネント(ReactのuseEffectなど)がアンマウントされる際は、`ws.onclose = null` とし、明示的に `ws.close()` を呼び出すことで、不要な再接続処理が走るのを防ぎましょう。
3. 状態管理の同期: フロントエンドのUI(例:「接続中」「切断中」の表示)は、`onclose` のイベントと同期させる必要があります。ReduxやZustandなどのストアを使用している場合、このイベント内で状態を更新するのを忘れないでください。