🔖

WebSocket通信とは?HTTPとの違いと基本的な仕組み

に公開

はじめに

リアルタイムなチャットや通知機能などを実装する際、「WebSocket通信」という言葉を目にすることがあります。
しかし「HTTPとどう違うのか」「なぜ必要なのか」がいまひとつ掴めないという方も多いかもしれません。

本記事では、WebSocket通信の基本的な仕組みと、HTTPリクエストとの違いを解説します。
また、initialize / finalize についても簡単に触れます。

HTTP通信との違いを理解する

まずは、最もよく使われるHTTP通信(例:POST /api/message)との違いを見てみましょう。

項目 HTTP通信(POSTなど) WebSocket通信
通信の開始 クライアントが毎回リクエストを送信 最初に一度だけ接続を確立
接続の維持 通信は1回ごとに終了 接続は継続(常時接続)
通信方向 クライアント → サーバー クライアント ↔ サーバー(双方向)
主な用途 API通信、データ登録、ファイル送信など チャット、ゲーム、通知、株価更新など

HTTP通信は、必要なときにリクエストを送り、そのたびにサーバーと一時的に接続する方式です。
これに対しWebSocket通信は、一度接続を確立するとそのまま通信が維持され、双方向で自由にデータをやり取りできます。

イメージで捉える:郵便と電話

通信のイメージを比喩で表すと、次のようになります。

  • HTTP通信:郵便のような仕組み
     手紙(リクエスト)を送ると、返事(レスポンス)が届いて通信は終了します。
     やり取りのたびに新しい封筒を準備する必要があります。

  • WebSocket通信:電話のような仕組み
     一度つながると、そのまま会話を続けられます。
     サーバー側から話しかける(通知を送る)ことも可能です。

この「つながったまま話せる」という特性が、リアルタイム性を求めるアプリケーションで活用されています。

initializefinalizeについて

WebSocket通信そのものは「通路」を提供する仕組みであり、
その中でどのようなメッセージをやり取りするかは、アプリケーション設計によって異なります。

例えば、通信の始まりと終わりを明確にするために
initialize コマンドと finalize コマンドを定義することがあります。

  • initialize:通信の開始をサーバーへ知らせる
  • finalize:通信の終了をサーバーへ知らせる

これらはWebSocketの標準仕様ではなく、アプリ側で独自に定めるプロトコルです。
会話で言えば「これから話を始めます」「話はここまでです」と区切るような役割を持ちます。

簡単な例(Python)

main.py
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