# 機能設計書 70-Socketクラス

## 概要

本ドキュメントは、mrubyにおけるSocketクラスの設計を記述する。mruby-socket gemとして実装され、TCP/UDP/UNIXソケット通信、アドレス解決、ソケットオプション操作等のネットワーク通信機能を提供する。

### 本機能の処理概要

Socketクラスは、BSDソケットAPIをmrubyから利用可能にするネットワーク通信機能を提供する。IOクラスを基底としたクラス階層（BasicSocket > IPSocket > TCPSocket、BasicSocket > Socket）を構築し、TCP/UDP通信、UNIXドメインソケット通信、アドレス情報の解決と変換を実装する。

**業務上の目的・背景**：組み込み機器のネットワーク通信（HTTP API呼び出し、データ送受信等）を実現するため、標準的なソケット通信APIが必要である。mruby-socketはCRubyのSocket APIに準じたインタフェースを提供する。

**機能の利用シーン**：TCPクライアント/サーバの実装、HTTPリクエストの送信、UDPデータグラム送受信、UNIXドメインソケット通信、名前解決（DNS）等で使用される。

**主要な処理内容**：
1. ソケット作成（Socket._socket）
2. 接続（Socket._connect）
3. バインド（Socket._bind）
4. リッスン（Socket._listen）
5. アクセプト（Socket._accept / _accept2）
6. データ送受信（BasicSocket#send / #recv / #recvfrom）
7. ソケットオプション設定・取得（setsockopt / getsockopt）
8. アドレス解決（Addrinfo.getaddrinfo / getnameinfo）
9. アドレス変換（IPSocket.ntop / pton）
10. ソケット制御（shutdown, setnonblock）

**関連システム・外部連携**：mruby-io（IOクラスの基底）、mruby-error（エラーハンドリング）、HAL gem（hal-posix-socket / hal-win-socket）に依存。

**権限による制御**：特になし。ソケット操作はOS側のネットワーク権限に依存。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | CLIベースのため画面なし |

## 機能種別

ネットワーク通信

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| domain | Integer | Yes（socket生成時） | アドレスファミリ（AF_INET等） | Socket::Constants定数 |
| socktype | Integer | Yes（socket生成時） | ソケットタイプ（SOCK_STREAM等） | Socket::Constants定数 |
| protocol | Integer | No | プロトコル（デフォルト0） | 整数 |
| nodename | String | Yes（getaddrinfo時） | ホスト名/IPアドレス | 文字列またはnil |
| servname | String/Integer | Yes（getaddrinfo時） | サービス名/ポート番号 | 文字列/整数/nil |
| mesg | String | Yes（send時） | 送信データ | 文字列 |
| maxlen | Integer | Yes（recv時） | 最大受信バイト数 | 正の整数 |
| flags | Integer | No | 送受信フラグ | 整数 |

### 入力データソース

mrubyスクリプトからの直接呼び出し

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| Socket | Socket/TCPSocket | ソケットオブジェクト |
| 受信データ | String | 受信した文字列データ |
| 送信バイト数 | Integer | 送信したバイト数 |
| Addrinfo配列 | Array[Addrinfo] | アドレス情報の配列 |
| ホスト名情報 | [String, String] | [hostname, service]の配列 |

### 出力先

mruby値スタック上に結果を返却

## 処理フロー

### 処理シーケンス

```
1. ソケット作成
   └─ Socket._socket(domain, type, protocol)
       a. socket()システムコールでソケットfd取得
2. 接続（クライアント）
   └─ Socket._connect(fd, sockaddr)
       a. connect()システムコールでサーバに接続
3. バインド+リッスン（サーバ）
   └─ Socket._bind(fd, sockaddr) + Socket._listen(fd, backlog)
4. アクセプト（サーバ）
   └─ Socket._accept(fd)
       a. accept()システムコールで接続を受け入れ
5. データ送受信
   └─ send(mesg, flags) / recv(maxlen, flags)
       a. send() / recv()システムコールを呼び出し
6. アドレス解決
   └─ Addrinfo.getaddrinfo(host, service, ...)
       a. getaddrinfo()でDNS解決
       b. Addrinfoオブジェクトの配列を生成
```

