メインコンテンツまでスキップ

WebSocketプロトコル実装ガイド (RFC 6455)

このドキュメントは、RFC 6455の詳細な技術ガイドおよび実装リファレンスであり、プロトコルの説明、コード例、ベストプラクティスを含んでいます。RFC公式章の翻訳については、各章のドキュメントを参照してください。

RFC 6455 - WebSocketプロトコル

Internet Engineering Task Force (IETF)
Request for Comments: 6455
カテゴリ: Standards Track

著者:
I. Fette (Google Inc.)
A. Melnikov (Isode Ltd.)

発行日: 2011年12月


このメモランダムのステータス

これはInternet Standards Trackドキュメントです。

このドキュメントは、Internet Engineering Task Force (IETF) の成果物であり、IETFコミュニティのコンセンサスを表しています。

著作権表示

Copyright (c) 2011 IETF Trust and the persons identified as the document authors. All rights reserved.


概要

WebSocketプロトコル (WebSocket Protocol) は、単一のTCP接続上でクライアントとサーバー間の全二重通信 (Full-Duplex Communication) を可能にします。WebSocketプロトコルは、WebブラウザおよびWebサーバーでの実装を目的として設計されていますが、任意のクライアントまたはサーバーアプリケーションで使用できます。

WebSocketプロトコルは、TCPベースの独立したプロトコルです。HTTPとの唯一の関係は、そのハンドシェイク (Handshake) がHTTPサーバーによってアップグレードリクエスト (Upgrade Request) として解釈されることです。

設計により、WebSocketプロトコルは既存のHTTPインフラストラクチャ上で動作することを目指しているため、HTTPポート80および443を使用し、HTTPプロキシおよび中間装置をサポートします。これは、ある程度の複雑さを意味する場合でもそうです。


目次


WebSocketコア用語集

基本概念

英語用語日本語訳説明
WebSocket ProtocolWebSocketプロトコル単一のTCP接続上で全二重通信を可能にするプロトコル
Full-Duplex Communication全二重通信双方向同時通信
Opening HandshakeオープニングハンドシェイクWebSocket接続を確立するためのHTTPアップグレードプロセス
Closing HandshakeクロージングハンドシェイクWebSocket接続を正常にクローズするためのプロセス
FrameフレームWebSocketデータ転送の基本単位
Messageメッセージ1つ以上のフレームで構成される完全なアプリケーションデータ
ClientクライアントWebSocket接続を開始する側 (通常はブラウザ)
ServerサーバーWebSocket接続を受け入れる側
Endpointエンドポイントクライアントまたはサーバー
Connection接続クライアントとサーバー間のWebSocket接続

ハンドシェイク関連

英語用語日本語訳説明
Upgrade RequestアップグレードリクエストWebSocketへのHTTPアップグレードリクエスト
Sec-WebSocket-KeyWebSocketキークライアントが送信するランダムキー
Sec-WebSocket-AcceptWebSocket受け入れサーバーが返す検証キー
Sec-WebSocket-ProtocolWebSocketサブプロトコル交渉されたアプリケーション層サブプロトコル
Sec-WebSocket-ExtensionsWebSocket拡張交渉されたプロトコル拡張
Sec-WebSocket-VersionWebSocketバージョンプロトコルバージョン番号 (現在は13)
Originオリジン接続を開始するWebページの発信元

フレーム構造関連

英語用語日本語訳説明
FINFIN / 終了ビットこれがメッセージの最後のフレームであることを示す
RSVRSV / 予約ビット拡張用に予約されたフラグビット
Opcodeオペコード / 操作コードフレームのタイプを定義
Maskマスククライアントからサーバーへのデータはマスク必須
Masking-keyマスキングキーデータマスキング用の32ビットキー
Payload Lengthペイロード長データ長
Payload Dataペイロードデータ実際に転送されるデータ
Extension Data拡張データ拡張によって交渉された追加データ
Application Dataアプリケーションデータアプリケーション層の実際のデータ

フレームタイプ

英語用語日本語訳オペコード説明
Continuation Frame継続フレーム0x0メッセージの後続フレーム
Text Frameテキストフレーム0x1UTF-8エンコードされたテキストデータ
Binary Frameバイナリフレーム0x2バイナリデータ
Close Frameクローズフレーム0x8接続クローズ用制御フレーム
Ping FramePingフレーム0x9ハートビート検出リクエスト
Pong FramePongフレーム0xAハートビート検出レスポンス
Control Frame制御フレーム0x8-0xF接続制御用フレーム
Data Frameデータフレーム0x1-0x2アプリケーションデータ転送用フレーム

