Transmission Control Protocol(トランスミッション コントロール プロトコル、TCP)はIPネットワーク上のアプリ間・コネクション型・高信頼性・ストリーム指向の通信プロトコルである。伝送制御プロトコルとも呼ばれる。

## 概要

TCPは通信プロトコルである。TCPではポート、ソケット、コネクションの概念が導入され、IPネットワークのホスト上にあるアプリケーション同士がコネクションを通じて通信する。確立されたコネクションはTCPが定める制御機構によって到着が保証され、破壊が検出され、流量や輻輳が制御されており、これを通信路としてバイトストリームが伝達される。

TCPを用いることでインターネットにおける到達保証付きアプリケーション間メッセージ通信が可能になるため、ファイル転送、電子メール、World Wide Web、リモート管理など様々なインターネット・アプリケーションで利用される。上位プロトコルにはHTTP、FTP、Telnet、SSHなどがある。

TCPはインターネット・プロトコル・スイートを構成し、Internet Protocolの上位プロトコルとして使われる。またOSI参照モデルに当てはめるならトランスポート層にあたる。その仕様はIETFによりRFC 9293 (STD 7) として標準化されている。なお、IPヘッダでのプロトコル番号は6である。

対比されるプロトコルにUser Datagram Protocol (UDP) がある。UDPは信頼性よりもシンプルさ・低レイテンシを重視したデータグラム通信を提供する。TCPはより信頼性が高くその分オーバーヘッドがある。

## 起源

1974年5月、Institute of Electrical and Electronic Engineers (IEEE) が "A Protocol for Packet Network Interconnection" と題した論文を出版。著者はヴィントン・サーフとロバート・カーンで、ノード間でパケット通信を使い資源を共有するインターネットワーキングのプロトコルを記述したものである。このモデルでの中核制御コンポーネントが Transmission Control Program で、ホスト間のコネクション指向のリンクとデータグラムサービスの両方を含んでいた。当初一体だった Transmission Control Program は後にモジュール化されたアーキテクチャに分割され、コネクション指向部分の Transmission Control Protocol とデータグラムサービス部分の Internet Protocol に分けられた。このモデルを一般に TCP/IP と呼ぶが、公式にはインターネット・プロトコル・スイートと呼ぶようになった。

## 機能

TCPはIPネットワーク上で利用され以下の機能を提供する。これらの機能を実現する機構は#プロトコル操作を参照。

### アプリケーション間通信

IPはホスト間通信を可能にするが、そのままだとホストへの全信号を1つのアプリケーションのみが受け取る。TCPはポート機能を提供することで、1ホスト内複数アプリケーションへの通信振り分けを可能にする。すなわちアプリケーション間通信を提供する。

### 到達保証

IPはパケットの送出を可能にするがパケットの到達を保証しない。TCPは到達確認とデータ再送要求を提供することで、送出されたセグメントが宛先へ到達することを保証する。

### ストリーム送信

IPは独立したパケットを送信するため、パケット間の関係性を扱わない。TCPはバイトストリーム入出力機能を提供することで、実用されるIPパケットサイズを超えた、ファイルやデータストリームの送信を可能にする。

#### 順序制御

ネットワークの輻輳や負荷分散、その他ネットワークの予測できない振る舞いにより、IPパケットは遅延し順序がばらばらに届きうる。ストリームをパケットに分割して送出しそれを到着順に再構成した場合、元のストリームを復元できる保証がない。TCPは順序制御を提供することで、パケット群からストリームを再構築できることを保証する。

### コネクション型通信

IPは疎通の確認なくパケットを送信するため、コネクション(仮想回線)の概念を持たない。TCPはソケット対に基づくコネクションを定義することでコネクション型通信を提供する。ストリーム送信や各種制御はコネクション単位で管理されるため、単一のアプリケーションが複数のコネクション(宛先アプリケーション)を独立して扱えるようになる。

#### 流量制御

通信先にとって過剰量のデータがコネクションへ流れ込まないよう、TCPは流量制御を提供する。

#### 輻輳制御

IPネットワークへ無秩序にパケットが送出されるとネットワークの混雑(輻輳)を引き起こす。TCPはそれらの問題を検出し、ネットワークの混雑が起きにくくなるよう制御して他の問題が発生する可能性を低くする。

## 特性

TCPは高速さよりも完全性に重きを置く(高信頼ストリーム配送サービス)。ゆえにパケットの到達が保証され、データを正しく並べかえることができる。その反面、メッセージの順序がばらばらだったり、喪失したメッセージの再送を待ったりすることがあると、秒のオーダーの比較的長い遅延が生じることがある。よってリアルタイム性を要求されないが、1ビットの誤りも許されないファイル転送などの用途が主である。TCPは完全性よりもリアルタイム性を求められるVoIPなどのアプリケーションには向いていない(多少のパケットの欠損には耐えうるため完全性は多少は緩めても良い)。そのような用途には一般に User Datagram Protocol (UDP) 上の Real-time Transport Protocol (RTP) などのプロトコルが推奨される。

## 利用

TCPの利用例としてWorld Wide Web (WWW)、電子メール、File Transfer Protocol、Secure Shell、ファイル共有、一部のストリーミングが挙げられる。

## TCPセグメント構造

TCPセグメント(英: TCP segment)はTCPにおける送受信単位である。

TCPは上位から受け取ったデータを分割し、それにTCPヘッダを付与してTCPセグメントを作成する。TCPセグメントはさらに Internet Protocol (IP) データグラムにカプセル化される。TCPセグメントは「データを相手と交換するためにTCPが使う情報の束」である。

なお、非公式に「TCPパケット」という用語が使われることがあるが、最近の用法では TCP PDU (Protocol Data Unit) は「セグメント」、IP PDU は「データグラム」、データリンク層のPDUは「フレーム」と呼ぶのが一般的である。

