backend
S
信頼度ランク
| S | 公式ソース確認済み |
| A | 成功実績多数・失敗例少数 |
| B | 賛否両論 |
| C | 動作未確認・セキュリティリスク高 |
| Z | 個人所感 |
WebSocketとServer-Sent Eventsの使い分け
リアルタイム通信の2大手法、WebSocketとSSE(Server-Sent Events)の違いを整理。チャット・通知・ライブデータなど用途別の選択基準と実装例を解説します。
一言結論
サーバーからの一方向通知(AI応答ストリーム・通知・ライブフィード)にはHTTP上で動き自動再接続するSSEで十分であり、双方向のリアルタイム通信(チャット・ゲーム)が必要な場合にのみWebSocketを選ぶのが正しい使い分けだ。
一言で違いを言うと
| WebSocket | SSE | |
|---|---|---|
| 通信方向 | 双方向(サーバー ↔ クライアント) | 単方向(サーバー → クライアント) |
| プロトコル | ws:// / wss:// | HTTP(通常の https://) |
| 再接続 | 手動実装が必要 | 自動(ブラウザが処理) |
| HTTPプロキシとの相性 | やや悪い | 良い |
| スケーリング | 難しい(接続を保持) | HTTP と同等 |
| ブラウザサポート | 全ブラウザ | 全ブラウザ(IE 除く) |
SSE を選ぶべきケース
- 通知・アラート: サーバーからの一方的な配信
- ライブフィード: 株価・スポーツスコア・ニュース
- AI ストリーミング: ChatGPT 風のトークン逐次配信
- 進捗バー: ファイルアップロード・バッチ処理の進捗
SSE の実装(Node.js / Express)
// サーバー側
app.get("/events", (req, res) => {
res.setHeader("Content-Type", "text/event-stream");
res.setHeader("Cache-Control", "no-cache");
res.setHeader("Connection", "keep-alive");
// 接続ごとに ID を発行
const clientId = Date.now();
// 3秒ごとにデータを送信
const timer = setInterval(() => {
const data = { time: new Date().toISOString(), id: clientId };
res.write(`data: ${JSON.stringify(data)}\n\n`); // \n\n が重要
}, 3000);
req.on("close", () => {
clearInterval(timer);
});
});
// クライアント側(ブラウザ)
const eventSource = new EventSource("/events");
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log("受信:", data);
};
// 名前付きイベント
eventSource.addEventListener("notification", (event) => {
showNotification(JSON.parse(event.data));
});
eventSource.onerror = () => {
console.log("接続エラー(自動再接続中)");
// ブラウザが自動的に再接続する
};
WebSocket を選ぶべきケース
- チャット: メッセージを双方向でリアルタイム送受信
- ゲーム: クライアントの入力をサーバーに即時送信
- コラボレーション: Google Docs 風の共同編集
- トレーディング: 注文・キャンセルをリアルタイム送信
WebSocket の実装(Node.js / ws ライブラリ)
npm install ws
// サーバー側
import { WebSocketServer } from "ws";
const wss = new WebSocketServer({ port: 8080 });
const clients = new Set<WebSocket>();
wss.on("connection", (ws) => {
clients.add(ws);
ws.on("message", (message) => {
const data = JSON.parse(message.toString());
// 全クライアントにブロードキャスト
clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify({ ...data, timestamp: Date.now() }));
}
});
});
ws.on("close", () => clients.delete(ws));
});
// クライアント側
const ws = new WebSocket("wss://example.com/chat");
ws.onopen = () => {
ws.send(JSON.stringify({ type: "join", room: "general" }));
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
appendMessage(data);
};
// 手動で再接続ロジックが必要
ws.onclose = () => {
setTimeout(() => reconnect(), 3000);
};
AI ストリーミング(SSE の実用例)
// ChatGPT 風のトークンストリーミング
app.post("/chat", async (req, res) => {
res.setHeader("Content-Type", "text/event-stream");
const stream = await openai.chat.completions.create({
model: "gpt-5.4-mini",
messages: req.body.messages,
stream: true,
});
for await (const chunk of stream) {
const token = chunk.choices[0]?.delta?.content ?? "";
if (token) {
res.write(`data: ${JSON.stringify({ token })}\n\n`);
}
}
res.write("data: [DONE]\n\n");
res.end();
});
まとめ
クライアント → サーバーの送信が必要?
YES → WebSocket
NO → SSE(シンプルで自動再接続、プロキシとの相性も良い)
SSE は HTTP/2 で多重化できるため、スケールアウトが容易です。クライアントからの送信が不要なら、まず SSE を検討することを推奨します。