クローズ関連

英語用語日本語訳説明
Close Codeクローズコード接続クローズの理由を示す数値コード
Close Reasonクローズ理由クローズのテキスト説明
Normal Closure正常クローズコード1000、目的達成後の正常なクローズ
Going Away離脱コード1001、エンドポイントが離脱 (ページナビゲーションなど)
Protocol Errorプロトコルエラーコード1002、プロトコル違反
Unsupported Dataサポートされないデータコード1003、受け入れられないデータタイプを受信
Invalid Frame Payload Data無効なフレームペイロードデータコード1007、データの不整合
Policy Violationポリシー違反コード1008、ポリシーの違反
Message Too Bigメッセージが大きすぎるコード1009、処理するには大きすぎるメッセージ
TLS Handshake FailureTLSハンドシェイク失敗コード1015、TLSハンドシェイク失敗

ドキュメント構造の説明

セクション1: 導入と概要

  • WebSocketの背景とニーズ
  • プロトコルの設計目標と原則
  • HTTP/TCPとの関係
  • セキュリティモデルと設計哲学

セクション2-3: 基本要件

  • 適合性要件と用語定義
  • WebSocket URIフォーマット (ws:// および wss://)

セクション4: オープニングハンドシェイク (コア)

  • クライアントがハンドシェイクを開始する方法
  • サーバーがハンドシェイクに応答する方法
  • サブプロトコルと拡張の交渉
  • 複数バージョンのサポート

セクション5: データフレーミング (コア)

  • フレーム構造の詳細説明
  • マスキングメカニズム
  • 断片化 (大きなメッセージを複数のフレームで転送)
  • 制御フレームとデータフレーム

セクション6-7: データ転送と接続クローズ

  • メッセージの送受信ルール
  • 正常なクローズフロー
  • 例外処理
  • クローズステータスコード

セクション8-10: 拡張、エラー、およびセキュリティ

  • 拡張メカニズム
  • エラー処理
  • 詳細なセキュリティ考慮事項

WebSocket接続確立フロー

1. クライアントがHTTPアップグレードリクエストを開始

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat

2. サーバーがアップグレードで応答

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat

3. 接続確立、双方向通信開始

Client ←→ Server
↓ ↓
Text/Binary Frames
Ping/Pong Frames
Close Frame

4. 接続をクローズ

Client → Server: Close Frame (Code: 1000)
Server → Client: Close Frame (Code: 1000)
TCP接続をクローズ

ハンドシェイクキー計算

Sec-WebSocket-Acceptの計算方法

// 1. クライアントがランダムなSec-WebSocket-Keyを生成 (Base64エンコードされた16バイトの乱数)
const clientKey = "dGhlIHNhbXBsZSBub25jZQ==";

// 2. マジック文字列 (RFC 6455で定義された固定GUID)
const magicString = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";

// 3. 連結してSHA-1ハッシュを計算
const concatenated = clientKey + magicString;
const hash = SHA1(concatenated);

// 4. Base64エンコードでSec-WebSocket-Acceptを取得
const serverAccept = Base64Encode(hash);
// 結果: "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="

このメカニズムは以下を保証します:

  • サーバーがWebSocketプロトコルを理解している (通常のHTTPサーバーではない)
  • キャッシングプロキシが誤った応答を返すのを防ぐ
  • 基本的なハンドシェイク検証を提供

WebSocketフレーム構造の詳細

 0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len | Extended payload length |
|I|S|S|S| (4) |A| (7) | (16/64) |
|N|V|V|V| |S| | (if payload len==126/127) |
| |1|2|3| |K| | |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
| Extended payload length continued, if payload len == 127 |
+ - - - - - - - - - - - - - - - +-------------------------------+
| |Masking-key, if MASK set to 1 |
+-------------------------------+-------------------------------+
| Masking-key (continued) | Payload Data |
+-------------------------------- - - - - - - - - - - - - - - - +
: Payload Data continued ... :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Payload Data continued ... |
+---------------------------------------------------------------+

フィールドの説明

FIN (1ビット):

  • 0 = さらにフレームが続く
  • 1 = これがメッセージの最後 (または唯一) のフレーム

RSV1, RSV2, RSV3 (各1ビット):

  • 拡張用に予約
  • 拡張が交渉されていない場合は0でなければならない

Opcode (4ビット):

  • 0x0 = Continuation (継続フレーム)
  • 0x1 = Text (テキストフレーム、UTF-8)
  • 0x2 = Binary (バイナリフレーム)
  • 0x8 = Close (クローズ)
  • 0x9 = Ping
  • 0xA = Pong
  • 0x3-0x7, 0xB-0xF = 予約済み

MASK (1ビット):

  • クライアントからサーバーへ: 1でなければならない
  • サーバーからクライアントへ: 0でなければならない

Payload Length (7ビット、7+16ビット、または7+64ビット):

  • 0-125: 実際の長さ
  • 126: 次の16ビットが実際の長さ
  • 127: 次の64ビットが実際の長さ

Masking-key (0または4バイト):

  • MASK=1の場合、32ビットマスキングキーを含む

Payload Data:

  • Extension data + Application data

マスキングメカニズムの説明

なぜマスキングが必要なのか?

セキュリティ上の理由: キャッシュポイズニング攻撃を防ぐため。一部の中間プロキシがWebSocketフレームを誤ってキャッシュする可能性があり、マスキングによりデータが予測不可能であることを保証します。

マスキングアルゴリズム

// クライアントがデータを送信する際
function maskData(data, maskingKey) {
const masked = new Uint8Array(data.length);
for (let i = 0; i < data.length; i++) {
masked[i] = data[i] ^ maskingKey[i % 4];
}
return masked;
}

// サーバーがデータを受信する際 (デコード用に同じアルゴリズムを使用)
function unmaskData(maskedData, maskingKey) {
return maskData(maskedData, maskingKey); // XORは自己逆演算
}

重要なポイント:

  • XOR演算 (^) を使用
  • キーの長さは4バイト
  • キーを循環的に使用
  • クライアント→サーバー: マスクが必須
  • サーバー→クライアント: マスク禁止

メッセージの断片化 (Fragmentation)

大きなメッセージは複数のフレームで送信できます:

例: 大きなテキストメッセージの送信

Frame 1: FIN=0, Opcode=0x1 (Text), Payload="Hello "
Frame 2: FIN=0, Opcode=0x0 (Continuation), Payload="World"
Frame 3: FIN=1, Opcode=0x0 (Continuation), Payload="!"

最終メッセージ: "Hello World!"

ルール:

  • 最初のフレーム: FIN=0, Opcode=データタイプ (0x1または0x2)
  • 中間のフレーム: FIN=0, Opcode=0x0 (Continuation)
  • 最後のフレーム: FIN=1, Opcode=0x0 (Continuation)
  • 制御フレームは断片化できないし、断片化されたデータフレームの間に挿入できる

制御フレームの詳細

Closeフレーム (0x8)

構造:
+--------+--------+------------------+
| Code | Reason |
| (2バイト) | (UTF-8テキスト) |
+--------+--------+------------------+

例:
Close Code: 1000 (正常クローズ)
Close Reason: "Going away"

よく使用されるクローズコード:

  • 1000: Normal Closure (正常クローズ)
  • 1001: Going Away (エンドポイントが離脱、ページナビゲーションなど)
  • 1002: Protocol Error (プロトコルエラー)
  • 1003: Unsupported Data (サポートされないデータタイプ)
  • 1006: Abnormal Closure (異常クローズ、Closeフレーム未送信)
  • 1009: Message Too Big (メッセージが大きすぎる)
  • 1011: Internal Server Error (サーバー内部エラー)

Pingフレーム (0x9)

  • クライアントまたはサーバーから送信可能
  • ハートビート検出に使用 (接続をアクティブに保つ)
  • アプリケーションデータを運ぶことができる (最大125バイト)
  • 受信者は必ずPongフレームで応答しなければならない

Pongフレーム (0xA)

  • Pingフレームへの応答に使用
  • Pingフレームと同じペイロードを含まなければならない
  • 能動的に送信することもできる (単方向ハートビート)

クローズフロー

正常クローズ (Clean Close)

1. イニシエーターがCloseフレームを送信
Client → Server: Close Frame (Code: 1000)

2. 受信者がCloseフレームで応答
Server → Client: Close Frame (Code: 1000)

3. イニシエーターがTCP接続をクローズ
Client: TCP接続をクローズ

4. 受信者もTCP接続をクローズ
Server: TCP接続をクローズ

異常クローズ

  • TCP接続が突然切断 (Closeフレームなし)
  • 無効なフレームデータを受信
  • 応答なしでタイムアウト
  • プロトコルルール違反

ステータスコード: 1006 (Abnormal Closure) - このコードはCloseフレームで送信されず、報告のみに使用


WebSocket URIフォーマット

ws:// (非暗号化)

ws://example.com/socket
ws://example.com:8080/chat
ws://192.168.1.1/data
  • デフォルトポート: 80
  • http:// のセキュリティレベルと同等

wss:// (TLS暗号化)

wss://example.com/socket
wss://secure.example.com:443/chat
  • デフォルトポート: 443
  • https:// のセキュリティレベルと同等
  • 本番環境では wss:// の使用を強く推奨

サブプロトコル (Subprotocol)

WebSocketはトランスポート層プロトコルであり、サブプロトコルはアプリケーション層のメッセージフォーマットを定義します:

クライアントリクエスト:
Sec-WebSocket-Protocol: chat, superchat

サーバーレスポンス:
Sec-WebSocket-Protocol: chat

確立された接続は"chat"サブプロトコルを使用

一般的なサブプロトコル:

  • STOMP: Simple Text Oriented Messaging Protocol
  • MQTT: Message Queuing Telemetry Transport
  • WAMP: Web Application Messaging Protocol
  • カスタムアプリケーションプロトコル

拡張 (Extensions)

拡張はWebSocket機能を強化できます:

クライアントリクエスト:
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits

サーバーレスポンス:
Sec-WebSocket-Extensions: permessage-deflate

よく使用される拡張:

  • permessage-deflate: メッセージ圧縮 (RFC 7692)
  • permessage-bzip2: Bzip2圧縮
  • カスタム拡張

セキュリティ考慮事項の要点

1. オリジン検証 (Origin Validation)

// サーバー側のオリジン検証
const allowedOrigins = ['https://example.com', 'https://app.example.com'];
const origin = request.headers['origin'];

if (!allowedOrigins.includes(origin)) {
// 接続を拒否
response.status(403).send('Forbidden');
}

2. TLS暗号化

  • 本番環境では wss:// を使用しなければならない
  • 中間者攻撃を防ぐ
  • データの機密性と完全性を保護

3. 認証と認可

方法1: ハンドシェイク時にCookieを介して認証
GET /socket HTTP/1.1
Cookie: session=abc123

方法2: サブプロトコルを介してトークンを渡す
ws://example.com/socket?token=jwt_token

方法3: 接続後の最初のメッセージで認証情報を運ぶ

4. レート制限

  • 接続数を制限 (DoS防止)
  • メッセージサイズを制限
  • メッセージ頻度を制限

5. 入力検証

  • UTF-8エンコーディングを検証 (テキストフレーム)
  • メッセージフォーマットを検証
  • インジェクション攻撃を防ぐ

実装のベストプラクティス

クライアント (ブラウザ)

// 1. WebSocket接続を作成
const ws = new WebSocket('wss://example.com/socket', ['chat']);

// 2. イベントリスナーを登録
ws.addEventListener('open', (event) => {
console.log('接続が確立されました');
ws.send('Hello Server!');
});

ws.addEventListener('message', (event) => {
console.log('メッセージを受信:', event.data);
});

ws.addEventListener('error', (event) => {
console.error('WebSocketエラー:', event);
});

ws.addEventListener('close', (event) => {
console.log('接続がクローズされました', event.code, event.reason);
// 再接続ロジックを実装
if (event.code !== 1000) {
setTimeout(() => reconnect(), 5000);
}
});

// 3. データを送信
ws.send('テキストメッセージ');
ws.send(new Blob(['バイナリデータ']));
ws.send(new ArrayBuffer(8));

// 4. 接続をクローズ
ws.close(1000, '正常クローズ');

サーバー側 (Node.js例)

const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });

