1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【図解でわかるWeb技術⑦】WebSocket ― 双方向リアルタイム通信の仕組みを完全理解する

1
Posted at

はじめに

「チャットアプリって、どうやってリアルタイムにメッセージが届くの?」

HTTPは「リクエスト → レスポンス」の一方通行。サーバーから勝手にデータを送ることはできません。では、SlackやDiscordのようなリアルタイム通信はどう実現しているのでしょうか?

その答えが WebSocket です。この記事では、WebSocketの仕組みをHTTPとの違いから図解で丁寧に解説します。

この記事は 「図解でわかるWeb技術の仕組み」シリーズ の第7回です。
第6回:CDN の仕組みと役割を先に読むと、よりスムーズに理解できます。


1. HTTPの限界 ― なぜWebSocketが必要なのか

HTTPの基本ルール

第1回で学んだとおり、HTTPには 「必ずクライアントから始まる」 という制約があります。

  • クライアントがリクエストを送る
  • サーバーがレスポンスを返す
  • これで1回のリクエスト/レスポンスが完結

つまり、サーバー側で新しいデータが発生しても、クライアントが聞きにこない限り届けられません

ポーリング ― 力技の解決策

HTTPの制約内でリアルタイム性を実現しようとすると、ポーリング(定期的な問い合わせ) という方法になります。

// ポーリングの例(3秒ごとにサーバーに問い合わせ)
setInterval(async () => {
  const res = await fetch('/api/messages');
  const data = await res.json();
  // 新着があれば表示
}, 3000);

しかし、この方法には問題があります。

問題 内容
無駄な通信 新着がなくても毎回リクエストを送る
遅延 ポーリング間隔(例: 3秒)だけタイムラグが発生
サーバー負荷 ユーザー数 × ポーリング間隔分のリクエストが常に飛ぶ

2. HTTP通信 vs WebSocket通信

07_websocket_http_vs_ws.png

HTTPとWebSocketの根本的な違いを比較すると、次のようになります。

比較項目 HTTP WebSocket
通信方向 クライアント → サーバー(一方向) 双方向
接続 リクエストごとに接続・切断 1回接続したら維持
プロトコル http:// / https:// ws:// / wss://
ヘッダー 毎回数百バイト~数KB 初回のみ。以降は2~14バイト
向いている用途 CRUD操作、ページ表示 リアルタイム通信

wss://ws:// のTLS暗号化版です。HTTPにおける https:// と同じ関係です。本番環境では必ず wss:// を使いましょう。


3. WebSocketハンドシェイク ― 接続確立の仕組み

WebSocket通信は、最初だけHTTPを使って接続を確立します。この仕組みを ハンドシェイク と呼びます。

07_websocket_handshake.png

ステップ解説

① クライアントがUpgradeリクエストを送る

GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZS4uLg==
Sec-WebSocket-Version: 13

ポイントは Upgrade: websocket ヘッダー。「HTTPからWebSocketに切り替えたい」とサーバーに伝えます。

② サーバーが101で応答する

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

ステータスコード 101 Switching Protocols は「プロトコル切り替えに同意した」という意味です。

③ WebSocket通信開始

ハンドシェイク完了後は、HTTPではなく WebSocketプロトコル でフレーム単位のデータを双方向にやり取りします。

ハンドシェイクはHTTPで行われるため、既存のHTTPインフラ(プロキシ、ロードバランサー等)を通過できるのがWebSocketの設計上の利点です。ただし、一部のプロキシはWebSocketに対応していない場合があるので注意が必要です。


4. WebSocketフレーム ― データ転送の仕組み

ハンドシェイク後のデータは フレーム という単位で送受信されます。

07_websocket_frame.png

フレームが軽量な理由

HTTPでは毎回のリクエスト/レスポンスに 数百バイト~数KBのヘッダー が付きます。一方、WebSocketフレームのヘッダーは わずか2~14バイト です。

この軽量さが、高頻度の双方向通信を実現するカギです。

主なOpcode(フレーム種別)