プロセスはTCPを呼び出し、データを格納したバッファを引数で渡すことでデータを送信する。TCPはそれらのバッファ内のデータをセグメント群にパッケージし、インターネット・モジュール(例えばIP)を呼び出すことで宛先のTCPへ各セグメントを送信する。

TCPセグメントは、セグメント・ヘッダとデータ部分から成る。TCPヘッダは10の必須フィールドとオプションの拡張フィールドを含む(テーブルではオプション部分をオレンジで示している)。

データ部はヘッダ部の後に続いている。その内容はアプリケーションに渡すべきデータである。データ部の長さはTCPセグメントのヘッダには記されておらず、IPヘッダにあるIPデータグラム長からIPヘッダとTCPヘッダの長さを差し引いて計算できる。

- 送信元ポート(16ビット) – 送信側のポート番号
- 送信先ポート(16ビット) – 受信側のポート番号
- シーケンス番号(32ビット) – 2つの役割がある。
  - SYNフラグがセットされている場合 (1)、初期シーケンス番号である。実際の先頭データバイトのシーケンス番号と対応する確認応答の確認応答番号は、このシーケンス番号に1を加えた値になる。
  - SYNフラグがセットされていない場合 (0)、このセッションにおけるこのパケットの先頭データバイトの累積シーケンス番号である。
- 確認応答番号(32ビット) – ACKフラグがセットされている場合、受信側が期待する次のシーケンス番号を格納している。(もしあれば)それまでの全バイト列の受信を確認済みであることを示す。最初に互いにACKを送る際は、相手側の初期シーケンス番号を確認するだけで、データは含めない。
- ヘッダ長(4ビット) – TCPヘッダのサイズを32ビットワード単位で表す。ヘッダの最小サイズは5ワードで、最大サイズは15ワードである。すなわち、ヘッダ部は20バイトから60バイトまでの大きさであり、21バイトめからの40バイトはオプションとなる。TCPセグメント内の実際にデータが始まる位置を示すことにもなるため、データオフセットとも呼ぶ。
- 予約(3ビット) – 将来の利用のために予約されたビット列であり、常に 0 をセットする。
- フラグあるいは制御ビット列(9ビット) – 9個の1ビットのフラグがある。
  - NS(1ビット) – ECN-nonce 輻輳保護(RFC 3540 でヘッダに追加)
  - CWR(1ビット) – 輻輳制御ウィンドウ縮小 (Congestion Window Reduced)。ECEフラグがセットされたTCPセグメントを受信した際、輻輳制御機構で応答するときにセットする。(RFC 3168 でヘッダに追加)
  - ECE(1ビット) – ECN-Echo を示す。
    - SYNフラグがセットされている場合 (1)、ECNを利用可能であることを示す。
    - SYNフラグがセットされていない場合 (0)、通常送受信時にIPヘッダに Congestion Experienced フラグがセットされたパケットを受信したことを示す。(RFC 3168 でヘッダに追加)
  - URG(1ビット) – 緊急ポインタ・フィールドが有効であることを示す。
  - ACK(1ビット) – 確認応答番号フィールドが有効であることを示す。最初のSYNパケットを除く以降の全パケットでこのフラグをセットする。
  - PSH(1ビット) – プッシュ機能。バッファに蓄えたデータをアプリケーションにプッシュする(押しやる)ことを依頼する。
  - RST(1ビット) – コネクションをリセットする。
  - SYN(1ビット) – シーケンス番号の同期。通信する両方で最初のパケットだけ、このフラグをセットする。他のフラグはこのフラグの値によって意味が変化したり、このフラグの値によって有効/無効が決まる。
  - FIN(1ビット) – 送信終了を示す。
- ウィンドウサイズ(16ビット) – 受信ウィンドウの大きさ。このセグメントの送信側がその時点(確認応答番号フィールドにあるシーケンス番号以降)で受信可能なバイト数を指定する。詳しくはフロー制御の節とウィンドウスケーリングの節を参照。
- チェックサム(16ビット) – ヘッダおよびデータの誤り検出用の16ビットチェックサム。後述の#誤り検出と#チェックサムの計算も参照。
- 緊急ポインタ(16ビット) – URGフラグがセットされている場合、最新の緊急データバイトのシーケンス番号からのオフセットを示す。
- オプション(0から320ビットまで可変で、32ビット単位で変化する) – ヘッダ長フィールドによって、このフィールドの長さが決まる。個々のオプションは、オプション種別(1バイト)、オプション長(1バイト)、オプションデータ(可変)から成る。オプション種別フィールドはオプションの種類を示し、オプションとしては必ず存在するフィールドである。オプションの種類によって扱い方が違い、後続の2つのフィールドの有無も異なる。存在する場合、オプション長フィールドはそのオプションの全長が格納されており、オプションデータ・フィールドにはオプションに関わる値が格納されている。例えば、オプション種別が 0x01 の場合、No-Op オプションであることを意味し、オプション長やオプションデータは存在しない。オプション種別が0の場合、End Of Options オプションであることを意味し、この場合も1バイトだけである。オプション種別が 0x02 の場合、最大セグメントサイズ (MSS) オプションであることを意味し、その後ろに1バイトのMSSフィールド長を指定するフィールドがある(値は 0x04)。この長さはオプションの全長であるため、オプション種別フィールドとオプション長フィールドのぶんも含んでいる。従って、MSS値は通常2バイトで表され、オプション長は4(バイト)となる。例えば、MSS値が 0x05B4 なら、MSSオプション全体の値は (0x02 0x04 0x05B4) となる。
- パディング – TCPヘッダが32ビット境界で終わるようにするために使われる。パディングは常に0である。

一部のオプションはSYNがセットされているときだけ送信される。それらは以下で で示している。各行の先頭は「オプション種別[, オプション長, オプション値](全ビット数)」の形式で示す。