wss.on('connection', (ws, request) => {
// オリジンを検証
const origin = request.headers.origin;
// ...検証ロジック

// ハートビート検出
ws.isAlive = true;
ws.on('pong', () => { ws.isAlive = true; });

// メッセージを受信
ws.on('message', (data) => {
console.log('受信:', data);

// 全クライアントにブロードキャスト
wss.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(data);
}
});
});

// エラー処理
ws.on('error', (error) => {
console.error('エラー:', error);
});

// クローズ
ws.on('close', (code, reason) => {
console.log('接続がクローズされました:', code, reason);
});
});

// ハートビート検出
const interval = setInterval(() => {
wss.clients.forEach((ws) => {
if (ws.isAlive === false) {
return ws.terminate();
}
ws.isAlive = false;
ws.ping();
});
}, 30000);

一般的な適用シナリオ

1. リアルタイムチャットアプリケーション

  • インスタントメッセージング
  • オンラインステータス表示
  • タイピングインジケーター

2. リアルタイムコラボレーションツール

  • ドキュメント共同編集 (Google Docsなど)
  • ホワイトボード共有
  • コラボレーティブコードエディタ

3. リアルタイムデータストリーム

  • 株価
  • スポーツスコア
  • IoTデータ

4. オンラインゲーム

  • マルチプレイヤーオンラインゲーム
  • リアルタイムゲーム状態同期
  • 低遅延インタラクション