Opcode 種別 用途
0x1 テキスト JSON文字列の送受信
0x2 バイナリ 画像・音声データ
0x8 Close 接続を切断する
0x9 Ping 接続の死活監視
0xA Pong Pingへの応答

5. WebSocket接続のライフサイクル

WebSocket接続には4つのフェーズがあります。

07_websocket_lifecycle.png

JavaScript APIでの実装例

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

// 接続成功
ws.onopen = () => {
  console.log('接続しました');
  ws.send(JSON.stringify({ type: 'join', room: 'general' }));
};

// メッセージ受信
ws.onmessage = (event) => {
  const data = JSON.parse(event.data);
  console.log('受信:', data);
};

// エラー発生
ws.onerror = (error) => {
  console.error('WebSocketエラー:', error);
};

// 接続切断
ws.onclose = (event) => {
  console.log(`切断: code=${event.code}, reason=${event.reason}`);
  // 必要に応じて再接続
};

再接続のベストプラクティス

本番環境では、ネットワーク障害による切断に備えて 自動再接続 を実装します。

function connectWithRetry(url, maxRetries = 5) {
  let retries = 0;

  function connect() {
    const ws = new WebSocket(url);

    ws.onopen = () => {
      retries = 0; // 成功したらリセット
    };

    ws.onclose = () => {
      if (retries < maxRetries) {
        retries++;
        const delay = Math.min(1000 * 2 ** retries, 30000); // 指数バックオフ
        setTimeout(connect, delay);
      }
    };

    return ws;
  }

  return connect();
}

指数バックオフ とは、再接続の間隔を 1秒 → 2秒 → 4秒 → 8秒 → ... と指数的に増やす手法です。サーバーが復旧する前に大量の再接続リクエストが殺到するのを防ぎます。


6. WebSocketの主なユースケース

07_websocket_usecases.png

WebSocketを使うべき場面

  • 双方向のリアルタイム通信が必要(チャット、ゲーム)
  • サーバーからのプッシュが頻繁に発生(通知、ライブ更新)
  • 低遅延が求められる(株価ティッカー、IoT)

WebSocketを使わなくてよい場面

ケース 代替手段
単純なCRUD操作 REST API
サーバー→クライアントの一方向配信 SSE(Server-Sent Events)
低頻度の更新確認 ロングポーリング

SSE(Server-Sent Events) は、サーバーからクライアントへの一方向のストリーミングに特化したAPI。チャットのようにクライアントからも送信する必要がなければ、SSEの方がシンプルに実装できます。


7. まとめ

項目 ポイント
WebSocketとは クライアントとサーバーが常時接続で双方向通信するプロトコル
HTTPとの違い HTTPは毎回接続/切断、WebSocketは1回接続したら維持
ハンドシェイク 最初はHTTPでUpgradeリクエスト → 101応答 → WebSocketに切替
フレーム ヘッダーが2~14バイトと軽量。テキスト/バイナリ/制御フレームがある
ライフサイクル open → message → error → close の4イベント
ユースケース チャット、リアルタイムダッシュボード、ゲーム、共同編集など
注意点 常時接続のため、再接続・死活監視の設計が重要

次回予告

第8回では 「キャッシュ戦略(ブラウザ・サーバー・CDN)」 を図解で解説します。Cache-ControlやETagを正しく理解して、Webアプリのパフォーマンスを劇的に改善する方法を学びます。


シリーズ目次

# テーマ
1 HTTPリクエスト/レスポンスの仕組み
2 Cookie・Session・JWTの違い
3 OAuth 2.0 の仕組み
4 DNS解決の流れ
5 HTTPS・TLS通信の仕組み
6 CDN の仕組みと役割
7 WebSocket ― 双方向リアルタイム通信(この記事)
8 キャッシュ戦略(ブラウザ・サーバー・CDN)
9 CORS ― クロスオリジンの壁を理解する
10 REST API 設計の基本原則

「この記事が役に立った」と思ったら、LGTM とストックをお願いします。

@kotaro_ai_lab
AI活用や開発効率化について発信しています。フォローお気軽にどうぞ!

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?