- - 0(8ビット) - オプションリストの終了
  - 1(8ビット) - 何もしない(NOP、パディング)。オプション・フィールドを32ビット境界に揃えるのに使う。
  - 2,4,SS(32ビット) - 最大セグメント長(最大セグメントサイズ を参照)
  - 3,3,S(24ビット) - ウィンドウスケール(詳しくはウィンドウスケーリング参照)
  - 4,2(16ビット) - 選択確認応答が可能。 (選択確認応答を参照)
  - 5,N,BBBB,EEEE,...(可変長、N は 10, 18, 26, 34 のいずれか) - 選択確認応答 (SACK)。最初の2バイトの後に選択確認応答される1から4ブロックのリストを32ビットの開始/終了ポインタで示す。
  - 8,10,TTTT,EEEE(80ビット) - タイムスタンプと前のタイムスタンプのエコー(TCPタイムスタンプを参照)
  - 14,3,S(24ビット) - チェックサム方式変更要求。
  - 15,N,...(可変長) - チェックサム方式が変更されて、そのチェックサムが16ビットより長い場合にこれでチェックサム値を示す。

他のオプションは既に使われていないもの、実験的なもの、標準になっていないものなどである。

## プロトコル操作

TCPプロトコル操作は3つのフェーズに分けられる。コネクションは多段階のハンドシェイクプロセスで正しく確立する必要があり(コネクション確立フェーズ)、その後「データ転送フェーズ」に入る。データ転送が完了したら「コネクション終了フェーズ」で仮想回線を閉じ、確保していたリソースを解放する。

TCPコネクションはオペレーティングシステムがソケットというプログラミングインタフェースを通して管理する。TCPコネクションが存在する間、以下のような状態間で遷移する。

1. LISTENING : サーバの場合、任意のリモートクライアントからのコネクション要求を待ち受ける。
2. SYN-SENT : SYNフラグとACKフラグをセットしたTCPセグメントを相手側が送ってくるのを待つ状態(通常、クライアント側)。
3. SYN-RECEIVED : コネクション確立要求に対して確認応答を返した後、相手側が確認応答を返してくるのを待つ状態(通常、サーバ側)。
4. ESTABLISHED : 相手側とコネクションが確立し、指定ポートでのデータの送受信が可能な状態。
5. FIN-WAIT-1 : FINフラグをセットしたTCPセグメントを先に相手に送り、確認応答が返ってくるのを待つ状態。
6. FIN-WAIT-2 : FINに対する確認応答を受け取り、相手からのFINを待つ状態。
7. CLOSE-WAIT : 相手から先にFINを受け取り、アプリケーションがクローズ可能となるのを待つ状態。クローズ可能になったら相手にFINに対する確認応答を送る。
8. LAST-ACK : FINセグメントを送って、その確認応答を待っている状態。
9. TIME-WAIT : 「FIN-WAIT2」でFINセグメントを受信し、確認応答を返した状態。相手が確認応答を受信完了することを保証するため、十分な時間待つ。RFC 793 では最大4分間この状態でコネクションを保つことができるとされており、これをMSL (maximum segment lifetime) と呼ぶ。
10. CLOSED : コネクションがクローズした状態。

### コネクション確立

コネクションを確立する際、TCPでは3ウェイ・ハンドシェイクを行う。

クライアントがサーバと接続する前、サーバはコネクション可能なようにポートをバインドしておかなければならない。これをパッシブ・オープンと呼ぶ。サーバ側がパッシブ・オープンになっていれば、クライアントはアクティブ・オープンを開始できる。コネクションを確立するための3ウェイ・ハンドシェイクは次のようになる。

1. SYN: クライアントはサーバにSYNを送り、アクティブ・オープンを行う。クライアントはこの際に無作為な値Aを選び、SYNセグメントのシーケンス番号とする。
2. SYN-ACK: それに対してサーバはSYN+ACKを返す。確認応答番号は受信したSYNセグメントのシーケンス番号に1を加えたもの (A + 1) とし、SYN+ACK のシーケンス番号は別の無作為な値 B とする。
3. ACK: クライアントがサーバからの SYN+ACK に対して ACK を返す。その際のシーケンス番号は受信した SYN+ACK の確認応答番号 (A + 1) とし、確認応答番号は SYN+ACK のシーケンス番号に1を加えた値 (B + 1) とする。

この時点でクライアントとサーバ双方がコネクション確立の確認応答を受け取ったことになる。

### リソースの使い方

多くの実装では、テーブルの1エントリを動作中のOSプロセスとのセッションにマッピングする。TCPセグメントにはセッション識別子がないため、通信している双方でクライアントのアドレスとポートでセッションを識別する。パケットを受信すると、TCPソフトウェアはそのテーブルを参照して、対応するプロセスを見つける。

サーバ側でのセッション数はメモリ容量にのみ制限され、コネクション確立要求がくるたびに増えていく。しかし、クライアントはサーバに最初のSYNセグメントを送る前に無作為にポートを選んで確保しなければならない。このポートはコネクションをクローズするまで確保され続け、実質的にクライアントの持つIPアドレス毎の外に出て行くコネクション数を制限している。アプリケーションが不要になったコネクションをクローズしそこねると、空いているポートが足りなくなり、新たなTCPコネクションを確立できなくなる。

また、通信する双方で確認応答を受け取っていない送信済みデータとアプリケーションに渡す前の受信データを格納しておく領域を確保する必要がある。

### データ転送

