mnagaaのメモ

技術的なことはこのブログで書きます

通信方式 ~vol.1~ (WebSocket)

最近自分が作りたいものがあり、それに必要な技術などを調査している。調査したことの整理のために、何回か分けてアウトプットする。今回はWebSocketについて

WebSocketの概要

  • クライアントとサーバー間で双方向通信をリアルタイムで行うためのプロトコル
  • HTTPのようなリクエスト・レスポンスの仕組みとは異なり、一度接続が確立されると、クライアントとサーバーの両方がいつでもデータを送受信することができる
  • リアルタイム性が求められるアプリケーションに非常に適している
    • チャットアプリ
    • オンラインゲーム
    • 金融取引のダッシュボード
  • 通信が軽量である

WebSocketの特徴

  1. 双方向通信: クライアントとサーバーの間でリアルタイムにデータの送受信可能。リクエストを待たずに、どちらからもデータを送信できる
  2. 持続的な接続: WebSocketは一度接続が確立されると、その接続がクローズするまでデータを送り続けることができる。HTTPのように1つのリクエストで1つのレスポンスを送る形とは異なる。
  3. 軽量: WebSocketのヘッダは非常に小さく、通信オーバーヘッドが少ないため、大量のメッセージを効率的にやり取りできる
  4. イベントドリブン: 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
  1. 接続の確立:クライアントは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
  1. メッセージの送受信:接続が確立されると、クライアントとサーバーは双方向にメッセージを送受信できる。メッセージは通常、テキストまたはバイナリデータ。クライアントとサーバーは、例えば、チャットメッセージや通知などを相互にやり取りする。
  2. 接続の終了:接続はクライアントまたは、サーバーのどちらかが閉じるまで持続する。接続が不要になった場合、接続をクローズする手続きを行う。

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)