# 機能設計書: WebSocket

## 1. 機能概要

### 1.1 機能名
WebSocket - WebSocketPeerによる双方向リアルタイム通信

### 1.2 機能説明
WebSocketPeerは、WebSocketプロトコル（RFC 6455）に基づく双方向リアルタイム通信を実現するクラスである。クライアント・サーバー両方の機能をサポートし、テキストおよびバイナリデータの送受信、TLS/SSL暗号化通信、サブプロトコルのネゴシエーション、ハートビートによる接続維持機能を提供する。内部実装はwslayライブラリを使用し、マルチプレイヤーゲームやリアルタイムアプリケーションの通信基盤として利用される。

### 1.3 関連画面
- なし（スクリプトベースの機能）

## 2. 機能詳細

### 2.1 主要機能一覧

| 機能ID | 機能名 | 説明 |
|--------|--------|------|
| F39-01 | WebSocket接続 | ws:// / wss:// URLへの接続 |
| F39-02 | データ送信 | テキスト/バイナリデータの送信 |
| F39-03 | データ受信 | パケットベースのデータ受信 |
| F39-04 | TLS/SSL対応 | wss://による暗号化通信 |
| F39-05 | サブプロトコル | プロトコルネゴシエーション |
| F39-06 | ハートビート | Ping/Pongによる接続維持 |
| F39-07 | サーバーモード | accept_stream()でサーバー側処理 |
| F39-08 | マルチプレイヤー連携 | WebSocketMultiplayerPeerとの統合 |

### 2.2 クラス構造

```
PacketPeer
    └── WebSocketPeer (抽象クラス)
            │
            └── WSLPeer (実装: wslayベース)
                    │
                    ├── StreamPeerTCP (TCP接続)
                    ├── StreamPeerTLS (TLS接続)
                    └── PacketBuffer<uint8_t> (受信バッファ)

MultiplayerPeer
    └── WebSocketMultiplayerPeer
            │
            ├── TCPServer (サーバーモード)
            ├── HashMap<int, WebSocketPeer> (接続管理)
            └── List<Packet> (受信パケットキュー)
```

### 2.3 WebSocketPeer主要プロパティ

| プロパティ名 | 型 | デフォルト値 | 説明 |
|-------------|-----|-------------|------|
| supported_protocols | PackedStringArray | [] | サポートするサブプロトコル |
| handshake_headers | PackedStringArray | [] | カスタムハンドシェイクヘッダー |
| inbound_buffer_size | int | 65535 | 受信バッファサイズ |
| outbound_buffer_size | int | 65535 | 送信バッファサイズ |
| max_queued_packets | int | 4096 | 最大キューパケット数 |
| heartbeat_interval | float | 0.0 | ハートビート間隔（秒、0で無効） |

### 2.4 WebSocketPeer主要メソッド

| メソッド名 | 戻り値 | 説明 |
|-----------|--------|------|
| connect_to_url(url, tls_options) | Error | URLに接続 |
| accept_stream(stream) | Error | サーバー側でストリーム受け入れ |
| send(message, write_mode) | Error | データを送信 |
| send_text(message) | Error | テキストデータを送信 |
| close(code, reason) | void | 接続を閉じる |
| poll() | void | 接続状態を更新 |
| get_ready_state() | State | 現在の接続状態を取得 |
| get_close_code() | int | 切断コードを取得 |
| get_close_reason() | String | 切断理由を取得 |
| was_string_packet() | bool | 最後の受信がテキストか |
| get_connected_host() | IPAddress | 接続先ホストを取得 |
| get_connected_port() | int | 接続先ポートを取得 |
| get_selected_protocol() | String | 選択されたサブプロトコル |
| get_requested_url() | String | リクエストURL |
| set_no_delay(enabled) | void | Nagle無効化設定 |
| get_current_outbound_buffered_amount() | int | 送信待ちデータ量 |

### 2.5 State列挙型

| 定数 | 値 | 説明 |
|------|-----|------|
| STATE_CONNECTING | 0 | 接続処理中 |
| STATE_OPEN | 1 | 接続確立済み |
| STATE_CLOSING | 2 | 切断処理中 |
| STATE_CLOSED | 3 | 切断完了 |