### フローチャート

```mermaid
flowchart TD
    A[Socket._socket] --> B[socket syscall]
    B --> C[Socket fd]
    C --> D{クライアント/サーバ?}
    D -->|クライアント| E[Socket._connect]
    D -->|サーバ| F[Socket._bind]
    F --> G[Socket._listen]
    G --> H[Socket._accept]
    E --> I[send/recv]
    H --> I
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-70-01 | af_tableルックアップ | アドレスファミリの処理はaf_table静的テーブルで線形探索 | sa2addrlist呼び出し時 |
| BR-70-02 | pf_tableルックアップ | プロトコルファミリ名の取得はpf_table静的テーブルで線形探索 | inspect時 |
| BR-70-03 | Windows対応 | Windowsではrecv/send関数を使用（read/writeの代わり） | _WIN32定義時 |
| BR-70-04 | HAL依存 | プラットフォーム固有のソケット操作はHAL gemに委譲 | ビルド時 |
| BR-70-05 | Socket::Constants | ソケット定数はconst.cstubファイルから自動生成・登録 | gem初期化時 |

### 計算ロジック

af_table/pf_tableは静的テーブルで、アドレスファミリの情報（family, name, port_offset, has_port）を保持する。ポート番号の抽出はテーブル駆動のオフセット計算で行い、プラットフォーム非依存の処理を実現する。

## データベース操作仕様

該当なし

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | SocketError | getaddrinfo/getnameinfo失敗 | ホスト名/サービス名を確認 |
| - | SocketError | 不正なsockaddr | 正しいsockaddrを使用 |
| - | Errno::* | ソケット操作のOSエラー | mrb_sys_fail()でシステムエラー通知 |
| - | ArgumentError | 不正なアドレスファミリ | AF_INET/AF_INET6を使用 |
| - | ArgumentError | 不正なIPアドレス文字列 | 正しいIPアドレス形式を使用 |
| - | EOFError | recv()で接続終了 | 接続状態を確認 |
| - | NotImplementedError | Windowsでのsysseek | ソケットではシーク不可 |

### リトライ仕様

該当なし

## トランザクション仕様

該当なし

## パフォーマンス要件

- af_table/pf_tableは静的コンパクトテーブルでメモリ効率を重視
- ROMテーブル（MRB_MT_INIT_ROM）による効率的なメソッド初期化
- getaddrinfo結果はmrb_ensure()でメモリリーク防止

## セキュリティ考慮事項

- ネットワーク通信は暗号化されないため、機密データの送受信にはTLS/SSLレイヤーが必要
- DNS解決は信頼できるネットワーク環境で行うべき
- サーバソケットのバインドアドレスには注意（0.0.0.0は全インタフェースで待ち受け）

## 備考

- mruby-io（IOクラス）に依存し、BasicSocket < IO < Objectのクラス階層を構築
- Socket::Constantsモジュールはconst.cstubから定数を自動定義
- HAL gem（hal-posix-socket / hal-win-socket）が必須
- Socket::Optionクラスでソケットオプションをオブジェクトとして管理

---

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

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | socket.c | `mrbgems/mruby-socket/src/socket.c` | af_table/pf_table静的テーブル |

**読解のコツ**:
- **44-50行目**: af_info_t構造体 - アドレスファミリ情報（family, name, port_offset, has_port）
- **52-56行目**: pf_info_t構造体 - プロトコルファミリ情報
- **59-82行目**: af_table - AF_INET, AF_INET6, AF_UNIX等のルックアップテーブル
- **97-114行目**: pf_table - PF_INET, PF_INET6等のルックアップテーブル

#### Step 2: アドレス解決を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | socket.c | `mrbgems/mruby-socket/src/socket.c` | Addrinfo.getaddrinfo |

**主要処理フロー**:
- **144-159行目**: gen_addrinfo() - addrinfo linked listからAddrinfo配列を生成
- **178-224行目**: mrb_addrinfo_getaddrinfo() - DNS解決のエントリーポイント
- **235-258行目**: mrb_addrinfo_getnameinfo() - 逆引きDNS解決
- **280-308行目**: sa2addrlist() - sockaddr→アドレス情報配列変換（テーブル駆動）

#### Step 3: 送受信操作を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | socket.c | `mrbgems/mruby-socket/src/socket.c` | BasicSocket#send / #recv |

**主要処理フロー**:
- **708-727行目**: mrb_basicsocket_send() - send/sendtoシステムコール
- **760-807行目**: mrb_basicsocket_setsockopt() - ソケットオプション設定（3引数 or Socket::Optionオブジェクト）
- **819-828行目**: mrb_basicsocket_shutdown() - ソケットシャットダウン

#### Step 4: gem初期化とクラス階層を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | socket.c | `mrbgems/mruby-socket/src/socket.c` | mrb_mruby_socket_gem_init() |

**主要処理フロー**:
- **1319-1368行目**: gem初期化
- **1323行目**: Addrinfoクラス定義
- **1328行目**: BasicSocket < IO
- **1330行目**: IPSocket < BasicSocket
- **1334行目**: TCPSocket < IPSocket
- **1337行目**: Socket < BasicSocket
- **1349行目**: Socket::Optionクラス
- **1353-1356行目**: ROMテーブルによるメソッド初期化
- **1358-1367行目**: Socket::Constantsモジュールとconst.cstub定数登録

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

```
Addrinfo.getaddrinfo(host, service, ...)
    |
    +-- mrb_addrinfo_getaddrinfo()
            +-- getaddrinfo()            ... DNS解決 (システムコール)
            +-- mrb_ensure()             ... メモリリーク防止
                    +-- gen_addrinfo()   ... Addrinfo配列生成
                    +-- free_addrinfo()  ... addrinfo解放

