19 ポイント 投稿者 GN⁺ 2025-11-16 | 1件のコメント | WhatsAppで共有
  • TCP(Transmission Control Protocol) は、不安定なネットワーク環境でも 信頼性があり順序が保証されたデータ転送 を可能にする、インターネットの中核プロトコル
  • IP がホスト間のデータ配送だけを担うのに対し、TCP は ポートベースのプロセス間通信エラー回復・再送・順序制御 を行う
  • フロー制御(flow control)輻輳制御(congestion control) により、受信バッファやネットワーク帯域幅の限界を超えないよう調整
  • C 言語で実装した シンプルな TCP サーバーと HTTP サーバーの例 を通じて、ソケット生成、バインド、リッスン、接続受け入れ、送受信の過程が具体的に説明される
  • TCP の シーケンス番号・ACK 番号、ウィンドウ、チェックサム、フラグ(SYN/ACK/FIN/RST) などの内部構造は、今日のインターネットの安定動作を支える重要な基盤

TCP の必要性と役割

  • IP はホスト間のパケット配送だけを担い、プロセス間通信 のためには TCP/UDP のような トランスポート層 が必要
    • IP アドレスは「建物」、ポートは「アパート」にたとえられ、各アプリケーションはポートにバインドされて通信する
  • TCP は パケット損失・重複・順序の入れ替わり などのネットワークの不安定さを隠蔽し、再送・チェックサム などで信頼性を保証
  • ルーターはシンプルに保たれ、信頼性は通信の両端で処理 されることで、ネットワークインフラの複雑さを減らす
  • この構造のおかげで、HTTP、SMTP、SSH など主要なインターネットサービスが安定して動作する

フロー制御と輻輳制御

  • 受信側は カーネルの受信バッファ(receive buffer) を通じてデータを一時保存し、バッファサイズは net.ipv4.tcp_rmem で設定
  • 送信側は、受信側が許容可能なデータ量を ウィンドウ(window) フィールドで受け取り、送信量を調整
  • ネットワーク全体の帯域差による 輻輳(congestion) を防ぐため、TCP は 輻輳制御アルゴリズム を導入
    • 1986 年に発生した 輻輳崩壊(congestion collapse) をきっかけに、「バックオフ(back-off)」メカニズムが追加された

TCP サーバーおよび HTTP サーバーの例

  • C 言語で書かれた 基本的な TCP エコーサーバー は、クライアントの入力を受けて「you sent:」を付けて再送する
    • socket(), bind(), listen(), accept(), send(), recv() などの Berkeley ソケット API を使用
    • サーバーが sleep() 中のとき、クライアントのデータは 受信バッファで待機 し、その後順次処理される
  • シンプルな HTTP/1.1 サーバーの例 では、TCP 接続を通じてリクエストを受け付け、HTTP/1.1 200 OK ヘッダーと本文を送信
    • リクエスト回数を i でカウントし、curl localhost:8080 を実行すると「[1] Yo, I am a legit web server」形式の応答が出力される

TCP セグメント構造と主要フィールド

  • TCP セグメントは 送信元・宛先ポート、シーケンス番号、ACK 番号、ウィンドウサイズ、チェックサム、フラグ などで構成
    • ポートは 16 ビットずつ割り当てられ、最大 64K ポート を利用可能
    • 接続は (プロトコル, 送信元 IP, 送信元ポート, 宛先 IP, 宛先ポート)5 タプル で識別
  • シーケンス番号 は送信されたバイト範囲を、ACK 番号 は受信完了したバイトを示す
    • 欠落データがある場合、ACK はその地点で止まり、再送後に累積 ACK として更新される
  • フラグビット は接続状態を制御
    • SYN/ACK3-way ハンドシェイク によって接続を確立
    • FIN4-way ハンドシェイク で接続を終了
    • RST は異常終了やエラー時に即座に接続を解除
  • ウィンドウフィールド は受信可能なデータ量を示し、ss コマンドでバッファ状態(rb131072, tb16384)を確認可能
  • チェックサム(checksum) は、セグメント内の 16 ビット単位の合算によってエラー検出を行う

結論

  • TCP は 信頼性・順序・完全性 を保証し、不安定なインターネット環境でもアプリケーションが正常に動作するよう支援
  • 数十年前は数 KB の転送も難しかったが、今日では 4K ストリーミング が日常化するほど発展
  • このような安定した通信を可能にした TCP の設計と実装の精緻さ が、インターネットの継続的な成長を支える基盤となっている