### 2.6 WriteMode列挙型

| 定数 | 値 | 説明 |
|------|-----|------|
| WRITE_MODE_TEXT | 0 | テキストフレーム |
| WRITE_MODE_BINARY | 1 | バイナリフレーム |

### 2.7 WebSocketMultiplayerPeer主要メソッド

| メソッド名 | 戻り値 | 説明 |
|-----------|--------|------|
| create_client(url, tls_options) | Error | クライアント作成 |
| create_server(port, bind_address, tls_options) | Error | サーバー作成 |
| get_peer(peer_id) | WebSocketPeer | 指定ピア取得 |
| get_peer_address(id) | IPAddress | ピアアドレス取得 |
| get_peer_port(id) | int | ピアポート取得 |

## 3. 処理フロー

### 3.1 クライアント接続処理フロー

```
[connect_to_url(url, tls_options) 呼び出し]
      │
      ▼
[URL解析] - wsl_peer.cpp: 477-506
      │
      ├─ スキーム判定 (ws:// or wss://)
      ├─ ホスト名抽出
      ├─ ポート決定 (デフォルト: ws=80, wss=443)
      └─ パス抽出 (デフォルト: "/")
      │
      ▼
[Resolver開始] - wsl_peer.cpp: 520-521
      │
      └─ DNS解決 or IPアドレス直接使用
      │
      ▼
[TCP接続] - wsl_peer.cpp: 518
      │
      └─ StreamPeerTCP::connect_to_host()
      │
      ▼
[ハンドシェイクリクエスト生成] - wsl_peer.cpp: 529-557
      │
      ├─ セッションキー生成（16バイトランダム+Base64）
      ├─ Upgradeヘッダー
      ├─ Sec-WebSocket-Key
      ├─ Sec-WebSocket-Version: 13
      └─ サブプロトコル（設定時）
      │
      ▼
[poll() でハンドシェイク処理]
      │
      ├─ [TLS接続] (wss://の場合)
      │       └── StreamPeerTLS::connect_to_stream()
      │
      ├─ [リクエスト送信] - wsl_peer.cpp: 356-373
      │       └── connection->put_partial_data()
      │
      └─ [レスポンス検証] - wsl_peer.cpp: 418-475
              │
              ├─ HTTP/1.1 101 Switching Protocols確認
              ├─ Sec-WebSocket-Accept検証
              └─ サブプロトコル確認
      │
      ▼
[wslay初期化] - wsl_peer.cpp: 406-411
      │
      ├─ wslay_event_context_client_init()
      ├─ バッファサイズ設定
      └─ ready_state = STATE_OPEN
```

### 3.2 サーバーハンドシェイク処理フロー

```
[accept_stream(stream) 呼び出し]
      │
      ▼
[ストリーム種別判定] - wsl_peer.cpp: 127-151
      │
      ├─ StreamPeerTCP → use_tls = false
      └─ StreamPeerTLS → use_tls = true
      │
      ▼
[_do_server_handshake()] - wsl_peer.cpp: 216-307
      │
      ├─ [TLSハンドシェイク] (use_tls時)
      │
      ├─ [クライアントリクエスト解析] - wsl_peer.cpp: 153-214
      │       │
      │       ├─ "GET / HTTP/1.1"確認
      │       ├─ "Upgrade: websocket"確認
      │       ├─ "Sec-WebSocket-Version: 13"確認
      │       └─ サブプロトコルネゴシエーション
      │
      └─ [レスポンス送信] - wsl_peer.cpp: 256-303
              │
              ├─ "HTTP/1.1 101 Switching Protocols"
              ├─ "Upgrade: websocket"
              ├─ "Sec-WebSocket-Accept: " + 計算値
              └─ カスタムヘッダー
      │
      ▼
[wslay初期化] - wsl_peer.cpp: 297-302
      │
      └─ wslay_event_context_server_init()
```

### 3.3 データ送受信フロー

