NET // communication networks
LESSON 34 / 発展編

WebSocket ─ ブラウザの双方向通信

HTTP は本質的に「クライアントが要求 → サーバが応答」の 片方向 プロトコルです。チャットやリアルタイム株価表示のように、サーバから能動的にプッシュしたい用途には不向き。WebSocket(RFC 6455, 2011年) はこの制約を、HTTP の上から「Upgrade」して双方向 TCP を確立するという発想で解決しました。本講ではハンドシェイクの仕組み、フレーム構造、Ping/Pong による接続維持、TLS 化(wss://)、そして Python と JavaScript で最小チャットを動かすハンズオンまで踏み込みます。最後に SSE・Long Polling・WebRTC・socket.io との使い分けも整理します。

学習目標

本講を終えると、以下を達成できるようになります。

本講は 標準 HTTP/1.1・HTTP/2標準 TLS/PKI、発展編の 第28回 ネットワークプログラミング第29回 HTTP プログラミング を前提とします。「TCP の上に独自プロトコルを設計する」という見方が腹落ちしていると、フレーム構造の必然性が見えやすくなります。

このレッスンの目次

01 なぜ WebSocket か HTTP は片方向。チャット・株価・通知のためにサーバプッシュが要る… 02 ハンドシェイク HTTP リクエスト → 101 Switching Protocols で TCP を双方向通信に「昇格」… 03 フレーム構造 FIN・opcode・MASK・payload length。最短 2 バイトのコンパクトなヘッダ… 04 Text と Binary JSON は Text、画像や独自バイナリは Binary。フラグメンテーションも… 05 制御フレーム Ping/Pong で生存確認、Close で正常切断。ブラウザは自動 Pong を返す… 06 wss と TLS 本番では wss:// 必須。Mixed Content の問題、443 番ポートの相乗り… 07 ハンズオン Python の websockets と JavaScript の WebSocket API で最小チャット… 08 関連技術比較 SSE / Long Polling / WebRTC / socket.io ─ 用途で使い分ける… 09 まとめと用語 本講の重要語句を整理 10 確認問題 5 問で理解度をチェック

なぜ WebSocket か ─ HTTP の限界とリアルタイム化

HTTP は「クライアントが要求し、サーバが1回応答する」リクエスト/レスポンス型です。「サーバ側で何かが起きたらクライアントに即座に伝えたい」用途では、これがそのまま不便さになります。WebSocket は HTTP の上から TCP を双方向通信に変換する ことでこの問題を一気に解決します。
POINT HTTP の限界:
・サーバから能動的にメッセージを送れない(クライアントが訊きに行くしかない)
・1リクエスト 1レスポンスで完結 → 接続のオーバーヘッドが繰り返し発生
・低遅延の連続更新(チャット・対戦ゲーム・株価表示)に向かない

WebSocket の解決:
・最初だけ HTTP でハンドシェイクし、以降は 同じ TCP コネクション上で双方向にメッセージを流す
・1 つのフレーム = 数バイトのオーバーヘッドだけ。HTTP ヘッダ繰り返しなし
・ファイアウォール・プロキシ越しでも 80/443 を使うので通る

「HTTP だけで頑張る」古い手法と比べると

3 つの方式の通信パターン ① 短周期ポーリング 何度も「変化ありますか?」 無駄なリクエスト多発 ② Long Polling (サーバは更新があるまで保留) (再度保留) サーバが応答を「貯める」 少しマシだが効率悪い ③ WebSocket 同じ TCP どちらからでも自由に 小さなフレーム送信

図の見方:① の短周期ポーリングは「とりあえず何回も訊く」方式。HTTP ヘッダのオーバーヘッドが毎回発生し、無駄が多い。② Long Polling はサーバが応答を保留することで頻度を下げるが、本質はリクエスト型のまま。③ WebSocket は最初だけ HTTP で「ハンドシェイク」し、以降は 1本の TCP の上で自由に双方向メッセージ を流せる。

つながる知識: HTTP/2 にも Server Push がありますが、これはあくまで「応答に付随してファイルを先送りする」用途であって、長時間の双方向対話には設計されていません(2022 年の Chrome では既定で無効化された)。HTTP/3 でも同様。「ブラウザとサーバの双方向対話」を素直に表現できるのは依然として WebSocket、または UDP ベースの WebTransport(2024 年現在まだ標準化途中)です。

確認: 通常の HTTP では実現困難で、WebSocket が解決した「双方向通信」とは具体的にどのような能力か?

正解:C。HTTP は基本的に「クライアントが要求 → サーバが応答」のリクエスト/レスポンス・モデル。サーバ側からの能動送信が必要だと、Long Polling や Comet のような擬似的な手段で「リクエストを開いたまま応答を遅延させる」工夫が必要だった。WebSocket は最初から双方向のコネクションを張る設計で、サーバが好きなタイミングでフレームを送れる。チャット・通知・株価・オンラインゲームでこれが必須になる。

ハンドシェイク ─ HTTP Upgrade で「昇格」する

WebSocket 接続は 必ず HTTP リクエストから始まります。クライアントは特殊なヘッダ Upgrade: websocket を含んだ HTTP/1.1 GET を送り、サーバが 101 Switching Protocols で承認すれば、その瞬間からその TCP コネクションは WebSocket フレームをやり取りする回線 に切り替わります。
POINT ハンドシェイクの 4 要素:
1. クライアント:GET /chat HTTP/1.1 + Upgrade: websocket + Connection: Upgrade
2. クライアント:ランダム 16 バイトを Base64 した Sec-WebSocket-Key を送る
3. サーバ:Key に固定文字列 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 を連結 → SHA-1 → Base64 して Sec-WebSocket-Accept として返す
4. サーバ:HTTP/1.1 101 Switching Protocols + 同じ Upgrade/Connection ヘッダ
→ 以降この TCP は WebSocket プロトコルで動く

実際のリクエスト/レスポンス例

# クライアント → サーバ
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
Origin: https://example.com

# サーバ → クライアント
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

# ↓ ここから先は WebSocket フレームでの双方向通信

Sec-WebSocket-Accept の計算

「キャッシュされた古い HTTP 応答を WebSocket 開始と勘違いしないため」 の検証手順です。サーバはクライアントの Key にマジックストリングを足してハッシュを取り、Accept として返します。クライアントは同じ計算をして突き合わせ、一致しなければ接続を破棄します。

# Python で確認
import hashlib, base64
key = "dGhlIHNhbXBsZSBub25jZQ=="
magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
accept = base64.b64encode(hashlib.sha1((key + magic).encode()).digest()).decode()
print(accept)  # → s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

考えてみよう: なぜ HTTP/1.1 GET から始まるのでしょうか?TCP を直接立てて独自プロトコルを話せば、Upgrade のような遠回りは不要なはずです。理由は 「インターネット中のプロキシ・ファイアウォール・LB が HTTP しか通さない」 という現実への適応です。HTTP リクエストとして見えれば、80/443 を経由してまず通り、その後で素性を変えられる。レガシーインフラに優しい新プロトコルの設計テクニック として知られています。

Q. なぜ WebSocket は独自ポート(例えば 81 番のような新しい予約ポート)を使わず、わざわざ 80/443 で HTTP として始めて Upgrade するという回りくどい設計を採用したのか?

フレーム構造 ─ 最短 2 バイトのコンパクトなヘッダ

ハンドシェイクが終わると、以降のデータは WebSocket フレーム という独自形式でやり取りされます。HTTP のように毎回ヘッダを並べる方式ではなく、2 バイト程度の極小ヘッダ + ペイロード で済むのが特徴。チャットの 1 メッセージは数十バイト級で運べます。

フレームのレイアウト(RFC 6455 §5.2)

WebSocket フレーム構造(各セルは 1 ビット) 0 7 8 15 16 23 F RSV1-3 opcode (4 bit) M payload len (7) 拡張 payload 長(16 bit / 64 bit)── len が 126 / 127 の時のみ Masking key(32 bit)── MASK=1 の時のみ Payload Data(可変長 / MASK=1 ならクライアント→サーバはマスク済み) Text フレームなら UTF-8 文字列 / Binary フレームなら任意のバイト列 F = FIN(このフレームで完結) RSV1-3 = 拡張用予約(普段 0) opcode = フレーム種別(下表参照) M = MASK(クライアント→サーバは必須) payload len = 7 / 7+16 / 7+64 ビット

図の見方:最小構成は FIN+opcode+MASK+payload len = 2 バイト。ペイロードが 125 バイト以下なら 7 ビットの payload len 欄に収まり、追加ヘッダなし。126 バイト以上なら 16 ビット拡張、64 KB 以上なら 64 ビット拡張が登場します。HTTP のように Content-Type: 等を毎回送らないので、短いメッセージほど効率的です。

opcode の種類

opcode意味用途
0x0continuation分割フレームの続き
0x1textUTF-8 文字列(JSON 等)
0x2binary任意バイト列(画像・Protobuf 等)
0x8close切断要求(ステータスコード付き)
0x9ping生存確認(送る側)
0xApong生存確認(返す側)
もっと詳しく:なぜマスクが必要なのか

WebSocket フレームの クライアント → サーバ方向は必ずマスク が施されます(MASK=1、4 バイトの masking key で XOR)。これは キャッシュ汚染攻撃(cache poisoning) 対策。中間プロキシが WebSocket を理解せず、ペイロードを HTTP メッセージと誤解してキャッシュに入れてしまうのを防ぐため、毎フレームでバイト列を撹拌します。サーバ → クライアント方向はマスクなし(MASK=0)で OK。

Text と Binary、フラグメンテーション

WebSocket のメッセージは Text フレーム(opcode 0x1)Binary フレーム(opcode 0x2) のいずれかです。両者は単純に「中身を UTF-8 と解釈するかどうか」が違うだけ。実用上は JSON は Text、画像・Protobuf・MessagePack は Binary という使い分けが多い。

使い分けの目安

Text(0x1)

  • JSON でメッセージを表現する場合
  • デバッグ時にブラウザの DevTools で読みやすい
  • UTF-8 として有効でない場合は接続が切れる(これも仕様)
  • JS では socket.send("...") で送る

Binary(0x2)

  • 画像、音声、Protobuf、MessagePack など
  • JSON より サイズ・速度で圧倒的に有利(ゲーム・IoT で重要)
  • JS では socket.send(arrayBuffer) または socket.binaryType = "arraybuffer"
  • デバッグは hex ダンプが必要

フラグメンテーション ─ 1 メッセージを複数フレームに割れる

大きいメッセージは FIN=0 のフレームを複数連ねて、最後に FIN=1 で送るルール。これによりストリーミング的に流せます。例:1 GB のファイル送信を 1 フレームではなく、64 KB ずつのフラグメントに分割。

# 例:大きい binary を 3 フラグメントに分割
[FIN=0, opcode=0x2, payload=...]   ← 最初のフラグメント(binary 開始)
[FIN=0, opcode=0x0, payload=...]   ← 続き(continuation)
[FIN=1, opcode=0x0, payload=...]   ← 最後の続き(完結)

つながる知識: 制御フレーム(Ping/Pong/Close)は フラグメント化できません。これは「制御メッセージは即座に確実に届く必要がある」ためで、データフラグメントの最中でも割り込んで送られます。これは TCP / IP の世界における「制御プレーンとデータプレーンの分離」発想を、アプリ層プロトコルの設計に持ち込んだ例として読めます。

確認: WebSocket のフラグメンテーション(分割送信)について、正しい記述はどれか。

正解:B。データは大きなファイルを 1 MB ずつ送るような用途で分割でき、各フレームの FIN ビットで「これが最後か」を表現する。一方、制御フレームは「即座に届くべき」という設計思想から分割禁止で、最大ペイロード 125 バイト。これにより閉じる宣言や Ping/Pong 応答がデータフラグメントの隙間で途切れず確実に処理できる ─ TCP の制御セグメントが優先転送されるのと同じ設計感覚。

制御フレーム ─ Ping / Pong / Close

制御フレームは 接続管理のための小さなメッセージ です。Ping/Pong は生存確認、Close は正常切断を表します。これらはアプリのデータとは別の opcode で送られ、ブラウザ実装は Ping を受けたら自動で Pong を返してくれます。
POINT 制御フレームの3つ:
Ping(0x9):任意のタイミングで生存確認。125 バイト以下のペイロード可
Pong(0xA):Ping への応答。Ping のペイロードをそのまま echo するのが慣例
Close(0x8):正常切断。先頭 2 バイトに ステータスコード(1000=Normal, 1001=Going Away, 1006=Abnormal)を入れる

Ping/Pong による接続維持

WebSocket 接続は長時間維持されます。途中の NAT・LB・プロキシ がアイドル状態の TCP を勝手に切るのを防ぐには、定期的な Ping/Pong が有効です。多くのライブラリではデフォルト 20〜60 秒間隔で送信します。

クライアント サーバ opcode 0x9 ping(任意のペイロード) opcode 0xA pong(同じペイロードを echo) 数十秒に1回繰り返し → NAT/LB のタイムアウトを回避

図の見方:Ping/Pong は アプリのデータとは別レーン で流れる小さな存在確認メッセージ。これがあるおかげで、何時間も発言のないチャットルームでも切断されません。

Close ステータスコード

コード意味
1000Normal Closure ─ 正常終了
1001Going Away ─ ページ遷移・サーバ停止
1002Protocol Error
1003Unsupported Data ─ 受信できない種別
1006Abnormal ─ Close フレームなしの切断(ライブラリが内部で生成、実際には送られない)
1011Internal Error ─ サーバ内部エラー

確認: 多くの WebSocket 実装で「数十秒に 1 回 Ping を送る」運用が標準的なのはなぜか?

正解:C。NAT 機器や LB は「無通信が数分続いたら切断」というタイマを持つことが多く、放置するとアプリは何もしなくても接続が消える(再接続が必要)。Ping/Pong は NAT バインディングを維持するためのアイドルアクティビティ として機能する。Cloudflare・AWS ALB など多くの環境で、idle timeout(60〜300 秒)があるため、それを下回る間隔で Ping するのが運用上のお作法になっている。

wss:// と TLS over WebSocket

プロトコル名としての ws://(平文)と wss://(TLS 化)は、HTTP に対する HTTPS と完全に並行関係にあります。本番環境では wss:// 一択。理由はトラフィックの盗聴防止だけでなく、ブラウザのセキュリティ制約(HTTPS ページから ws:// は接続不可= Mixed Content)もあります。
POINT ws:// と wss:// の違い:
ws:// ─ TCP 80 上の WebSocket(平文、デバッグ用)
wss:// ─ TLS 1.2/1.3 で暗号化された TCP 443 上の WebSocket(本番)
・HTTPS のページからは 必ず wss:// を使う(ブラウザが ws:// 接続を Mixed Content として拒否)
・Let's Encrypt 等で取得した サーバ証明書を nginx/Caddy で TLS 終端 し、内部は ws のまま、が定石

典型的な wss 構成

ブラウザ nginx / Caddy TLS 終端 + リバースプロキシ アプリサーバ wss:// (TLS 1.3 暗号化) ws:// (内部 LAN は平文)

図の見方:外向きは TLS で暗号化、内側のアプリサーバとのやり取りは平文 ws のまま、というのが運用上ほぼ標準。アプリサーバは TLS 処理から解放され、nginx 側で証明書管理・HTTP/2/3 終端・WebSocket 中継が一元化できる。

もっと詳しく:nginx でリバースプロキシする最小設定
location /chat {
    proxy_pass http://127.0.0.1:8000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade    $http_upgrade;     # ← 必須
    proxy_set_header Connection "upgrade";          # ← 必須
    proxy_set_header Host       $host;
    proxy_read_timeout 86400;                       # 長寿命化
}

ポイントは UpgradeConnection: upgrade ヘッダの透過。これらが欠けると nginx が WebSocket と認識せず、HTTP 短命接続として扱ってしまいます。

ハンズオン ─ Python と JS で双方向チャット

実際に動かしてみると、WebSocket は驚くほど簡潔に書けます。ここでは Python の websockets ライブラリ でサーバを立て、ブラウザの素の WebSocket API でクライアントを書く、最小チャットを構成します。

サーバ:Python

まずは依存をインストール。

python3 -m pip install websockets
# server.py — 全クライアントにメッセージをブロードキャスト
import asyncio, websockets

CLIENTS = set()

async def handler(ws):
    CLIENTS.add(ws)
    try:
        async for msg in ws:                        # 受信ループ
            print("recv:", msg)
            await asyncio.gather(
                *[c.send(f"<> {msg}") for c in CLIENTS if c is not ws]
            )
    finally:
        CLIENTS.remove(ws)

async def main():
    async with websockets.serve(handler, "localhost", 8765):
        print("listening on ws://localhost:8765/")
        await asyncio.Future()                      # 永久に待機

asyncio.run(main())

クライアント:HTML + JavaScript

<!doctype html>
<input id="msg" placeholder="message" />
<button id="send">send</button>
<ul id="log"></ul>

<script>
  const ws = new WebSocket("ws://localhost:8765/");

  ws.addEventListener("open",    () => console.log("connected"));
  ws.addEventListener("message", e  => {
    const li = document.createElement("li");
    li.textContent = e.data;
    document.getElementById("log").appendChild(li);
  });
  ws.addEventListener("close",   e  => console.log("closed", e.code));

  document.getElementById("send").addEventListener("click", () => {
    const v = document.getElementById("msg").value;
    ws.send(v);
  });
</script>

動作確認

1) python3 server.py でサーバ起動 → 2) HTML をブラウザで複数タブ開く → 3) どれかのタブで送信すると 他のタブ全てに表示される。これだけで「双方向チャットの背骨」が完成します。本番運用するには認証・room 機能・永続化を足していくだけ。

