# 機能設計書 41-TCP接続

## 概要

本ドキュメントは、libuv の `uv_tcp_connect()` APIによる非同期TCP接続機能の設計を記述する。クライアント側からリモートサーバへのTCP接続を非同期的に開始し、接続完了時にコールバックで通知する機能である。

### 本機能の処理概要

**業務上の目的・背景**：ネットワーククライアントアプリケーションにおいて、リモートサーバへのTCP接続をノンブロッキングで行う必要がある。従来のブロッキングconnect()はスレッドを停止させるため、イベント駆動型プログラミングモデルでは非同期接続が不可欠である。本機能はOSごとの差異（EINPROGRESS, ECONNREFUSED等のエラー処理）を吸収し、統一的なAPIを提供する。

**機能の利用シーン**：TCPクライアントアプリケーションがリモートサーバに接続する際に使用される。HTTPクライアント、データベース接続、RPC呼び出し、プロキシサーバなど、クライアント側通信を行うあらゆるアプリケーションで利用される。

**主要な処理内容**：
1. ハンドルの型バリデーション（UV_TCPであることを確認）
2. アドレスファミリに基づくアドレス長の算出（IPv4: sizeof(sockaddr_in), IPv6: sizeof(sockaddr_in6)）
3. 必要に応じてソケットの遅延生成（maybe_new_socket）
4. IPv6リンクローカルアドレスのscope_id自動補完
5. connect()システムコールの発行（EINTRのリトライ対応）
6. 非同期接続進行中（EINPROGRESS）の場合のI/Oウォッチャー登録
7. 接続完了時のコールバック通知

**関連システム・外部連携**：OSのソケットAPI（connect()システムコール）を使用する。IPv6リンクローカルアドレスの場合はifaddrs/uv_interface_addressesを使用してscope_idを解決する。

**権限による制御**：特別な権限は不要。ただし、OSレベルのファイアウォール設定やネットワーク制限に依存する。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 18 | TCP | 主機能 | uv_tcp_connect()による非同期TCP接続のAPI定義 |
| 35 | ネットワーク | 補助機能 | TCPクライアント接続の使用例 |

## 機能種別

ネットワーク接続処理（非同期I/O操作）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| req | uv_connect_t* | Yes | 接続リクエスト構造体へのポインタ | NULLでないこと |
| handle | uv_tcp_t* | Yes | 初期化済みTCPハンドル | handle->type == UV_TCP |
| addr | const struct sockaddr* | Yes | 接続先アドレス（sockaddr_in or sockaddr_in6） | sa_family が AF_INET または AF_INET6 |
| cb | uv_connect_cb | Yes | 接続完了時のコールバック関数 | - |

### 入力データソース

アプリケーションから直接指定される。接続先アドレスは`uv_ip4_addr()`や`uv_ip6_addr()`で事前に構築される。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 戻り値 | int | 成功時0、失敗時負のエラーコード（UV_EINVAL, UV_EALREADY等） |
| req->cb | コールバック | 接続完了時に呼ばれるコールバック。statusパラメータで成功/失敗を通知 |

### 出力先

コールバック関数への非同期通知。接続完了（成功または失敗）時にイベントループから呼び出される。

## 処理フロー

### 処理シーケンス

```
1. 型チェック（uv-common.c）
   └─ handle->type != UV_TCP の場合 UV_EINVAL を返す
2. アドレスファミリに基づくアドレス長算出（uv-common.c）
   └─ AF_INET: sizeof(sockaddr_in), AF_INET6: sizeof(sockaddr_in6)
3. uv__tcp_connect() 呼び出し（unix/tcp.c）
   ├─ 二重接続チェック（connect_req != NULL → UV_EALREADY）
   ├─ delayed_error確認
   ├─ maybe_new_socket()でソケット生成・設定
   ├─ IPv6リンクローカルアドレスのscope_id補完
   ├─ connect()システムコール発行
   ├─ EINPROGRESS: 非同期接続として続行
   ├─ ECONNREFUSED: delayed_errorに記録
   └─ その他エラー: エラーコードを返す
4. リクエスト初期化・登録
   └─ uv__req_init()でリクエストをループに登録
5. I/Oウォッチャー登録
   └─ POLLOUT監視開始（書き込み可能=接続完了の意味）
6. delayed_errorがある場合はI/Oフィード
   └─ 次のイベントループ反復で即座にコールバック呼び出し
```

