はじめに
「チャットアプリって、どうやってリアルタイムにメッセージが届くの?」
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通信
HTTPとWebSocketの根本的な違いを比較すると、次のようになります。
| 比較項目 | HTTP | WebSocket |
|---|---|---|
| 通信方向 | クライアント → サーバー(一方向) | 双方向 |
| 接続 | リクエストごとに接続・切断 | 1回接続したら維持 |
| プロトコル |
http:// / https://
|
ws:// / wss://
|
| ヘッダー | 毎回数百バイト~数KB | 初回のみ。以降は2~14バイト |
| 向いている用途 | CRUD操作、ページ表示 | リアルタイム通信 |
wss:// は ws:// のTLS暗号化版です。HTTPにおける https:// と同じ関係です。本番環境では必ず wss:// を使いましょう。
3. WebSocketハンドシェイク ― 接続確立の仕組み
WebSocket通信は、最初だけHTTPを使って接続を確立します。この仕組みを ハンドシェイク と呼びます。
ステップ解説
① クライアントが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フレーム ― データ転送の仕組み
ハンドシェイク後のデータは フレーム という単位で送受信されます。
フレームが軽量な理由
HTTPでは毎回のリクエスト/レスポンスに 数百バイト~数KBのヘッダー が付きます。一方、WebSocketフレームのヘッダーは わずか2~14バイト です。
この軽量さが、高頻度の双方向通信を実現するカギです。
主なOpcode(フレーム種別)
| Opcode | 種別 | 用途 |
|---|---|---|
0x1 |
テキスト | JSON文字列の送受信 |
0x2 |
バイナリ | 画像・音声データ |
0x8 |
Close | 接続を切断する |
0x9 |
Ping | 接続の死活監視 |
0xA |
Pong | Pingへの応答 |
5. WebSocket接続のライフサイクル
WebSocket接続には4つのフェーズがあります。
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の主なユースケース
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活用や開発効率化について発信しています。フォローお気軽にどうぞ!




