最近自分が作りたいものがあり、それに必要な技術などを調査している。調査したことの整理のために、何回か分けてアウトプットする。今回はWebSocketについて
WebSocketの概要
- クライアントとサーバー間で双方向通信をリアルタイムで行うためのプロトコル
- HTTPのようなリクエスト・レスポンスの仕組みとは異なり、一度接続が確立されると、クライアントとサーバーの両方がいつでもデータを送受信することができる
- リアルタイム性が求められるアプリケーションに非常に適している
- チャットアプリ
- オンラインゲーム
- 金融取引のダッシュボード
- 通信が軽量である
WebSocketの特徴
- 双方向通信: クライアントとサーバーの間でリアルタイムにデータの送受信可能。リクエストを待たずに、どちらからもデータを送信できる
- 持続的な接続: WebSocketは一度接続が確立されると、その接続がクローズするまでデータを送り続けることができる。HTTPのように1つのリクエストで1つのレスポンスを送る形とは異なる。
- 軽量: WebSocketのヘッダは非常に小さく、通信オーバーヘッドが少ないため、大量のメッセージを効率的にやり取りできる
- イベントドリブン: WebSocketでは、サーバーからクライアントに通知をプッシュすることができる
WebSocketの通信の流れ
sequenceDiagram
participant Client
participant Server
Client->>Server: WebSocket Handshake (HTTP Upgrade)
Server-->>Client: 101 Switching Protocols
Client->>Server: WebSocket Connection Established
Client->>Server: Send Data Frame
Server-->>Client: Send Data Frame (Response)
Client->>Server: Close Connection Frame
Server-->>Client: Acknowledge Close Frame
Client->>Server: WebSocket Connection Closed
- 接続の確立:クライアントはHTTPプロトコルを使って、サーバーに接続要求を送る。この時、Upgradeヘッダーを使って、WebSocketへのプロトコル変更を要求する。サーバーがその要求を承認すると、HTTP接続がWebSocketにアップグレードされて、継続的な接続が確立される。
GET /chat HTTP/1.1 Host: example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== Sec-WebSocket-Version: 13
- メッセージの送受信:接続が確立されると、クライアントとサーバーは双方向にメッセージを送受信できる。メッセージは通常、テキストまたはバイナリデータ。クライアントとサーバーは、例えば、チャットメッセージや通知などを相互にやり取りする。
- 接続の終了:接続はクライアントまたは、サーバーのどちらかが閉じるまで持続する。接続が不要になった場合、接続をクローズする手続きを行う。
WebSocketのヘッダが軽量とは?
従来のHTTPリクエスト/レスポンスとの通信オーバーヘッドの違い
簡単にまとめると
特徴 | HTTP/1.1 | WebSocket |
---|---|---|
ヘッダサイズ | 500バイト〜1KB以上 | 2〜14バイト |
ヘッダの送信頻度 | 各リクエスト/レスポンスごと | 接続時(最初のハンドシェイクのみ) |
持続的接続のサポート | 基本的にリクエスト/レスポンス型 | 持続的 |
リアルタイム性 | リクエストごとにラウンドトリップ | 持続的でリアルタイム |
HTTPリクエスト/レスポンスのヘッダの大きさ
- HTTP/1.1では、リクエストやレスポンスごとに、以下のような多くのメタデータ情報を送るため、特に短いメッセージを送る際にも、リクエストごとに繰り返し送信されるため、オーバーヘッドが大きくなる。
- URL
- メソッド
- プロトコルバージョン
- ホスト名
- ユーザーエージェント
- クッキー
- 認証情報
- 一般的なHTTPリクエストのヘッダのサイズは、数百バイトから1KB程度になることが多い。
- HTTPヘッダには、クッキー、リファラ、ホスト情報などの、通信内容には直接関係しない多くの情報が含まれることがある
HTTPリクエストでは、具体的には、以下のような形式をとる。HTTPヘッダ部分だけでも、約100〜200バイト程度になり、これに加えてデータ部分が送信される。
POST /chat HTTP/1.1 Host: example.com User-Agent: Mozilla/5.0 Content-Type: application/json Content-Length: 17 {"message":"Hello"}
WebSocketフレームのヘッダーの大きさ
- WebSocketでは、最初のハンドシェイク(HTTP 101 Switching Protocols)でプロトコルをWebSocketにアップグレードした後、持続的な接続を維持する。そのため、通信のたびにHTTPのような重いヘッダを繰り返し送信する必要がなくなる
- WebSocketフレームのヘッダは非常に小さく、基本的には2Byteから14Byteで構成される。
- フレームのヘッダには、ペイロードの長さ、メッセージのタイプ(テキストかバイナリか)、マスクなどが含まれているが、HTTPに比べてはるかに効率が良い。
具体的なWebSocketフレームの基本ヘッダ構造
- 1バイト目: FINビット(メッセージの終了を示す)とOpcode(データの種類: テキスト、バイナリ、Ping、Pongなど)。
- 2バイト目: ペイロード長とマスクビット(クライアントからの送信は必ずマスクされる)。
- ペイロードが126バイトを超える場合、追加のフィールドが使われるが、通常は非常に短いメッセージであれば2〜14バイトで済みます。
WebSocketフレームの具体例は以下のようになる。この場合には、ヘッダのサイズは約2バイトになる。HTTPに比べると効率的。
FIN=1, Opcode=1 (テキスト), Masked, Payload length=5 データ: Hello
フレームについて
フィールド名 | ビット数/サイズ | 説明 |
---|---|---|
FIN | 1ビット | メッセージの最後のフレームかを示す(1 = 最後、0 = 続く) |
RSV1, RSV2, RSV3 | 各1ビット | 予約ビット。通常は0。拡張機能のために使用される場合がある |
Opcode | 4ビット | フレームの種類を示す(例: 0x1 = テキストフレーム、0x2 = バイナリフレーム) |
Mask | 1ビット | マスキングの有無を示す(クライアントから送信される場合は1、サーバーは通常0) |
Payload Length | 7ビット、または7+16ビット、7+64ビット | ペイロードデータの長さを示す(0~125の場合はそのまま、126なら16ビット、127なら64ビット) |
Masking Key | 32ビット(クライアントからのみ) | クライアント側から送信される場合に使用されるマスキングキー |
Payload Data | 可変長 | 実際のデータ(テキストまたはバイナリ)。UTF-8エンコード(テキストの場合)またはそのままバイナリ |
Opcodeの主な値
Opcodeの値 | 意味 |
---|---|
0x0 | 継続フレーム |
0x1 | テキストフレーム |
0x2 | バイナリフレーム |
0x8 | Closeフレーム |
0x9 | Pingフレーム |
0xA | Pongフレーム |
WebSocket Opening ハンドシェイクについて
- WebSocket通信が始まる際に、クライアントとサーバー間で行われる初期のプロトコル交渉。
- ハンドシェイクプロセスは、クライアントが通常のHTTPリクエストを使ってサーバーにWebSocketプロトコルへの切り替えを要求して、サーバーがその要求を承認することで行われる。
- このハンドシェイクを通じて、クライアントとサーバーはWebSocket接続を確立し、その後の双方向通信を可能にする。
関連技術メモ
今後、知りたいことのメモ
- Operational Transformation (OT)
- WebRTC
- Server-Sent Events (SSE)
- WebTransport
- HTTP/2 Server Push
- gRPC with HTTP/2
- QUIC (Quick UDP Internet Connection)