WebSocket通信とは?HTTPとの違いと基本的な仕組み
はじめに
リアルタイムなチャットや通知機能などを実装する際、「WebSocket通信」という言葉を目にすることがあります。
しかし「HTTPとどう違うのか」「なぜ必要なのか」がいまひとつ掴めないという方も多いかもしれません。
本記事では、WebSocket通信の基本的な仕組みと、HTTPリクエストとの違いを解説します。
また、initialize / finalize についても簡単に触れます。
HTTP通信との違いを理解する
まずは、最もよく使われるHTTP通信(例:POST /api/message)との違いを見てみましょう。
| 項目 | HTTP通信(POSTなど) | WebSocket通信 |
|---|---|---|
| 通信の開始 | クライアントが毎回リクエストを送信 | 最初に一度だけ接続を確立 |
| 接続の維持 | 通信は1回ごとに終了 | 接続は継続(常時接続) |
| 通信方向 | クライアント → サーバー | クライアント ↔ サーバー(双方向) |
| 主な用途 | API通信、データ登録、ファイル送信など | チャット、ゲーム、通知、株価更新など |
HTTP通信は、必要なときにリクエストを送り、そのたびにサーバーと一時的に接続する方式です。
これに対しWebSocket通信は、一度接続を確立するとそのまま通信が維持され、双方向で自由にデータをやり取りできます。
イメージで捉える:郵便と電話
通信のイメージを比喩で表すと、次のようになります。
-
HTTP通信:郵便のような仕組み
手紙(リクエスト)を送ると、返事(レスポンス)が届いて通信は終了します。
やり取りのたびに新しい封筒を準備する必要があります。 -
WebSocket通信:電話のような仕組み
一度つながると、そのまま会話を続けられます。
サーバー側から話しかける(通知を送る)ことも可能です。
この「つながったまま話せる」という特性が、リアルタイム性を求めるアプリケーションで活用されています。
initialize と finalizeについて
WebSocket通信そのものは「通路」を提供する仕組みであり、
その中でどのようなメッセージをやり取りするかは、アプリケーション設計によって異なります。
例えば、通信の始まりと終わりを明確にするために
initialize コマンドと finalize コマンドを定義することがあります。
-
initialize:通信の開始をサーバーへ知らせる -
finalize:通信の終了をサーバーへ知らせる
これらはWebSocketの標準仕様ではなく、アプリ側で独自に定めるプロトコルです。
会話で言えば「これから話を始めます」「話はここまでです」と区切るような役割を持ちます。
簡単な例(Python)
from __future__ import annotations
import json
from typing import Any, Dict, Optional
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
app = FastAPI(title="WebSocket initialize/finalize demo")
class SessionState:
"""接続(1 WebSocket)単位で保持する状態"""
def __init__(self) -> None:
self.initialized: bool = False
self.user_id: Optional[int] = None
self.context: Dict[str, Any] = {}
@app.websocket("/ws")
async def websocket_endpoint(ws: WebSocket):
# 接続を受理
await ws.accept()
state = SessionState()
try:
while True:
raw = await ws.receive_text()
# JSONパース
try:
msg = json.loads(raw)
except json.JSONDecodeError:
await ws.send_json({"type": "error", "error": "invalid_json"})
continue
command = msg.get("command")
# ---- コマンド分岐 ----
if command == "initialize":
# 例:user_idなどの初期パラメータを受け取り、状態に保存
state.initialized = True
state.user_id = msg.get("user_id")
# 付随情報があれば context に積む
state.context.update(msg.get("context", {}))
await ws.send_json({
"type": "ack",
"event": "initialized",
"initialized": state.initialized,
"user_id": state.user_id,
})
elif command == "finalize":
# 終了の明示(アプリ内プロトコル)
await ws.send_json({
"type": "ack",
"event": "finalized"
})
# 物理的な切断(WebSocketを閉じる)
await ws.close()
break
elif command == "echo":
# 例:任意のアプリ処理。initialize 前は拒否するなどの制御が可能
if not state.initialized:
await ws.send_json({"type": "error", "error": "not_initialized"})
continue
await ws.send_json({
"type": "message",
"event": "echo",
"payload": msg.get("payload"),
"user_id": state.user_id,
})
elif command == "ping":
await ws.send_json({"type": "pong"})
else:
await ws.send_json({"type": "error", "error": "unknown_command", "command": command})
except WebSocketDisconnect:
# クライアント側が切断した場合の後片付け(必要に応じて)
# ここで state を使ってログ出しなどを行ってもよい
return
まとめ
- HTTPは「郵便のような一回限りの通信」
- WebSocket通信は「電話のように常に開いた通信」
-
initialize/finalizeは通信の開始・終了を明示するための独自コマンド
WebSocket通信を理解することで、チャットや通知、位置情報の更新など、リアルタイム性の高い機能を設計しやすくなります。
まずは基本的な動作原理を押さえ、アプリに必要な場面で活用していきましょう。
Discussion