あなた:
なんかwebsocketで再接続の実装をしているんですが、なぜか接続先が落ちていてもOPENにreadyState がなっていて、再接続処理とかを動かせないってことがあります。またその状態でwebsocket.sendが実行されたとき、エラーにならずに送信失敗しているって状態になります。
なるほど、その現象は WebSocket の仕様とブラウザや実装の挙動が関係しています。順を追って整理します。
readyState が OPEN のままになる理由WebSocket.readyState はブラウザ内部で管理されている接続状態のフラグですが、サーバ側が死んでいる/ネットワークが切断されている場合でも、ブラウザ側はすぐにその状態を知れないことがあります。
理由は TCP レベルの仕組み:
WebSocket は TCP 上で動作するプロトコル。
TCP では送信側は ACK を受け取らない限り「送った」とは思えますが、ブラウザは readyState を「TCP コネクションが開かれている」と見なします。
サーバが突然落ちていても、ブラウザは TCP の FIN/RST を受け取らない限り OPEN のままになります。
結果として readyState は OPEN のままになり、再接続判定が走らない。
websocket.send() がエラーにならないけど送信失敗する理由これは同じく TCP 層の挙動です:
ws.send() は送信バッファにデータを詰めるだけ。
TCP がまだ接続を「生きている」と見なしている場合、ブラウザはすぐに例外を投げません。
サーバが応答しない場合、送信自体は失敗している(実際には届いていない)が、ブラウザは非同期的に処理するのでエラーにならないことがあります。
結果として「送信したけど届いてない/エラーは出ない」という状態が発生。
ハートビート/ping
WebSocket で定期的に ping/pong メッセージを送信して、一定時間応答がない場合は再接続する。
例:
jslet pingInterval = setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
try {
ws.send(JSON.stringify({ type: 'ping' }));
} catch (e) {
// 送信失敗したら再接続
reconnect();
}
} else {
reconnect();
}
}, 5000);
送信失敗時の catch
ws.send に try/catch を使うことで、送信不可時に即再接続処理に入れる。
close イベントの監視
ws.onclose はブラウザが TCP の FIN/RST を受け取った時に発火するので、確実に切断を検知できます。
タイムアウト監視
サーバ応答が一定時間ない場合は自前で再接続フラグを立てる。
つまり、ブラウザの readyState だけに頼るとサーバ落ちの検知が遅れる/失敗するので、ハートビートや送信タイムアウトなどの追加監視が必要になります。
もし希望であれば、ブラウザだけでサーバ落ちを確実に検知できるシンプルな再接続ループの例を出すこともできます。