```
[送信処理]
      │
      ▼
[send() / send_text() 呼び出し]
      │
      └─ _send(buffer, size, opcode)
              │
              ├─ wslay_event_queue_msg()
              └─ wslay_event_send()
                      │
                      └─ _wsl_send_callback()
                              └─ connection->put_partial_data()

[受信処理]
      │
      ▼
[poll() 呼び出し]
      │
      └─ wslay_event_recv()
              │
              ├─ _wsl_recv_callback()
              │       └─ connection->get_partial_data()
              │
              ├─ _wsl_recv_start_callback()
              │       └─ フレーム開始処理
              │
              ├─ _wsl_frame_recv_chunk_callback()
              │       └─ in_buffer.write_packet()
              │
              └─ _wsl_msg_recv_callback()
                      │
                      ├─ CLOSE → ready_state = STATE_CLOSING
                      ├─ PONG → heartbeat_waiting = false
                      └─ TEXT/BINARY → パケット完了処理
      │
      ▼
[get_packet() / get_available_packet_count()]
      │
      └─ in_buffer.read_packet()
```

### 3.4 ハートビート処理フロー

```
[poll() 内ハートビート処理] - wsl_peer.cpp: 715-737
      │
      ▼
[heartbeat_interval_msec > 0 ?]
      │
      ├─ No → スキップ
      │
      └─ Yes → [経過時間チェック]
               │
               └─ ticks - last_heartbeat > heartbeat_interval_msec
                       │
                       ├─ heartbeat_waiting == true
                       │       │
                       │       └─ [タイムアウト] → close(-1)
                       │
                       └─ heartbeat_waiting == false
                               │
                               ├─ WSLAY_PING送信
                               ├─ heartbeat_waiting = true
                               └─ last_heartbeat = ticks
      │
      ▼
[PONGフレーム受信時]
      │
      └─ heartbeat_waiting = false
```

### 3.5 WebSocketMultiplayerPeerデータフロー

```
[サーバーモード: _poll_server()] - websocket_multiplayer_peer.cpp: 278-401
      │
      ▼
[新規接続受け入れ]
      │
      ├─ tcp_server->take_connection()
      └─ pending_peers[id] = peer
      │
      ▼
[保留中ピア処理]
      │
      ├─ [TLSハンドシェイク] (設定時)
      ├─ [WebSocketハンドシェイク]
      │       └─ peer.ws->accept_stream()
      │
      └─ [接続確立]
              ├─ peer_id送信
              ├─ peers_map[id] = peer.ws
              └─ emit_signal("peer_connected", id)
      │
      ▼
[接続済みピアポーリング]
      │
      ├─ ws->poll()
      ├─ パケット受信 → incoming_packets追加
      └─ 切断検出 → emit_signal("peer_disconnected")

[クライアントモード: _poll_client()] - websocket_multiplayer_peer.cpp: 218-276
      │
      ▼
[peer->poll()]
      │
      └─ STATE_OPEN時
              │
              ├─ [ID受信] (初回)
              │       └─ unique_id = 受信値
              │
              └─ [パケット受信]
                      └─ incoming_packets追加
```

## 4. コードリーディングガイド

### 4.1 推奨読解順序

1. **データ構造の理解**
   - `modules/websocket/websocket_peer.h` (36-127行目): WebSocketPeer基底クラス定義
   - `modules/websocket/wsl_peer.h` (45-166行目): WSLPeer実装クラス定義

2. **エントリーポイント**
   - `websocket_peer.cpp` **_bind_methods()** (41-93行目): GDScriptバインディング
   - `wsl_peer.cpp` **connect_to_url()** (477-561行目): クライアント接続

3. **コア処理**
   - `wsl_peer.cpp` **poll()** (697-779行目): メイン処理ループ
   - `wsl_peer.cpp` **_do_client_handshake()** (312-416行目): クライアントハンドシェイク
   - `wsl_peer.cpp` **_do_server_handshake()** (216-307行目): サーバーハンドシェイク

### 4.2 重要な処理ポイント

#### セッションキー生成（wsl_peer.cpp: 682-689行目）
```cpp
String WSLPeer::_generate_key() {
    // Random key
    Vector<uint8_t> bkey;
    int len = 16; // 16 bytes, as per RFC
    bkey.resize(len);
    _wsl_genmask_callback(nullptr, bkey.ptrw(), len, nullptr);
    return CryptoCore::b64_encode_str(bkey.ptrw(), len);
}
```