TCPが提供する機能(#機能を参照)は以下の機構で提供される。

- 順序保証: 受信側でシーケンス番号を使って並べ替え
- 到達保証/再送: 確認応答のないセグメントは再送
- 誤りのないデータ転送: チェックサム
- フロー制御: 受信側は常にどのくらいのデータを受け取れるかを知らせている(スライディングウィンドウで制御している)。受信側のバッファが一杯になると、次の確認応答ではウィンドウサイズを0とするので送信が停止し、バッファ内のデータを処理する余裕ができる。

#### 高信頼転送

TCPは「シーケンス番号」を使ってデータの各バイトを識別する。シーケンス番号は双方のホストが送信するバイト列に先頭から振られる番号であり、それによってデータがどう分割されても、順番が入れ替わっても、転送中に失われても、元のデータを復元できる。ペイロードの各バイトを送るたびにシーケンス番号をインクリメントしなければならない。3ウェイ・ハンドシェイクの最初の2ステップで、双方のホストは初期シーケンス番号 (ISN) をやりとりする。この番号は任意であり、TCPシーケンス番号予測攻撃への防御のために予測不可能な値とすべきである。

TCPは「累積確認応答」方式を採用しており、受信側が確認応答を返すとき、そのセグメントで示されている確認応答番号は、対応するシーケンス番号未満のデータを全て受信済みであることを示している。送信側はペイロードの先頭バイトのシーケンス番号をそのセグメントのシーケンス番号として設定する。受信側は次に受信することを期待しているバイトのシーケンス番号を確認応答番号に設定して確認応答を返す。例えば、送信側が4バイトのペイロードをシーケンス番号 100 で送信する場合、そのペイロードの4バイトのシーケンス番号は順に 100、101、102、103 である。受信側がこのセグメントを受信すると、その確認応答での確認応答番号は 104 となり、次のパケットで送られてくるペイロードの先頭バイトのシーケンス番号となっている。

累積確認応答に加えて、受信側は選択確認応答でさらなる情報を送ることができる。

送信側がネットワーク内でデータが失われたと判断した場合、データの再送を行う。

#### 誤り検出

後述の#チェックサムの計算も参照。
シーケンス番号と確認応答は、パケットの重複、喪失パケットの再送、データの順序通りの並べ替えなどを扱っている。受信したパケットの内容が正しいことを確認するため、TCPにはチェックサムフィールドがある。チェックサムフィールドは設定必須の項目であり省略できない。

TCPチェックサムは、現在の標準から見れば弱い。データリンク層のビット誤り率が高ければ、TCPチェックサムとは別の誤り検出訂正機能が必要である。TCP/IPの下層であるデータリンク層には一般にCRCなどのもっと強力な検査機構があり、TCPチェックサムの弱さを一部補っている(例えば、PPPやイーサネット)。しかし、だからといって16ビットのTCPチェックサムが無駄というわけではない。実際、CRCで保護された通信路でパケットに誤りが残ることはよくあるが、エンドツーエンドの16ビットTCPチェックサムがそういった単純な誤りを捉えている。これはエンドツーエンド原理が機能している例である。

#### フロー制御

TCPはエンドツーエンドのフロー制御プロトコルを使い、送信ペースが受信側にとって速すぎる状態になるのを防いでいる。様々な性能の機器が接続されたネットワークでは、フロー制御は欠かせない機構である。例えば、PCから性能の劣るPDAにデータを送る場合、PDAの性能に合わせて送信ペースを調整する必要がある。

TCPはスライディングウィンドウによるフロー制御を採用している。各TCPセグメントについて、受信側は「ウィンドウサイズ」フィールドで、そのコネクション用のバッファの空き容量に相当する今後受信可能なデータの量(バイト単位)を示す。送信側は確認応答を待ち合わせるまでに、そのフィールドで指定された量までのデータを送り、新たな確認応答でウィンドウサイズ・フィールドを確認してさらに送信できるデータ量を更新する。

受信側がウィンドウサイズを0としたとき、送信側は送信を停止してタイマ (persist timer) を起動する。このタイマは受信側のウィンドウサイズの更新セグメントが喪失してデッドロック状態になるのを防ぐためのものである(受信側がウィンドウサイズを更新しないと送信を再開できないため)。タイマがタイムアウトすると、送信側は小さなパケットを送り、その確認応答で受信側のウィンドウサイズが回復したかを調べる。

受信側での受信データの処理が遅いと、ウィンドウサイズの指定は小さいままとなる。これをSilly Window Syndrome (SWS)と呼び、送信側は1度に数バイトのデータしか送れなくなり、TCPヘッダの方が大きな割合を占めるため転送効率が極端に低下する。そのような状況に陥らないようにするためのウィンドウ・アルゴリズムが RFC 813 (Window and acknowledgement strategy in TCP) に記載されている。

#### 輻輳制御

TCPは高性能を達成し輻輳崩壊(ネットワーク性能が数桁のオーダーで低下する現象)を防ぐため、輻輳制御機構をいくつか備えている。ネットワークに流入させるデータレートを制御し、崩壊のきっかけとなるようなレート未満でデータを送るよう保つ。また、おおまかに最大最小公平なフロー割り当てを目指す。

送信データへの ACK (確認応答)の有無は、送信側でネットワークの状態を推測する材料となる。これをタイマと組み合わせ、データのフローの振る舞いを変化させることができる。これを一般に輻輳制御またはネットワーク輻輳回避などと呼ぶ。

最近のTCP実装では、スロースタート、輻輳回避、TCP高速再送アルゴリズム、ファストリカバリ(en, RFC 5681) という4つの相互にからみあったアルゴリズムを使用している。

スループットをあげるため輻輳しない限界まで送信側はスライディングウィンドウを大きくする必要があるが、ウィンドウサイズを調整する輻輳回避アルゴリズムは長年研究されている。一覧は w:TCP congestion avoidance algorithm を参照。かつては、輻輳するとパケットロスが発生することを利用し、パケットロスをベースにした TCP Reno およびそれを改良した TCP NewReno (RFC 3782) がよく使われていたが、現在では、送信側のスライディングウィンドウにどれだけの時間とどまっていたかを元にしたアルゴリズム (Delay-based Congestion Avoidance) が中心になっており、Microsoft Windows では、Windows Vista 以降は、Compound TCP が採用され、Linux では 2.6.8〜2.6.18 は BIC TCP が、2.6.19 以降は CUBIC TCP が使われている。

さらに送信側には「再送タイムアウト (RTO)」があり、送信してから確認応答が戻るまでの時間であるラウンドトリップタイム (RTT) を推算し、ばらつきも考慮して設定する。このタイマの動作は RFC 2988 で規定されている。RTTの推算には微妙な点がある。例えば、再送パケットのRTTを計算する場合は注意しなければならず、一般にカーンのアルゴリズムやTCPタイムスタンプ(RFC 1323 参照)を使う。個々のRTTの標本から時系列で平均をとり、ジェイコブソンのアルゴリズムを使って Smoothed Round Trip Time (SRTT) を生成する。最終的にSRTT値がRTTの推算に使われる。

TCPを拡張して、喪失を高信頼に扱ったり、誤りを最小化したり、輻輳を制御してより高速化しようという試みが今も行われている。

#### 遅延送信

最大セグメントサイズ以下の小さなパケットをばらばらと送るのは非効率なので、送信を遅延し、それらを1つのTCPパケットにまとめるのが、Nagleアルゴリズムである。同様に、複数のACKを1つにまとめるのが、TCP遅延ACKである。どちらも、送信を遅延させるという点においては同じだが、相互に影響し合い、遅延が増大するという問題があり、詳細はNagleアルゴリズムを参照。

### 最大セグメントサイズ

最大セグメントサイズ (MSS) はバイト単位で指定され、単一のセグメントとして受信可能な最大データ量を示す。性能を最大限発揮するにはIPフラグメンテーションを十分防げる程度に小さくする必要がある。IPフラグメンテーションが行われると、パケット喪失時の再送に時間がかかることになる。一般にコネクション確立時にMSSオプションを使って双方のMSSを通知するので、適切なMSSを決めるにはデータリンク層の Maximum Transmission Unit (MTU) から導出したMSSを通知すればよい。さらに送信側は経路MTU探索を使うことができ、通信相手との間にある経路でMTUが最小の部分を推定し、それを使ってMSSを動的に調整しIPフラグメンテーションを防ぐことができる。

MSS通知は「MSSネゴシエーション」とも呼ばれる。ネゴシエーションというと送信側と受信側が交渉して合意に達するかのように思われるが、実際には異なり、送信する方向によってそれぞれ異なるMSSが設定可能である。これは例えば一方がメモリ容量が小さいため、バッファ領域を大きくとれない場合などに起きる(発見したパスMTUより小さいこともありうる)。

### 選択確認応答

もともとのTCPプロトコルで採用されている累積確認応答方式を使うと、パケットを喪失したときに非効率になる可能性がある。例えば、10,000バイトのデータを10個のTCPセグメントで送信し、その最初のパケットが喪失したとする。もともとの累積確認応答プロトコルでは、受信側は1,000から9,999までのバイトは受信成功、ただし0から999までのバイトを含む先頭パケットの受信に失敗したという風に伝えることができないので、送信側は10,000バイト全体を再送しなければならない。

このため RFC 2018 で「選択確認応答 (SACK)」オプションが追加された。これは、通常の累積確認応答とは別に、受信側が不連続なブロックを正しく受信したという確認応答を返せるようにしたものである。選択確認応答にはオプション部分にいくつかのSACKブロックを指定し、SACKブロックには正しく受信できた連続な範囲のシーケンス番号の開始点と終了点を指定する。例えば、先ほどの例では 1000 と 9999 のシーケンス番号をSACKオプションで示せばよい。すると送信側は 0 から 999 までのバイトを含む最初のパケットだけを再送する。

SACKオプションの拡張として RFC 2883 で定義されたデュプリケートSACK (D-SACK) オプションがある。パケットの順序がばらばらになると、送信側への確認応答も順序どおりにならないため送信側でパケット喪失と勘違いし、喪失したと思われるパケットを再送することがあり、同時にネットワーク輻輳を防ぐため送信ペースを落とす。このとき、受信側が D-SACK オプションで再送パケットが重複していることを通知すれば、遅くなっていた送信ペースを元に戻すことができる。

SACKオプションは必須ではなく、両者がサポートしている場合だけ使われる。これはコネクション確立時に調整される。SACKオプションは主なTCPスタックでサポートされており、広く使われている。選択確認応答は Stream Control Transmission Protocol (SCTP) でも使われている。

### ウィンドウスケーリング

広帯域ネットワークをより効率的に使うには、TCPウィンドウのサイズを大きくする必要がある。TCPヘッダのウィンドウサイズのフィールドは16ビットで、2バイトから65,535バイトまでしか設定できない。

ウィンドウサイズ・フィールドは拡張できないので、スケールファクタが導入されている。RFC 7323 で定義されているウィンドウスケール・オプションを使えば、最大ウィンドウサイズを 65,535 バイトから 1 ギガバイトに拡張できる。ウィンドウサイズのスケールアップはTCPのチューニング (en) に必須の要素である。

ウィンドウスケール・オプションは3ウェイ・ハンドシェイクの際にしか使われない。ウィンドウスケール・オプションのオプション値は、16ビットのウィンドウサイズ・フィールドの値を左にシフトするビット数を表している。ウィンドウスケールの値は0(シフトしない)から14まで指定でき、通信の双方向で別々に設定できる。どちらの方向もSYNセグメントのオプションとして通知する。

一部のルーターやファイアウォールは、このスケールファクタを転送時に書き換えることがある。すると送信側と受信側でウィンドウサイズの認識が異なることになり、トラフィックが不安定になって、非常に低速になることがある。

### TCPタイムスタンプ

TCPタイムスタンプは RFC 1323 で定義されており、パケット送出の順序をTCPレベルで知ることが出来る。TCPタイムスタンプはシステムクロックに合わせているわけではなく、無作為な値から開始する。多くのOSはこのタイムスタンプ値をミリ秒単位でインクリメントする。ただし、RFCは単に時間経過に比例して増加すべきだとしているだけである。

タイムスタンプのフィールドは2つある。

- 4バイトの送信側タイムスタンプ値(自分のタイムスタンプ)
- 4バイトの応答タイムスタンプ値(相手から直近に受け取ったタイムスタンプ値)

TCPタイムスタンプは PAWS (Protection Against Wrapped Sequences) と呼ばれるアルゴリズム(RFC 1323 参照)で利用する。PAWSは、2の32乗まであるシーケンス番号が一周してしまう場合に使われる。シーケンス番号はデータバイト毎に振られるので、最大4ギガバイトしか表せないが、最近の高速ネットワークでは1分以内に一周する可能性があり、再送が必要になったとき、現在の周回なのか前の周回なのかを識別するのにタイムスタンプを使う。

RFC 1323 の2.2節では、ウィンドウサイズは1ギガバイトまでとされているため、多くの実装でスケールオプションの最大値を14までとしている。

また、Eifel detection アルゴリズム (RFC 3522) でもTCPタイムスタンプを使っており、再送の原因がパケット喪失なのか順序がばらばらになったせいなのかを判断する。

### 帯域外データ

ストリームが完了するのを待たずに、キューイングされたストリームに割り込むことができる。この場合、緊急 (urgent) と指定したデータを使う。それによって受信側プログラムが緊急データをすぐさま処理すべきであることを知らせる。その処理が終了すると、もとのストリーム・キューの処理を再開する。例えば、リモートログインのセッションにTCPを使っているとき、ユーザーが実行中のプログラムをアボートさせるキーシーケンスを送るときなどに使われる。プログラムが暴走したときなど、そのプログラムの出力を待っているのではなく、強引にアボートさせたいときに必須となる。

帯域外データの概念は現在のインターネット向けの設計ではない。緊急ポインタは相手ホストでの処理を変えるものであって、ネットワーク上の処理は何も変わらない。緊急ポインタのあるセグメントがリモートホストに到着したとき、若干異なる2つのプロトコルの解釈があり、単一バイトの帯域外データしか信頼できないという状況になっている。そのため滅多に使われず、実装も貧弱になる傾向がある
。

### 強制的データ送出

通常、TCPは送信すべきデータが最大セグメントサイズ (MSS) まで溜まるか、200ミリ秒経過するまで待つ(Nagleアルゴリズムで小さいメッセージを単一パケットにまとめようとするため)。これは例えばファイル転送のような一定の送信が要求される場合に問題となることがある。例えば、送信ブロックが一般的な4KBで、MSSも一般的な1460バイトだとする。すると1ブロックが3セグメントで送信され、最後の1セグメントはMSSに満たないことになる。すると、2パケットまでは約1.2ミリ秒で送信され、1176バイトの3パケット目は197ミリ秒待ってから送信ということになる。

Telnetの場合、ユーザーがキーを押下するたびに通信先からエコーバックされて画面に文字が表示される。すると、1文字押下するたびに200ミリ秒待たされることになり、非常にストレスになる。

この場合、ソケットのオプション `TCP_NODELAY` を使ってデフォルトの200ミリ秒の送信遅延を変更することができる。

RFCには `PSH` フラグを使って「受信側TCPスタックでそのデータを即座にアプリケーションに送る」という機能が定義されている。しかしソケットにはこれを制御するインタフェースがなく、プロトコルスタックの実装に任されている。

### コネクション終了

コネクション終了フェーズは多くの場合「4ウェイ・ハンドシェイク」を使い、コネクションの双方がそれぞれ独立に終了できる。一方がコネクションを終了したい場合、FINセグメントを送信し、相手がそのACKを返す。相手も同様にFINを送ってACKを受信することでコネクションを終了する。両方のFIN/ACK交換が済むと、最後にACKを送った側(先にFINを送った側)がタイマを設定してタイムアウトするまで当該ポートを別のコネクションに再利用できないようにする。これによって配送が遅れていたパケットが新たなコネクションで受信されて混乱するのを防ぐ。

コネクションは「ハーフオープン」という状態にもでき、一方だけ終了していても、もう一方はデータを送り続けることができる。終了した側はもう一方が終了するまで受信可能の状態を継続する。

コネクション終了を3ウェイ・ハンドシェイクで行うこともでき、ホストAのFIN送信に対してホストBが FIN+ACK で応答し、ホストAがACK応答する。実際にはこれが最も一般的である。

両方から同時にFINセグメントを送りあい、双方がACKを返すということもありうる。FIN/ACKシーケンスが並行して行われるため、2ウェイ・ハンドシェイクと呼ぶこともできる。

## 脆弱性

TCPは様々な方法で攻撃される可能性がある。TCPの完全なセキュリティアセスメントの結果は、認識されていた問題の考えうる対策と共に2009年に公表され、その後もIETF内で進められている。

### DoS攻撃

IPスプーフィングを使い、悪意を持って作ったSYNパケットを繰り返し送信することで、偽の接続に対処するために対象サーバの多大な量のリソースを消費させることができる。これを SYN flood 攻撃と呼ぶ。この問題への対策として提案された方法として、SYN cookies や暗号的パズルがある。Sockstress も類似の攻撃法だが、システムのリソース管理によって効果を和らげることができる。オンラインマガジン Phrack 66号では、TCPの Persist Timer に存在する脆弱性を利用した改良型DoS攻撃の分析を行っている。

### コネクション乗っ取り

TCPセッションを盗聴できパケットをリダイレクトできる攻撃者は、TCPコネクションを乗っ取ることができる。その場合、攻撃者は進行中の通信からシーケンス番号を読み取り、ストリームにおける次のセグメントを装った偽のセグメントを作る。そのような簡単な乗っ取りで、通信中の一方に1つのパケットを誤って受け取らせることができる。受け取ったホストが余分なセグメントへの確認応答をコネクションのもう一方に返すと、同期が失われる。ARPまたはルーティング攻撃を組合わせることで、乗っ取ったTCPコネクションの制御を完全に奪うことができる。

RFC 1948 が登場する以前は異なるIPアドレスを真似ることは難しくなく、初期シーケンス番号は容易に推測可能だった。そのため攻撃者はARP/ルーティング攻撃を併用することなく、適当な一連のパケットを受信者に送信し、異なるIPアドレスからのものだと信じさせることができた。その際、偽装したIPアドレスの本来のホストがダウンしていれば十分であり、Dos攻撃でそのホストをダウンさせればよかった。以上のような理由から、初期シーケンス番号のランダムな選択が行われるようになった。

## TCPポート

TCPにおけるポートは「ホスト内アドレス」である。

単一のホストでは複数のプロセス(≒ アプリケーション)が動作している。TCPはホスト内の"部屋番号"に相当するポート、ポートとインターネットアドレスの組み合わせであるソケット(英: socket)を定義し、このソケット-ソケット間で通信を行う。単一ホスト内に複数ポートが存在することで、単一のホスト上で複数のプロセスが同時にTCP通信できる(多重化)。各ポートはポート番号として知られるポート識別子が設定されており、プロセスとポートを結びつけることでそのプロセスへの通信を可能にする。ポート番号は16ビット符号なし整数 (0-65535) の範囲をもつ。

コネクション(英: connection)は一組のソケットで識別される論理的通信路である。すなわち「ソケット `172.16.0.0:1024` とソケット `192.168.0.0:80` を繋ぐ論理的通信路」といった形で識別されるものがコネクションである。受信したTCPセグメントは特定のコネクションに属すると識別される。

ポート番号は大きく3つに分類されており、ウェルノウン (well-known)、レジスタード (registered)、ダイナミック/プライベート (dynamic/private) がある。ウェルノウンポート番号は Internet Assigned Numbers Authority (IANA) が割り当てを行っており、主にシステムレベルや重要なプロセスで使われている。サーバとして動作する有名なアプリケーションは、それらのポートを使いコネクション確立要求を待ち受けているのが一般的である。例えば、FTP (20, 21)、SSH (22)、TELNET (23)、SMTP (25)、HTTP (80) などがある。レジスタードポート番号は一般にエンドユーザー用アプリケーションが送信元のエフェメラルポートとしてサーバに接続するのに使うが、サードパーティが登録した名前を持ったサービスの識別にも使われている。ダイナミック/プライベートポート番号もエンドユーザーのアプリケーションで使えるが、一般にそのような使い方は少ない。ダイナミック/プライベートポート番号は、それを使っている特定のTCPコネクションでしか意味を持たない。

## 発展

TCPは複雑なプロトコルである。長年重大な改良が実施されたり提案されたりしてきたが、1974年に RFC 675 で最初の仕様が登場し、1981年9月に RFC 793 でバージョン4が登場して以来、基本的動作はほとんど変わっていない。RFC 1122 (Host Requirements for Internet Hosts) はTCPプロトコルの実装時の要求仕様を何点か明確にし、近年最も重要なTCP関連のRFCの1つである RFC 2581 (TCP Congestion Control) は輻輳を防ぐための新たなアルゴリズムを提示している。2001年、RFC 3168 で明示的輻輳通知 (ECN) が提示された。2022年8月には、RFC 675とそれ以降の拡張をまとめたRFC 9293が公開されている。

当初のTCP輻輳回避アルゴリズムは "TCP Tahoe" と呼ばれているが、代替アルゴリズムも多数提案されている(TCP Reno、TCP Vegas、FAST TCP、TCP New Reno、TCP Hybla など)。

マルチパスTCP (MPTCP)はIETFで近年進行中の改良で、リソース利用効率と冗長性を高めるためにTCPコネクションを複数経路にする試みである。Multipath TCP による冗長性は、無線ネットワークでリソースの統計多重化を可能にし、TCPのスループットを劇的に高める。マルチパスTCP はデータセンター環境にも性能面の利点をもたらす。Multipath TCP のリファレンス実装がLinuxカーネル向けに開発されている。

TCP Cookie Transactions (TCPCT) は2009年12月に提案された拡張で、サーバをDoS攻撃から守ることを意図している。SYN cookies とは異なり、TCPCTはウィンドウスケーリングなどの他のTCP拡張と共存できる。TCPCTは、短命なTCPコネクションをサーバが多数処理しなければならないDNSSECでの必要から設計された。

tcpcrypt は2010年7月に提案された拡張で、TCP自身で直接暗号化するものである。透過的に動作可能なように設計されており、設定変更は不要である。TLS (SSL) とは異なり、tcpcrypt 自体は認証機構を持たないが、そのための簡単なプリミティブをアプリケーションに提供する。2010年現在、IETF による最初のドラフトが公表されており、いくつかの主要プラットフォームでの実装が存在する。その後、2019年には実験的(Experimental)のステータスのRFC 8547およびRFC 8548が公開されている。

## 無線ネットワークでのTCP

TCPは有線ネットワーク向けに最適化されてきた。一般にパケット喪失はネットワーク輻輳の結果と判断され、予防のために輻輳ウィンドウサイズが大幅に縮小される。しかし無線の場合、減衰、影に入る、ハンドオーバーなどの無線特有の原因でパケットを喪失することがあり、輻輳が原因とは限らない。無線パケット喪失による(誤った)輻輳ウィンドウサイズ縮小後、輻輳回避のための保守的なウィンドウサイズの縮小も行われる可能性がある。これにより無線リンクの効率が低下する。このような問題への対策が広く研究されている。提案されている対策としては、エンドツーエンド型の対策(クライアントとサーバの修正が必要)とリンク層の対策(RLPなど)とプロキシを使った対策(端点以外のネットワークの何らかの変更が必要)がある。

## デバッグ

LANアナライザはネットワークリンク上のTCPトラフィックをインターセプトできる機器で、リンク上を通るパケットの内容を表示でき、ネットワーク、プロトコルスタック、TCPを使っているアプリケーションのデバッグに有効である。一部の実装ではソケットの setsockopt() で SO\_DEBUG オプションを指定でき、全パケット、TCPのステータス、ソケット上のイベントなどを出力でき、デバッグに有効である。他に、netstatもデバッグに使われる。

## 代替となる選択肢

TCPの使用で明らかになった主要な問題は、ヘッドオブラインブロッキングとマルチホーミングの欠如による、コールシグナリングに許容できない遅延の発生である。さらに、TCPの多くの用途は適切とはいえない。(少なくとも通常の実装での)最大の問題は、喪失パケットの再送を受信してからでないと受信済みの後続のパケットをアプリケーションで利用できない点である。特にストリーミング、オンラインゲーム、VoIPなどのリアルタイム型アプリケーションで重要な問題であり、データの順序性よりも適時性が重要である。

歴史的・性能的理由により、ストレージエリアネットワーク (SAN) はTCP/IPよりもファイバーチャネルプロトコルを採用することが多い。

組み込みシステムでも、ネットワークブートや多数のクライアントからの簡単な要求を受け付けるサーバ(例えばDNSサーバ)でTCPの複雑さが問題となる可能性がある。さらには、STUNなどの NAT traversal 技法では相対的に複雑なTCPを使わずに、遥かに単純な方法で実現している。

一般にTCPが適さない場合は User Datagram Protocol (UDP) を使用する。UDPはTCPと同様にアプリケーション多重化とチェックサム機構を提供するが、ストリームの構築や再送を行わず、アプリケーションにそういった機能の実装を任せている。

SCTPは、TCPとよく似たストリーム指向のサービスを提供するプロトコルである。TCPより新しくさらに複雑であり、広く普及したとは言い難い。しかし、信頼性とリアルタイム性を同時に必要とする用途を意図して設計されている。

TCPは広帯域環境でも問題を抱えている。TCP輻輳回避アルゴリズムは、送信者が事前にわからない場当たり的な環境ではうまく機能するが、通信パターンが予測可能な環境では Asynchronous Transfer Mode (ATM) のようなタイミングに基づくプロトコルの方がオーバーヘッドが小さく、うまく機能する。

## チェックサムの計算

### IPv4でのTCPチェックサム

IPv4上のTCPの場合、チェックサム計算法は RFC 793 で定義されている。

チェックサム・フィールドは、ヘッダおよびテキストの全16ビットワードの1の補数の総和の1の補数の下位16ビットである。オクテット数が奇数の場合、最後のオクテットの右にゼロの列をパディングして16ビットワードにしてからチェックサムを計算する。このパディングはセグメントの一部として送信することはない。チェックサム計算時、チェックサム・フィールド自体はゼロとして計算する。

言い換えれば、正しくパディングした後、全16ビットワードを1の補数表現で加算していく。そして総和をビット毎に反転してチェックサム・フィールドに挿入する。チェックサム計算時には、IPv4パケットヘッダを真似た擬似ヘッダも含めて行うことになっている。擬似ヘッダを含めたチェックサム計算範囲を以下に示す。

上のピンクの部分はIPv4ヘッダの一部である。プロトコル番号はTCPでは 6 である。パケット長はTCPヘッダとデータの合計の長さである。

### IPv6でのTCPチェックサム

IPv6上のTCPの場合、チェックサム計算法は RFC 2460 で示すように変更されている。

チェックサム計算にIPヘッダのアドレスを含める上位層のプロトコルでは、IPv4の32ビットアドレスの代わりにIPv6の128ビットのアドレスを使うよう変更しなければならない。

チェックサム計算で使うIPv6ヘッダを真似た擬似ヘッダは次のようになる。

- 送信元IPアドレス - IPv6ヘッダにあるもの
- あて先IPアドレス - 最終送信先。ルーティングヘッダがある場合、TCPは最終のあて先アドレスを使用する。発信元ノードでは、そのアドレスはルーティングヘッダの最後の要素にあり、受信側ではIPv6ヘッダの着信アドレスフィールドにある。
- パケット長 - TCPのヘッダとデータをあわせた全長
- 次のヘッダ - TCPのプロトコル番号

### チェックサム・オフロード

多くのTCP/IPスタック実装では、ネットワークカードによる自動チェックサム計算を補助的に使うオプションを用意している。これによりCPUサイクルをチェックサム計算に費やすコストを低減でき、ネットワーク性能を向上させることができる。

なお、送信時にチェックサム計算をネットワークカードに任せていると、LANアナライザがチェックサムエラーを検出することがある。

## 学習用参考書

- 小口正人:「コンピュータネットワーク入門―TCP/IPプロトコル群とセキュリティ」、サイエンス社、ISBN 978-4781911663 (2007年5月)。
- 村公保:「基礎からわかるTCP/IP ネットワークコンピューティング入門 第3版」、オーム社、ISBN 978-4274050732(2015年2月26日)。
- 安永遼真、中山悠、丸田一輝:「TCP技術入門 ――進化を続ける基本プロトコル」、技術評論社、ISBN 978-4297106232(2019年7月6日)。
- みやたひろし:「図解入門TCP/IP 仕組み・動作が見てわかる」、SBクリエイティブ、ISBN 978-4815604974(2020年12月22日)。