5. プッシュ通知

  • ブラウザプッシュ
  • リアルタイムアラート
  • システム監視

他の技術との比較

WebSocket vs HTTP Long Polling

特性WebSocketLong Polling
接続永続的な接続繰り返しHTTPリクエスト
レイテンシ非常に低い高い (HTTPオーバーヘッド)
双方向真の双方向疑似双方向
リソース低 (1つの接続)高 (複数の接続)
複雑さ中程度より単純

WebSocket vs Server-Sent Events (SSE)

特性WebSocketSSE
双方向通信✅ 双方向❌ サーバー→クライアントのみ
データフォーマットバイナリまたはテキストテキスト (UTF-8)
プロトコル独立したプロトコルHTTPベース
ブラウザサポート広くサポート広くサポート (IEを除く)
再接続手動実装自動再接続

デバッグツール

ブラウザ開発者ツール

  • Chrome DevTools → Network → WS
  • フレームの送受信を表示
  • 接続状態を表示

コマンドラインツール

# wscat (Node.js)
npm install -g wscat
wscat -c ws://echo.websocket.org

# websocat (Rust)
websocat ws://echo.websocket.org

オンラインテストツール

  • websocket.org Echo Test
  • Hoppscotch (旧Postwoman)
  • Firecamp

リファレンスリソース