### フローチャート

```mermaid
flowchart TD
    A[uv_tcp_connect 呼び出し] --> B{handle->type == UV_TCP?}
    B -->|No| C[UV_EINVAL を返す]
    B -->|Yes| D{addr->sa_family チェック}
    D -->|AF_INET/AF_INET6| E[addrlen 算出]
    D -->|その他| C
    E --> F[uv__tcp_connect 呼び出し]
    F --> G{connect_req != NULL?}
    G -->|Yes| H[UV_EALREADY を返す]
    G -->|No| I{delayed_error?}
    I -->|Yes| J[リクエスト初期化へスキップ]
    I -->|No| K[maybe_new_socket でソケット確保]
    K --> L{IPv6 リンクローカル?}
    L -->|Yes| M[scope_id 補完]
    L -->|No| N[connect システムコール]
    M --> N
    N --> O{結果判定}
    O -->|成功/EINPROGRESS| J
    O -->|ECONNREFUSED| P[delayed_error 設定]
    O -->|その他エラー| Q[エラーコード返却]
    P --> J
    J --> R[req 初期化・I/Oウォッチャー登録]
    R --> S[0 を返す]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-41-1 | 二重接続禁止 | 同一ハンドルで接続中に再接続を試みるとUV_EALREADYが返る | handle->connect_req != NULL |
| BR-41-2 | アドレスファミリ制限 | AF_INETまたはAF_INET6のみサポート | 常時 |
| BR-41-3 | IPv6リンクローカル補完 | scope_idが0の場合、最初のリンクローカルインターフェースのscope_idを自動設定 | addr がFE80::/10のIPv6アドレス |
| BR-41-4 | ECONNREFUSED遅延通知 | Solaris/OpenBSDでのECONNREFUSEDは即座にエラーを返さず、次のループ反復で通知 | connect()がECONNREFUSEDを返した場合 |

### 計算ロジック

特になし。

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

該当なし（ネットワーク操作機能のため）。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| UV_EINVAL | バリデーションエラー | ハンドル型がUV_TCPでない、またはアドレスファミリが不正 | 引数を確認する |
| UV_EALREADY | 状態エラー | 既に接続リクエストが進行中 | 前の接続完了を待つ |
| UV_ECONNREFUSED | 接続拒否 | リモートサーバが接続を拒否 | サーバの状態を確認する |
| UV_ENOENT | ソケットエラー | ソケットの生成に失敗 | システムリソースを確認する |

### リトライ仕様

connect()システムコールがEINTRを返した場合は自動的にリトライする。アプリケーションレベルのリトライはユーザー側で実装する必要がある。

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

該当なし。

## パフォーマンス要件

非同期接続のため、connect()呼び出し自体はノンブロッキングで即座に返る。接続完了までの時間はネットワーク状況に依存する。

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

接続先アドレスの妥当性はアプリケーション側で検証する必要がある。TLS/SSL暗号化はlibuv単体では提供されず、別途実装が必要。

## 備考

- IPv6リンクローカルアドレスのscope_id自動補完は、AIXではuv_interface_addresses()を使用し、その他のUNIXではgetifaddrs()を使用する
- OpenBSDではEINVALがECONNREFUSEDと同等に扱われる

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | uv.h | `include/uv.h` | uv_tcp_s構造体（592-596行目）、uv_connect_s構造体（644-649行目）の定義 |
| 1-2 | uv.h | `include/uv.h` | uv_connect_cb型定義、uv_tcp_connect()のAPI宣言（638-641行目） |

**読解のコツ**: uv_tcp_s はUV_HANDLE_FIELDSとUV_STREAM_FIELDSマクロで展開されるため、uv_handle_t、uv_stream_tの親構造を理解する必要がある。uv_connect_sはUV_REQ_FIELDSマクロでリクエスト共通フィールドを持つ。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | uv-common.c | `src/uv-common.c` | uv_tcp_connect()のバリデーション処理（394-411行目） |

**主要処理フロー**:
1. **400-401行目**: handle->type != UV_TCP のチェック
2. **403-408行目**: アドレスファミリに基づくaddrlen算出
3. **410行目**: プラットフォーム固有のuv__tcp_connect()呼び出し

#### Step 3: プラットフォーム固有実装を理解する（Unix）

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | tcp.c | `src/unix/tcp.c` | uv__tcp_connect()の実装（277-349行目） |
| 3-2 | tcp.c | `src/unix/tcp.c` | maybe_new_socket()（85-110行目）、new_socket()（64-82行目） |
| 3-3 | tcp.c | `src/unix/tcp.c` | uv__is_ipv6_link_local()（214-225行目）、uv__ipv6_link_local_scope_id()（228-274行目） |

**主要処理フロー**:
- **286行目**: assertでハンドル型を検証
- **288-289行目**: 二重接続チェック（connect_req != NULL → UV_EALREADY）
- **294-298行目**: maybe_new_socketでソケット生成（UV_HANDLE_READABLE | UV_HANDLE_WRITABLE）
- **300-306行目**: IPv6リンクローカルアドレスのscope_id補完
- **308-311行目**: connect()システムコール発行（EINTRリトライ）
- **318-333行目**: エラー処理（EINPROGRESS: 正常、ECONNREFUSED: 遅延通知）
- **337-346行目**: リクエスト初期化、I/Oウォッチャー登録

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

```
uv_tcp_connect() [src/uv-common.c:394]
    │
    └─ uv__tcp_connect() [src/unix/tcp.c:277]
           │
           ├─ maybe_new_socket() [src/unix/tcp.c:85]
           │      └─ new_socket() [src/unix/tcp.c:64]
           │             ├─ uv__socket() [src/unix/core.c]
           │             └─ uv__stream_open() [src/unix/stream.c]
           │
           ├─ uv__is_ipv6_link_local() [src/unix/tcp.c:214]
           │
           ├─ uv__ipv6_link_local_scope_id() [src/unix/tcp.c:228]
           │
           ├─ connect() [OS system call]
           │
           ├─ uv__req_init() [src/uv-common.h]
           │
           ├─ uv__io_start() [src/unix/core.c]
           │
           └─ uv__io_feed() [src/unix/core.c] (delayed_error時)
```

### データフロー図

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

uv_connect_t* req     ──▶  uv_tcp_connect()          ──▶   戻り値(0/エラー)
uv_tcp_t* handle      ──▶  ├ バリデーション
sockaddr* addr        ──▶  ├ ソケット生成
uv_connect_cb cb      ──▶  ├ connect() syscall
                           ├ req初期化
                           └ I/Oウォッチャー登録    ──▶   cb(req, status)
                                                           (非同期コールバック)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| uv.h | `include/uv.h` | ヘッダー | uv_tcp_connect()のAPI宣言、uv_tcp_s/uv_connect_s構造体定義 |
| uv-common.c | `src/uv-common.c` | ソース | uv_tcp_connect()のバリデーション・ディスパッチ |
| tcp.c (unix) | `src/unix/tcp.c` | ソース | Unix系プラットフォームのuv__tcp_connect()実装 |
| tcp.c (win) | `src/win/tcp.c` | ソース | Windowsプラットフォームの接続実装 |
| stream.c | `src/unix/stream.c` | ソース | uv__stream_open()によるストリームFD管理 |
| core.c | `src/unix/core.c` | ソース | uv__io_start()/uv__io_feed()によるI/Oウォッチャー管理 |