BasicSocket#send(mesg, flags)
    |
    +-- mrb_basicsocket_send()
            +-- socket_fd()              ... fdの取得
            +-- send() / sendto()        ... データ送信 (システムコール)

BasicSocket#recv(maxlen)
    |
    +-- mrb_basicsocket_recv()
            +-- socket_fd()              ... fdの取得
            +-- recv()                   ... データ受信 (システムコール)

IPSocket.ntop(af, addr)
    |
    +-- mrb_ipsocket_ntop()
            +-- mrb_hal_socket_inet_ntop()  ... アドレス→文字列変換（HAL）
```

### データフロー図

```
[入力]                      [処理]                           [出力]

host + service       ──▶ getaddrinfo()          ──▶ Addrinfo[]
                            +-- DNS解決

domain + type        ──▶ socket()               ──▶ Socket (fd)

Socket + sockaddr    ──▶ connect() / bind()     ──▶ 接続/バインド済みSocket

Socket + mesg        ──▶ send()                 ──▶ Integer (送信バイト数)

Socket + maxlen      ──▶ recv()                 ──▶ String (受信データ)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| socket.c | `mrbgems/mruby-socket/src/socket.c` | ソース | Socketクラス全実装（1374行） |
| socket_hal.h | `mrbgems/mruby-socket/src/socket_hal.h` | ヘッダ | HALインタフェース定義 |
| mrbgem.rake | `mrbgems/mruby-socket/mrbgem.rake` | 設定 | gem仕様定義・HAL依存 |
| const.cstub | `mrbgems/mruby-socket/src/const.cstub` | 生成物 | ソケット定数定義 |
| io.h | `mrbgems/mruby-io/include/mruby/io.h` | ヘッダ | 基底IOの構造体定義 |