#### キーレスポンス計算（wsl_peer.cpp: 691-695行目）
```cpp
String WSLPeer::_compute_key_response(const String &p_key) {
    String key = p_key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; // Magic UUID as per RFC
    Vector<uint8_t> sha = key.sha1_buffer();
    return CryptoCore::b64_encode_str(sha.ptr(), sha.size());
}
```

#### wslayコールバック登録（wsl_peer.cpp: 672-680行目）
```cpp
wslay_event_callbacks WSLPeer::_wsl_callbacks = {
    _wsl_recv_callback,
    _wsl_send_callback,
    _wsl_genmask_callback,
    _wsl_recv_start_callback,
    _wsl_frame_recv_chunk_callback,
    nullptr,
    _wsl_msg_recv_callback
};
```

### 4.3 プログラム呼び出し階層図

```
WebSocketPeer::connect_to_url(url, tls_options)
    │
    └── WSLPeer::connect_to_url()  [wsl_peer.cpp: 477-561]
            │
            ├── p_url.parse_url()  [URL解析]
            │
            ├── Resolver::start(host, port)  [DNS解決開始]
            │       └── IP::resolve_hostname_queue_item()
            │
            ├── tcp.instantiate()  [TCP作成]
            │
            └── session_key = _generate_key()  [キー生成]
                    └── CryptoCore::b64_encode_str()

WSLPeer::poll()  [wsl_peer.cpp: 697-779]
    │
    ├── [STATE_CONNECTING]
    │       │
    │       ├── _do_client_handshake()  [クライアント]
    │       │       │
    │       │       ├── Resolver::try_next_candidate()
    │       │       ├── StreamPeerTLS::connect_to_stream()  [TLS時]
    │       │       ├── connection->put_partial_data()  [リクエスト送信]
    │       │       └── _verify_server_response()  [レスポンス検証]
    │       │               └── wslay_event_context_client_init()
    │       │
    │       └── _do_server_handshake()  [サーバー]
    │               │
    │               ├── _parse_client_request()  [リクエスト解析]
    │               └── wslay_event_context_server_init()
    │
    └── [STATE_OPEN / STATE_CLOSING]
            │
            ├── [ハートビート処理]
            │       └── wslay_event_queue_msg(WSLAY_PING)
            │
            ├── wslay_event_recv()  [受信]
            │       │
            │       ├── _wsl_recv_callback()
            │       │       └── connection->get_partial_data()
            │       │
            │       └── _wsl_msg_recv_callback()
            │               └── in_buffer.write_packet()
            │
            └── wslay_event_send()  [送信]
                    └── _wsl_send_callback()
                            └── connection->put_partial_data()

WebSocketPeer::send(buffer, size, mode)
    │
    └── WSLPeer::_send()  [wsl_peer.cpp: 781-797]
            │
            ├── wslay_event_queue_msg()
            └── wslay_event_send()

WebSocketMultiplayerPeer::create_server(port, bind_ip, tls_options)
    │
    └── [websocket_multiplayer_peer.cpp: 181-195]
            │
            ├── tcp_server.instantiate()
            ├── tcp_server->listen(port, bind_ip)
            └── connection_status = CONNECTION_CONNECTED

WebSocketMultiplayerPeer::poll()  [websocket_multiplayer_peer.cpp: 403-412]
    │
    ├── [サーバー] _poll_server()
    │       │
    │       ├── tcp_server->take_connection()  [新規接続]
    │       ├── ws->accept_stream()  [ハンドシェイク]
    │       └── ws->poll() + get_packet()  [データ受信]
    │
    └── [クライアント] _poll_client()
            │
            └── peer->poll() + get_packet()  [データ受信]
```

### 4.4 関連ファイル一覧