関連RFC標準

  • RFC 6455: WebSocketプロトコル (このドキュメント)
  • RFC 7692: WebSocket圧縮拡張
  • RFC 8441: HTTP/2上のWebSocketブートストラッピング
  • RFC 8307: WebSocket用Well-Known URI

推奨実装ライブラリ

JavaScript (ブラウザ):

  • ネイティブWebSocket API

Node.js:

  • ws (軽量)
  • Socket.IO (機能豊富、自動フォールバック)
  • uWebSockets.js (高性能)

Python:

  • websockets (asyncio)
  • ws4py
  • Tornado

Java:

  • Java WebSocket API (JSR 356)
  • Netty
  • Spring WebSocket

Go:

  • gorilla/websocket
  • nhooyr/websocket

次のステップの学習提案

  1. 基礎: WebSocketハンドシェイクとフレーム構造を理解
  2. 実践: シンプルなチャットアプリケーションを実装
  3. 応用: サブプロトコルと拡張を学習
  4. 最適化: ハートビート、再接続、エラー処理を実装
  5. セキュリティ: セキュリティ考慮事項とベストプラクティスを深く理解

まとめ

WebSocketプロトコルはWebリアルタイム通信の基盤です:

  • 正確性: 双方向通信の正確な実装
  • 伝達性: 明確なプロトコル仕様
  • 優雅性: エレガントな設計と実装

コアバリュー:

  1. 🔄 真の全二重通信
  2. ⚡ 低遅延リアルタイムデータ転送
  3. 📦 軽量フレームプロトコル
  4. 🔐 組み込みセキュリティメカニズム

最適な使用シナリオ:

  • リアルタイムチャット
  • コラボレーティブ編集
  • リアルタイムゲーム
  • リアルタイムデータストリーム
  • プッシュ通知

関連RFC:

  • RFC 6455: WebSocketプロトコル (このドキュメント)
  • RFC 7692: WebSocket圧縮拡張
  • RFC 8441: HTTP/2上のWebSocketブートストラッピング

RFCリストに戻る