ブラウザの DevTools で WebSocket を観察する のは強くオススメ。Chrome の Network → WS タブで、フレーム単位で送受信内容(opcode・payload)が確認できます。Ping/Pong も可視化されるので、ライブラリの挙動が「目に見える」状態で勉強できます。

つながる知識: 上のサーバ実装は async / await を多用しています。これは 1 プロセスで多数の長寿命コネクションを捌く ための現代的な設計。WebSocket のように接続が長時間生きる用途では、スレッドあたり 1 接続のモデルではメモリが足りなくなります。発展編 第29回 HTTP プログラミング の aiohttp 章とも考え方が共通しています。

関連技術との比較 ─ SSE / Long Polling / WebRTC / socket.io

「双方向リアルタイム通信」と一言で言っても、用途と要件によって最適解は変わります。WebSocket と並ぶ選択肢を整理しておきましょう。

4 つの技術を要件で比較

技術方向下層得意な用途苦手な用途
WebSocket双方向TCP(HTTP Upgrade)チャット・ゲーム・通知・株価P2P・大量バルク
Server-Sent Events(SSE)サーバ→クライアント のみHTTP/1.1 持続接続ニュース速報・株価表示・通知クライアント送信が必要な場合
Long Polling双方向(疑似)HTTP リクエスト連続レガシー対応・WebSocket NG 環境レイテンシ・効率
WebRTC双方向 P2PUDP / DTLS / SCTP音声・ビデオ通話・低レイテンシクライアント間 NAT 越えの複雑さ
socket.io双方向(WebSocket + フォールバック)WebSocket / HTTP互換性最重視・ルーム機能内蔵素直なプロトコル理解には邪魔