| ファイルパス | 種別 | 役割 |
|-------------|------|------|
| modules/websocket/websocket_peer.h | ヘッダー | WebSocketPeer基底クラス定義 |
| modules/websocket/websocket_peer.cpp | 実装 | WebSocketPeer基底実装 |
| modules/websocket/wsl_peer.h | ヘッダー | WSLPeer実装クラス定義 |
| modules/websocket/wsl_peer.cpp | 実装 | wslay使用のWebSocket実装 |
| modules/websocket/websocket_multiplayer_peer.h | ヘッダー | マルチプレイヤー対応クラス |
| modules/websocket/websocket_multiplayer_peer.cpp | 実装 | サーバー/クライアント管理 |
| modules/websocket/packet_buffer.h | ヘッダー | パケットバッファ |
| core/io/stream_peer_tcp.h | ヘッダー | TCP接続 |
| core/io/stream_peer_tls.h | ヘッダー | TLS接続 |
| thirdparty/wslay/ | 外部ライブラリ | WebSocketプロトコル処理 |

## 5. 設計上の考慮事項

### 5.1 プロトコル準拠
- RFC 6455 WebSocketプロトコル完全準拠
- バージョン13（Sec-WebSocket-Version: 13）
- Magic UUID使用のSec-WebSocket-Accept計算

### 5.2 セキュリティ
- TLS/SSLによるwss://サポート
- ランダムマスキングキー生成（クライアント側）
- 証明書検証オプション

### 5.3 パフォーマンス
- 非バッファリングモード（wslay_event_config_set_no_buffering）
- 設定可能なバッファサイズ
- TCP_NODELAY対応

### 5.4 接続維持
- Ping/Pongハートビート機構
- タイムアウト検出
- 正常/異常切断の区別

## 6. 使用例

### 6.1 基本的なWebSocketクライアント
```gdscript
var ws = WebSocketPeer.new()

func _ready():
    ws.connect_to_url("wss://echo.websocket.org")

func _process(delta):
    ws.poll()
    var state = ws.get_ready_state()

    if state == WebSocketPeer.STATE_OPEN:
        while ws.get_available_packet_count():
            var packet = ws.get_packet()
            print("Received: ", packet.get_string_from_utf8())
    elif state == WebSocketPeer.STATE_CLOSING:
        pass  # 切断処理中
    elif state == WebSocketPeer.STATE_CLOSED:
        var code = ws.get_close_code()
        var reason = ws.get_close_reason()
        print("Closed: ", code, " - ", reason)
```

### 6.2 テキストメッセージ送信
```gdscript
func send_message(message: String):
    if ws.get_ready_state() == WebSocketPeer.STATE_OPEN:
        ws.send_text(message)
```

### 6.3 バイナリデータ送信
```gdscript
func send_binary(data: PackedByteArray):
    if ws.get_ready_state() == WebSocketPeer.STATE_OPEN:
        ws.send(data, WebSocketPeer.WRITE_MODE_BINARY)
```

### 6.4 サブプロトコル使用
```gdscript
func _ready():
    ws.supported_protocols = PackedStringArray(["chat", "json"])
    ws.connect_to_url("wss://example.com/ws")

func _on_connected():
    var protocol = ws.get_selected_protocol()
    print("Using protocol: ", protocol)
```

### 6.5 ハートビート設定
```gdscript
func _ready():
    ws.heartbeat_interval = 30.0  # 30秒間隔
    ws.connect_to_url("wss://example.com/ws")
```

### 6.6 WebSocketMultiplayerPeerサーバー
```gdscript
var peer = WebSocketMultiplayerPeer.new()

func _ready():
    peer.create_server(8080)
    multiplayer.multiplayer_peer = peer
    multiplayer.peer_connected.connect(_on_peer_connected)

func _on_peer_connected(id):
    print("Peer connected: ", id)
```

### 6.7 WebSocketMultiplayerPeerクライアント
```gdscript
var peer = WebSocketMultiplayerPeer.new()

func _ready():
    peer.create_client("ws://localhost:8080")
    multiplayer.multiplayer_peer = peer
```

## 7. 関連機能
- [No.38 HTTPリクエスト](./38-HTTPリクエスト.md) - HTTP通信
- [No.40 マルチプレイヤー](./40-マルチプレイヤー.md) - マルチプレイヤー機能
