C
ネットワーク/リアルタイム/Lesson 08

リアルタイム通信 — WebSocket · SSE · Long Polling

30分·theory

リアルタイム通信 — WebSocket · SSE · Long Polling

🎯 このlessonを読み終えたら

このlessonを読み終えると、以下の3つを自信を持って説明できるようになります。

  • ✅ WebSocket vs SSE vs Long Polling の違い
  • ✅ socket.io のフォールバックメカニズム
  • ✅ WebRTC の P2P 接続 + STUN/TURN

学習目標をチェックリストとして手元に置き、すべて答えられるようになったらlessonを閉じてください。

リアルタイム通信4方式の比較

リアルタイム = サーバー → クライアントへのプッシュ(または双方向)

方式方向プロトコル用途
Pollingクライアント pullHTTP軽量ポーリング(例: 5秒ごと)
Long Pollingpull(長い待機)HTTPチャット・通知(イベントまで待機)
SSEサーバー → クライアントHTTP通知・ライブフィード(単方向)
WebSocket双方向WS/WSSチャット・ゲーム・リアルタイム協働
gRPC streaming双方向HTTP/2内部 MSA
WebRTCP2PUDP映像・音声

選択ガイド:

  • 🟢 単方向通知 → SSE(シンプル・自動再接続)
  • 🟢 双方向チャット・ゲーム → WebSocket
  • 🟢 P2P 映像 → WebRTC
  • 🟢 ポーリング + 互換性 → Long Polling(レガシーブラウザ)
  • 🔴 単純なポーリング → できるだけ避ける

WebSocket の動作 + 使用パターン

WebSocket = 1本の TCP 接続で双方向メッセージ:

ハンドシェイク(HTTP → WS アップグレード):

code
GET /ws HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: <base64>
Sec-WebSocket-Version: 13

レスポンス:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Sec-WebSocket-Accept: <hash>

クライアント(ブラウザ):

javascript
const ws = new WebSocket('wss://example.com/ws');

ws.onopen = () => ws.send(JSON.stringify({ type: 'subscribe', room: 'general' }));
ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  console.log(msg);
};
ws.onerror = (e) => console.error(e);
ws.onclose = () => console.log('接続終了');

サーバー(Node.js + ws):

javascript
const { WebSocketServer } = require('ws');
const wss = new WebSocketServer({ port: 8080 });

wss.on('connection', (ws) => {
  ws.on('message', (data) => {
    const msg = JSON.parse(data);
    if (msg.type === 'chat') {
      // ブロードキャスト
      wss.clients.forEach(c => {
        if (c.readyState === WebSocket.OPEN) {
          c.send(JSON.stringify({ user: msg.user, text: msg.text }));
        }
      });
    }
  });
});

実務ライブラリ:

  • Socket.IO — フォールバック(XHR · Long polling)、ルーム・ネームスペース
  • Pusher · Ably · Supabase Realtime — マネージドサービス
  • Centrifugo · Mercure — セルフホスティング

スケーリングパターン:

  • Redis Pub/Sub — 複数サーバー間のメッセージルーティング
  • スティッキーセッション — 同一ユーザーは同一サーバーへ(または Redis で状態共有)
  • ハートビート(ping/pong) — 切断した接続の検出(通常30秒間隔)

SSE + Long Polling

SSE(Server-Sent Events)サーバー → クライアントの単方向ストリーム:

クライアント(ブラウザ):

javascript
const es = new EventSource('/api/notifications');
es.onmessage = (event) => {
  const data = JSON.parse(event.data);
  showNotification(data);
};
es.onerror = () => console.log('再接続中...');     // 自動再接続
es.close();    // 終了

サーバー(Node.js):

javascript
app.get('/api/notifications', (req, res) => {
  res.setHeader('Content-Type', 'text/event-stream');
  res.setHeader('Cache-Control', 'no-cache');
  res.setHeader('Connection', 'keep-alive');

  const interval = setInterval(() => {
    res.write(`data: ${JSON.stringify({ msg: 'hi', time: Date.now() })}

`);
  }, 1000);

  req.on('close', () => clearInterval(interval));
});

SSE のメリット:

  • HTTP 標準(ファイアウォール・プロキシフレンドリー)
  • 自動再接続
  • WebSocket より シンプル
  • Last-Event-ID ヘッダーで切断箇所から再開

SSE のデメリット:

  • 単方向のみ(クライアント → サーバーは別途 POST が必要)
  • IE 非対応(現在は無視可)
  • HTTP/1.1 の同時接続数制限(HTTP/2 以上を推奨)

Long Polling — レスポンスをできる限り遅らせる:

code
1. クライアント: GET /poll?last=42
2. サーバー: 新しいイベントが来るまで*レスポンスを保留*(最大30秒)
3. 新しいイベント → 即座にレスポンス
4. または30秒タイムアウト → 空レスポンス
5. クライアント: 即座に再ポーリング(1へ戻る)

用途:

  • レガシーブラウザ(IE9-)
  • WebSocket · SSE がブロックされた環境
  • 単純な通知(1秒以内の遅延が許容される場合)

多くの場合 → WebSocket · SSE の方が効率的

🤖 AIへのリクエスト例

このlessonの概念を理解すれば、AIに具体的に指示できるようになります。漠然とした「直して」ではなく、語彙を持ったリクエスト — それがトークン節約の出発点です。

  • 「このpollingコードをWebSocket(socket.io)に移行して」
  • 「このSSEをWebSocket vs SSEで比較し、適切な方に整理して」

なぜこれがトークンを減らすのか

概念を知らないままAIの回答を受け取っても、「それは何ですか?」とまた聞き直す必要があります。その「聞き直し」がトークンを消費します。概念を一度理解しておけば、会話が一度で終わります

リアルタイム通信 — WebSocket・SSE・ロングポーリング - ネットワーク