バックグラウンドから戻ったWebSocketのOPENは、確かめるまで主張にすぎない

AppleとAndroidの公式ドキュメントには、バックグラウンドのアプリは一時停止され、ネットワークが止められ、既存の接続が閉じられうると書かれています。つまり、フォアグラウンドに戻ったときの readyState === OPEN は、最後に処理したイベントの記憶にすぎません。この記事では、その記述から「確認する、作り直す、取りこぼしを取り戻す」という小さなルーチンを導き、Linux上の凍結プロセスという代役で動かします。実機では試していない点も、はっきり書きます。

2026年9月12日 | 14 min

WebSocket の Origin 検証が止めるのは他サイトのページで、他のクライアントではない

WebSocket のハンドシェイクにある Origin ヘッダーは、ブラウザが付けるもので、それ以外のクライアントは好きな値を送れます。許可リストが守るのは、Cookie 認証のブラウザセッションが他のサイトから使われることで、接続してきた相手が誰かは証明しません。動くサーバー、実ブラウザでの攻撃、そこから導かれるハンドシェイクの規則を示します。

2026年9月10日 | 11 min

HTTP/2で1本がタイムアウトしたら、閉じるのは接続ではなくストリーム

たくさんのリクエストを同時に流している1本の接続で、1つのリクエストの期限が来たとき、接続ごと閉じるべきか、そのリクエストだけを閉じるべきか。小さなNodeの実験では、HTTP/2のセッションを閉じると無関係な2本が失敗し、ストリームだけをキャンセルすると同じTCP接続のまま2本とも完了しました。ただし、TCPの1パケットの遅れは接続上の全ストリームを止めます。その場合に接続の生死を確かめる方法として、PINGを使った確認も試します。

2026年9月8日 | 17 min

bufferedAmountを待っても守れるのは送信側だけで、遅い受信側は守れない

WebSocketのsend()は待ってくれず、bufferedAmountが見ているのは自分のプロセスの中の待ち行列だけです。小さな実験では、bufferedAmountが減るのを待ってから送る送信側でも、受信側のメモリには、400件のほぼ全部が溜まりました。受信側が処理済みを返す「クレジット窓」(8件)なら、溜まるのは8件で止まりました。send()、bufferedAmount、メッセージサイズ、バックプレッシャーについてよくある4つの思い込みを実際に試し、使い分けの表にまとめます。

2026年9月6日 | 14 min

カーソル・有限ログ・スナップショットで、再接続したイベントストリームを再開する

再接続したクライアントが、状態を取りこぼさず、重複せず、履歴を読み直さずに追いつく方法を、「全部読み直す」から「上限つきのログとスナップショットのフォールバック」まで、版0から版4まで段階的に組み立てます。ブラウザの EventSource が再接続時と non-200 応答のときに実際に何をするか、スナップショットの順序のバグ、決定木も扱います。

2026年9月4日 | 15 min

write() を2回呼ぶだけで、TCPが40ms止まる

ヘッダーと本文を小さな write() 2回に分けて送り、返事を待つと、CPUもネットワークも空いているのに、1往復ごとに約40ms止まることがあります。Nagleアルゴリズムも遅延ACKも、単体では正しい仕様です。2つが組み合わさると、タイマーが切れるまで互いに待ち合ってしまいます。この記事では、停止を予想し、50行のスクリプトで再現し、パケットトレースで確かめ、4つの対処を比べます。

2026年9月1日 | 13 min

リフレッシュトークンをローテーションすると、並行リクエストでログアウトされることがある

リフレッシュトークンのローテーションは、使用済みトークンの再利用を盗難の警報として扱います。期限切れのアクセストークンで3つのリクエストを同時に送り、401ごとに更新するクライアントは、同じリフレッシュトークンを3回提示して、自分で警報を鳴らします。この記事では、素朴なクライアントをあえて壊し、single-flight と stale check で直します。そのうえで、サーバー側の猶予期間、リクエストの待機、先回り更新、送信者制約付きトークンを比べます。

2026年8月30日 | 13 min

リトライで二重に処理される。「ちょうど1回」は作れない

リクエストがタイムアウトしたとき、クライアントはリクエストが失われたのか、確認応答だけが失われたのかを区別できません。だから「ちょうど1回」の配送は作れず、実際にできるのは「少なくとも1回の配送」と「重複を除く受け手」の組み合わせです。不安定なネットワークでの実験、正しく見えて89件を重複させるバグ、原子的な重複排除ストア、ジッターのシミュレーションで確かめます。

2026年8月28日 | 15 min

エッジトリガーのepollは部分読みで止まる。イベント配信も、同じ理由で壊れる

epoll のエッジトリガーは部分的に読むと止まりますが、レベルトリガーは止まりません。同じ違いが、イベントの喪失・重複・順序入れ替えに耐えられるかを分けます。再現できる epoll の実験、損失のあるチャネル上での5通りのシミュレーション、そして見落とされがちな競合(再取得の最中に届いたイベント)を扱う40行ほどの invalidator を紹介します。

2026年8月25日 | 15 min

死んだTCPの相手に気づくまで、書く側は約15分、読むだけの側は無期限

TCP は、相手がいなくなったことを教えてくれません。読み取り側は永遠にブロックし、書き込み側は十数分も再送を続け、SO_KEEPALIVE は未確認のデータがあるあいだ何もしません。この記事では、root 不要の1ファイルの Linux 実験環境を作り、何もしない、keepalive、TCP_USER_TIMEOUT、アプリケーションのハートビートという順に仕組みを積み上げて、気づくまでの時間を測ります。NAT と RFC がどこに関わるかも整理します。

2026年8月23日 | 19 min