Goのnet/httpはTCP接続をどのように扱うか

[![表紙](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967563/3c233c48-df6e-40cc-95a0-625d039c52c3.png)](https://leapcell.io/?lc_t=q_gotcpmgr)

## 前書き

始める前に、Socket とファイルディスクリプタという 2 つの概念について簡単に説明します。これは後の理解を助けるためです。

### Socket とは

Socket（ソケット）はネットワーク通信の基本的な抽象であり、アプリケーションがネットワークプロトコルスタックにアクセスするための標準インターフェースを提供します。簡単に言えば、Socket はネットワーク通信のエンドポイントであり、異なるコンピュータ上のプログラム同士がネットワークを通じてデータ交換を行うことを可能にします。

Socket の主な特徴：

- アプリケーション層とトランスポート層の間のインターフェースである
- 特殊なファイルとみなされ、読み書き操作ができる
- 様々な種類がある：TCP ソケット（コネクション型）、UDP ソケット（コネクションレス型）など

### ファイルディスクリプタとは

ファイルディスクリプタ（File Descriptor）は、オペレーティングシステムが開いているファイルを識別し管理するために使う整数値です。Unix/Linux システムでは、すべてがファイルとみなされ、通常のファイル、ディレクトリ、デバイス、さらにはネットワーク接続も含まれます。

ファイルディスクリプタの重要なポイント：

- 0 から始まる非負整数である（0 は標準入力、1 は標準出力、2 は標準エラー）
- OS カーネル内でファイルディスクリプタはファイルテーブルエントリへのインデックスである
- 各プロセスは独自のファイルディスクリプタテーブルを持っている

### Socket とファイルディスクリプタの関係

Unix/Linux システムでは、Socket も特殊なファイルとみなされるため、対応するファイルディスクリプタがあります。Socket を作成するとき：

- OS がファイルディスクリプタを割り当てる
- そのファイルディスクリプタは以降のネットワーク操作（読み込み、書き込み、クローズなど）に使われる
- アプリケーションはこのファイルディスクリプタを通じて Socket とやり取りする

## TCP 接続確立のプロセス

### ソケットの作成

```go
// net パッケージ内部実装
fd, err := socket(family, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
```

このステップでは、システムコールによってソケットのファイルディスクリプタが作成されます。

### サーバー側のバインド（Bind）およびリッスン（Listen）

```go
// 簡略化したサーバー側のコードフロー
bind(fd, addr)
listen(fd, backlog)
```

サーバーはソケットを特定のアドレスとポートにバインドし、接続要求のリッスンを開始します。

### 接続の受け入れ（Accept）

```go
// net/http/server.go 簡易版
func (srv *Server) Serve(l net.Listener) error {
    for {
        rw, err := l.Accept()  // 新しい接続を受け入れる
        if err != nil {
            // エラー処理
            continue
        }
        go srv.newConn(rw).serve(ctx)  // 各接続ごとに新しい goroutine を作成
    }
}
```

### クライアント側の接続（Connect）

```go
// net/http/transport.go 簡易版
func (t *Transport) dialConn(ctx context.Context, addr string) (*conn, error) {
    // TCP 接続の作成
    netConn, err := t.dial(ctx, "tcp", addr)
    if err != nil {
        return nil, err
    }
    // HTTP 接続としてラップ
    return &conn{
        conn: netConn,
        // ... その他のフィールド
    }, nil
}
```

### データ転送：

```go
// データの読み込み
n, err := syscall.Read(fd, buf)
// データの書き込み
n, err := syscall.Write(fd, data)
```

### 接続のクローズ：

```go
err := syscall.Close(fd)
```

## 重要な実装詳細

### マルチプレクシング（多重化）

- HTTP/1.1 は Keep-Alive 機構で TCP 接続を再利用
- HTTP/2 はストリーム（Stream）によるマルチプレクシングを実現し、複数の HTTP リクエストが同一の TCP 接続を共有する

### コネクションプール管理

```go
// net/http/transport.go
type Transport struct {
    // アイドルコネクションプール
    idleConn     map[connectMethodKey][]*persistConn
    // 最大アイドルコネクション数
    maxIdleConns int
    // ... その他のフィールド
}
```

### タイムアウト制御

```go
// コネクションのタイムアウト設定
conn.SetDeadline(time.Now().Add(timeout))
```

### エラー処理およびリトライ機構

```go
// 簡略化したリトライロジック
for retry := 0; retry < maxRetries; retry++ {
    conn, err := dial()
    if err == nil {
        return conn
    }
    // 待機後にリトライ
    time.Sleep(backoff)
}
```

## ワークフロー

### クライアントが HTTP リクエストを送信する時：

1. まずコネクションプールに利用可能なコネクションがあるかどうかを確認する
2. なければ新しい TCP コネクションを作成する
3. HTTP リクエストデータを送信する
4. レスポンスを待ち、読み取る

### サーバー側がリクエストを処理する時：

1. Accept ループで新しい接続を受け入れる
2. 各接続ごとに goroutine を作成する
3. HTTP リクエストを解析する
4. リクエストを処理し、レスポンスを返す

これが net/http パッケージが TCP プロトコル上で HTTP 接続を実現するためのコアな仕組みです。
抽象化やカプセル化によって、開発者は TCP 接続の低レベルな詳細を直接扱う必要がなくなり、かつ効率的なコネクション管理や再利用の仕組みも提供されています。

---

### [私たちはLeapcell、Goプロジェクトのホスティングの最適解です。](https://leapcell.io/?lc_t=q_gotcpmgr)

[![Leapcell](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967563/0e8bc725-d6fc-44fa-9727-0d61c1dcb84a.png)](https://leapcell.io/?lc_t=q_gotcpmgr)

[Leapcell](https://leapcell.io/?lc_t=q_gotcpmgr)は、Webホスティング、非同期タスク、Redis向けの次世代サーバーレスプラットフォームです：

**複数言語サポート**

- Node.js、Python、Go、Rustで開発できます。

**無制限のプロジェクトデプロイ**

- 使用量に応じて料金を支払い、リクエストがなければ料金は発生しません。

**比類のないコスト効率**

- 使用量に応じた支払い、アイドル時間は課金されません。
- 例: $25で6.94Mリクエスト、平均応答時間60ms。

**洗練された開発者体験**

- 直感的なUIで簡単に設定できます。
- 完全自動化されたCI/CDパイプラインとGitOps統合。
- 実行可能なインサイトのためのリアルタイムのメトリクスとログ。

**簡単なスケーラビリティと高パフォーマンス**

- 高い同時実行性を容易に処理するためのオートスケーリング。
- ゼロ運用オーバーヘッド — 構築に集中できます。

[ドキュメントで詳細を確認！](https://docs.leapcell.io/)

[![Try Leapcell](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967563/52fde1a0-bb33-4b74-8634-949043035476.png)](https://leapcell.io/?lc_t=q_gotcpmgr)

Xでフォローする：[@LeapcellHQ](https://x.com/LeapcellHQ)

---

[ブログでこの記事を読む](https://leapcell.io/blog/go-net-http-internals)