1件のコメント

 
GN⁺ 2025-11-16
Hacker Newsの意見
  • 信頼性のない データグラム層 の上に信頼できるデータストリームを作ろうとすると、結局ほぼTCPと同じ形になる
    TCPの初期の限界は、小さな ウィンドウサイズ、損失パケット処理の不十分さ、そして単一ストリームしか扱えない点だった
    こうした問題を解決するために SCTPQUIC が登場した
    輻輳制御アルゴリズムはプロトコルの一部ではなく、各接続の両端で動作するコードである
    初期のアルゴリズム(Reno、Vegasなど)は単純だったが十分に効果的で、その後も大容量バッファ・長いRTT・公平性などを扱う研究が続いている

    • Web開発者側にも、マルチストリームを十分に活用できなかった責任があると思う
      以前、JavaScriptで複数ダウンロードを1本のストリーム上で 優先順位付けとキャンセル機能 によって制御できるライブラリを作ったことがある
      GreaseMonkeyスクリプトで出会い系サイトのサムネイルをバックグラウンドで先読みさせ、スクロール位置に応じてプリロードするようにした
      結果としてサーバー負荷を減らしつつユーザー体験を改善できた
      面白いことに、そのスクリプトをあるマッチ相手に共有したのだが、その人とは今でも一緒にいる — ほとんど Tinder以前のTinder だったわけだ
    • TCPが作られた当時は、電話網中心の 回線交換(circuit switching) 的な発想が支配的だった
      TCPはパケット交換網の上で仮想回線を提供する構造であり、信頼性を 再送 によって実現するという概念はフランスの Cylades ネットワークに由来する
    • TCPの根本的な欠陥の1つは 安全にできないこと
      攻撃者はネットワーク上のどこからでもデータを 注入(inject) したり、RSTパケット で接続を切断したりできる
      ファイアウォールでRSTを遮断すれば安定性は高まるが、偽造されたシーケンス番号による 非同期化攻撃 は依然として可能だ
      そのため、すべてのアプリケーションは別個の接続として resume機能 を実装しなければならず、TCPのスロースタート(slow start)の問題も抱えることになる
      また、アドレスとポートを分離した概念そのものも非効率だと思う
    • アプリケーションによっては、単一のTCP接続でも十分うまく動作する
      たとえば DNS over TLS(DoT) では、1本のTCP接続で複数のクエリを同時に送り、応答を 順不同で 受け取れる
      これは複数接続を開くより効率的で行儀のよい方法だ
      QUICのほうが速いかどうかは分からないが、サーバー対応はまだ限定的だ
      HTTP/1.1パイプライニング でも似たことはできるが、応答は順番通りに返ってくる
    • TCPの 輻輳制御アルゴリズム がプロトコルの外部にあるという点は、当時としては非常に革新的だった
      しかし多くの大学の講義ではこの点が強調されないため、TCPには単一のアルゴリズムしかないと誤解されがちだ
  • SCTP に愛着がある人はいるだろうか
    SCTPはUDPのメッセージ指向性とTCPの信頼性を組み合わせたプロトコルで、マルチストリーミングマルチホーミング をサポートする
    複数の独立したストリームを並列送信できるため、Webページのテキストと画像を同時に送れる
    詳しくは Wikipedia: Stream Control Transmission Protocol を参照

    • SCTPは問題の半分しか解決せず、その代わりに新しい欠陥をいくつも持ち込んだ
      結局、最良の答えは UDP上の信頼性層、つまり QUIC
    • BSDを愛用し、Erlang で仕事をしている立場からすると、SCTPは大好きだ
  • IPだけで直接パケットを送れるのか気になっていた
    中間ルーターがTCPやUDP以外のパケットを拒否しそうに思えた

    • 直接 IPパケットを細工して送信 することはできる
      IPv4なら IANAプロトコル番号一覧 で0〜255のいずれかを指定すればよい
      コアルーターはこのフィールドを検査しないが、NATやISPの機器は検査する場合がある
      2台のLinuxサーバー間であれば、実験用番号(253、254)でも通信可能だ
    • ICMP も忘れてはならない。IPv6ではさらに重要だ
      IPsec、GRE、L2TPのようなプロトコルもTCP/UDPではない
      企業ネットワークのファイアウォールやNAT環境では、任意のプロトコルが遮断されることがある
      NATは end-to-end原則 を壊し、その結果、人々はTCPやUDPの上に、あるいは HTTPの上に 何でも載せるようになった
    • NATやロードバランサーがなければ問題ないが、最近では一般的なのでIPv6のほうがよいかもしれない
    • ルーターは レイヤー3機器 なので、TCP/UDPかどうかは気にしない
      ただしECMPハッシュのエントロピーが減る程度の影響はある
      結局のところ、相手がそのプロトコルを理解できるかどうかが肝心だ
    • TCPとUDPは単なるIPペイロードにすぎず、ルーターはそれを解釈しない
      ポート番号は単なる ノード内部のサービス識別子 にすぎない
  • RUDP(Plan9) はTCPとUDPの間にある優れた折衷案だった
    Reliable User Datagram Protocol 参照

  • TCPがデフォルトになったおかげで、信頼性や順序保証が不要な場合であっても無条件で使われてきた
    いまは HTTP/3(QUICベース) が広がることで状況が改善する可能性がある
    ただしQUICははるかに複雑で、その強力さが役立つのは一部の人だけだ
    UDP + WireGuardスタイルの単純な暗号化層 のほうがよりよい代案かもしれない

  • TCPは人類の偉大な発明の1つだが、半接続型ネットワーク(NATベース) が支配的になることは予想していなかった

    • 「NATのことを言っているのか?」という問いが出る
    • 1981年に戻って「世界中で一意なアドレスの代わりに制限された範囲のアドレスを使い、一部のノードは到達不能にしよう」と言ったら
      当時のエンジニアたちは、なぜわざわざそんなに複雑にするのかと聞いたはずだ
      結局、今の 非対称リンク構造 やクライアント–サーバーの区別は、こうした発想から生まれた
  • TCPの 輻輳制御アルゴリズム には、開発者があまり知らない興味深い効果がある
    新しい接続でデータを送ると、初期送信は遅く、速度上昇は 遅延(latency) によって決まる
    データセンターでは、RTTを数マイクロ秒縮めるだけでも大きな速度向上が得られる
    ほとんどのTCPスタックはバイト単位ではなく セグメント単位 で速度上昇を計算するため、ジャンボフレーム を使うと6倍速く上がることがある
    AWSはこのため 低スイッチング遅延ジャンボフレーム対応 に多大な労力を注いでいる
    専門家はこうしたチューニングを行うが、多くの人はなぜ10Gbpsリンクで10Gbps出ないのか不思議に思っている

  • IP上に独自プロトコルを作るのは 非常に簡単なこと だった
    15年前なら、Pythonで 直接パケットを組み立てて 実験できた