判断フロー

サーバ → クライアントだけでいい

SSE を選ぶ。HTTP/1.1 のままで動き、再接続・イベント ID も標準で組み込まれている。シンプル。ただし IE / 古い Edge 非対応(現代では問題にならない)。

双方向で本格的

WebSocket。ブラウザ標準 API、wss:// で TLS 化、フレーム効率良し。本講で扱った内容そのもの。

音声・ビデオ・P2P 直接通信

WebRTC。UDP ベースでレイテンシが圧倒的に低い。STUN/TURN サーバで NAT 越えする実装の複雑さが代償。

複雑な互換性保証が必須

socket.io。WebSocket が使えれば WebSocket、ダメなら HTTP Long Polling に自動フォールバック。Room・Namespace 等の機能内蔵。プロトコル学習目的には重すぎる。

考えてみよう: Slack や Discord のチャットは何で動いているでしょうか?Web 版の DevTools を開くと、WebSocket(wss://) の長寿命コネクションがちゃんと見えます。一方、X(Twitter)のタイムライン更新は SSE、Zoom や Google Meet の音声・ビデオは WebRTC ─ それぞれ「双方向の頻度」と「レイテンシ要件」と「データ量」のトレードオフで設計が分かれています。

確認: 株価ダッシュボードや SNS のタイムラインのように サーバから一方向に頻繁に通知を送る 用途で、WebSocket ではなく SSE(Server-Sent Events) を選ぶことが多いのはなぜか?

正解:B。SSE は text/event-stream の MIME を持つ普通の HTTP レスポンスをずっと開いておくだけで、サーバが data: ...\n\n 形式で逐次送る仕組み。ブラウザ側に EventSource API があり再接続も自動。双方向不要なら WebSocket の Upgrade 機構やフレーム実装が不要で、HTTP/2 の多重化にも乗る。「能動送信が必要だがクライアントから多くは送らない」用途で軽量。一方、チャットやゲームのように双方向高頻度なら WebSocket が自然。

まとめと用語チェック

SUMMARY 1. WebSocket は HTTP Upgrade で TCP を双方向通信に昇格 させるプロトコル(RFC 6455)
2. ハンドシェイクは HTTP/1.1 GET + 101 Switching Protocols。Sec-WebSocket-Key/Accept で偽装防止
3. フレームは FIN + opcode + MASK + payload-len の最小 2 バイトヘッダ + ペイロード
4. クライアント → サーバの payload は 必ずマスク(キャッシュ汚染対策)
5. opcode は Text(0x1) / Binary(0x2) / Close(0x8) / Ping(0x9) / Pong(0xA)
6. 本番では wss://(TLS 1.3) 一択。HTTPS ページから ws:// は Mixed Content
7. 用途で使い分け:双方向 = WebSocket、片方向 = SSE、P2P 音声/動画 = WebRTC

用語チェック

用語1行説明
WebSocketRFC 6455。HTTP Upgrade で TCP を双方向通信に変える
Upgrade ハンドシェイクHTTP/1.1 GET → 101 Switching Protocols でプロトコル切替
Sec-WebSocket-Key / Acceptマジックストリングを混ぜた SHA-1 で偽装防止する検証ペア
opcodeフレーム種別(text 0x1, binary 0x2, close 0x8, ping 0x9, pong 0xA)
FINこのフレームでメッセージが完結することを示すビット
MASK / masking keyクライアント→サーバ方向のペイロードを XOR で撹拌する仕組み
フラグメンテーション1 メッセージを FIN=0 のフレームを連ねて分割送信できる仕組み
Ping/Pong長寿命接続を NAT/LB に切られないための生存確認
Close ステータス1000=正常、1001=遷移、1006=異常切断 など
ws:// / wss://平文 / TLS 化された WebSocket。443 の wss が事実上の標準
SSE(Server-Sent Events)サーバ→クライアント片方向のシンプル代替
WebRTCUDP ベースの P2P リアルタイム通信(音声・ビデオ)
関連: 前回 第33回 QUIC と HTTP/3 では、TCP の制約を超えるためのトランスポート再設計を見ました。HTTP の上に双方向を載せた WebSocket、UDP の上に多重化された TCP 相当を載せた QUIC ─ いずれも「既存層の制約を上層で乗り越える」という発想の系譜にあります。次回 第31回 GNS3 + VyOS では、ここまで学んだプロトコルを実機で組み立てます。

確認問題

問1. WebSocket がチャットなどの用途で HTTP より優れている本質的な理由として、最も適切なものを 1 つ選べ。

次の選択肢から最も適切なものを選択してください。
A. HTTP より暗号化が強力だから
B. WebSocket は UDP を使うのでパケットロスに強いから
C. 1 本の TCP コネクション上で サーバからも自発的にメッセージを送れる(双方向)から
D. WebSocket はバイナリしか扱わないので JSON より速いから
正解:C
WebSocket の本質は「双方向化」。HTTP が「クライアントが訊く → サーバが答える」しかできなかったのに対し、WebSocket では サーバが任意のタイミングでクライアントにメッセージを push できる。A は ws:// は平文(暗号化は wss:// で TLS を使うだけ)、B は WebSocket は TCP ベース、D は Text/Binary 両方扱える。

問2. WebSocket のハンドシェイクで使われるステータスコードはどれか。

次の選択肢から最も適切なものを選択してください。
A. 200 OK
B. 101 Switching Protocols
C. 301 Moved Permanently
D. 426 Upgrade Required
正解:B
WebSocket ハンドシェイクではサーバが HTTP/1.1 101 Switching Protocols を返し、これがプロトコル切替の合図となる。1xx 系の数少ない実用例の1つ。D の 426 は「Upgrade してくれ」とサーバ側がクライアントに要求するステータスで、ハンドシェイク自体には登場しない。

問3. WebSocket フレームでクライアント→サーバ方向のペイロードが 必ずマスクされる 理由として、最も適切なものはどれか。

次の選択肢から最も適切なものを選択してください。
A. 暗号強度を高めるため
B. CPU 負荷を均等化するため
C. ペイロードを圧縮するため
D. 中間プロキシが WebSocket フレームを HTTP メッセージと誤認してキャッシュ汚染するのを防ぐため
正解:D
マスクは XOR の単純処理で 暗号機能ではない。目的は キャッシュ汚染攻撃の防止。WebSocket を理解しない中間プロキシがペイロードを「HTTP の続き」と誤解しないよう、4 バイトの masking key で毎フレームバイト列を撹拌する。サーバ→クライアント方向はマスクなしで OK(攻撃モデル上不要)。

問4. WebSocket の opcode 0x9 の意味として、正しいものはどれか。

次の選択肢から最も適切なものを選択してください。
A. text フレーム(UTF-8 文字列)
B. binary フレーム(任意バイト列)
C. ping(生存確認)
D. close(切断要求)
正解:C
opcode の対応は:0x0 continuation / 0x1 text / 0x2 binary / 0x8 close / 0x9 ping / 0xA pong。Ping/Pong は長寿命接続が NAT/LB のアイドルタイムアウトで切られないように、定期的に小さなフレームを流す用途で使う。

問5. 「サーバから配信されるイベントを受け取るだけ(クライアントから能動送信は不要)」という用途に最も適した技術はどれか。

次の選択肢から最も適切なものを選択してください。
A. WebSocket
B. Server-Sent Events(SSE)
C. WebRTC
D. socket.io
正解:B
SSE は サーバ → クライアントの一方向プッシュ に特化した HTTP/1.1 上のシンプルな仕組み。再接続・イベント ID も標準で内蔵。クライアントから送るのが要らないなら、SSE のほうが WebSocket より軽量で実装も簡単。A は双方向が要るとき、C は P2P メディア向け、D は WebSocket の互換ラッパで重い。
← PREV
第33回 QUIC と HTTP/3
NEXT →
第35回 暗号の歴史と OTP