Transmission Control Protocol
Transmission Control Protocol(トランスミッション コントロール プロトコル、TCP)はIPネットワーク上のアプリ間・コネクション型・高信頼性・ストリーム指向の通信プロトコルである。伝送制御プロトコルとも呼ばれる。
TCPは通信プロトコルである。TCPではポート、ソケット、コネクションの概念が導入され、IPネットワークのホスト上にあるアプリケーション同士がコネクションを通じて通信する。確立されたコネクションはTCPが定める制御機構によって到着が保証され、破壊が検出され、流量や輻輳が制御されており、これを通信路としてバイトストリームが伝達される。
TCPを用いることでインターネットにおける到達保証付きアプリケーション間メッセージ通信が可能になるため、ファイル転送、電子メール、World Wide Web、リモート管理など様々なインターネット・アプリケーションで利用される。上位プロトコルにはHTTP、、Telnet、SSHなどがある。
TCPはインターネット・プロトコル・スイートを構成し、Internet Protocolの上位プロトコルとして使われる。またOSI参照モデルに当てはめるならトランスポート層にあたる。その仕様はIETFにより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は高速さよりも正確さに最適化されている(高信頼ストリーム配送サービス)。ゆえにパケットの到達が保証され、データを正しく並べかえることができる。その反面、メッセージの順序がばらばらだったり、喪失したメッセージの再送を待ったりすることがあると、秒のオーダーの比較的長い遅延が生じることがある。これはリアルタイム性を求められるVoIPなどのアプリケーションには向いていない。そのような用途には一般に User Datagram Protocol (UDP) 上の Real-time Transport Protocol (RTP) などのプロトコルが推奨される。
TCPの利用例としてWorld Wide Web (WWW)、電子メール、、Secure Shell、、一部のストリーミングが挙げられる。
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つの役割がある。
<tt>SYN</tt>フラグがセットされている場合 (1)、初期シーケンス番号である。実際の先頭データバイトのシーケンス番号と対応する確認応答の確認応答番号は、このシーケンス番号に1を加えた値になる。
<tt>SYN</tt>フラグがセットされていない場合 (0)、このセッションにおけるこのパケットの先頭データバイトの累積シーケンス番号である。
確認応答番号(32ビット) – <tt>ACK</tt>フラグがセットされている場合、受信側が期待する次のシーケンス番号を格納している。(もしあれば)それまでの全バイト列の受信を確認済みであることを示す。最初に互いに<tt>ACK</tt>を送る際は、相手側の初期シーケンス番号を確認するだけで、データは含めない。
ヘッダ長(4ビット) – TCPヘッダのサイズを32ビットワード単位で表す。ヘッダの最小サイズは5ワードで、最大サイズは15ワードである。すなわち、ヘッダ部は20バイトから60バイトまでの大きさであり、21バイトめからの40バイトはオプションとなる。TCPセグメント内の実際にデータが始まる位置を示すことにもなるため、データオフセットとも呼ぶ。
予約(3ビット) – 将来の利用のために予約されたビット列であり、常に 0 をセットする。
フラグあるいは制御ビット列(9ビット) – 9個の1ビットのフラグがある。
<tt>NS</tt>(1ビット) – ECN-nonce 輻輳保護(RFC 3540 でヘッダに追加)
<tt>CWR</tt>(1ビット) – 輻輳制御ウィンドウ縮小 (Congestion Window Reduced)。<tt>ECE</tt>フラグがセットされたTCPセグメントを受信した際、輻輳制御機構で応答するときにセットする。(RFC 3168 でヘッダに追加)
<tt>ECE</tt>(1ビット) – ECN-Echo を示す。
<tt>SYN</tt>フラグがセットされている場合 (1)、明示的輻輳通知を利用可能であることを示す。
<tt>SYN</tt>フラグがセットされていない場合 (0)、通常送受信時にIPヘッダに Congestion Experienced フラグがセットされたパケットを受信したことを示す。(RFC 3168 でヘッダに追加)
<tt>URG</tt>(1ビット) – 緊急ポインタ・フィールドが有効であることを示す。
<tt>ACK</tt>(1ビット) – 確認応答番号フィールドが有効であることを示す。最初の<tt>SYN</tt>パケットを除く以降の全パケットでこのフラグをセットする。
<tt>PSH</tt>(1ビット) – プッシュ機能。バッファに蓄えたデータをアプリケーションにプッシュする(押しやる)ことを依頼する。
<tt>RST</tt>(1ビット) – コネクションをリセットする。
<tt>SYN</tt>(1ビット) – シーケンス番号の同期。通信する両方で最初のパケットだけ、このフラグをセットする。他のフラグはこのフラグの値によって意味が変化したり、このフラグの値によって有効/無効が決まる。
<tt>FIN</tt>(1ビット) – 送信終了を示す。
ウィンドウサイズ(16ビット) – 受信ウィンドウの大きさ。このセグメントの送信側がその時点(確認応答番号フィールドにあるシーケンス番号以降)で受信可能なバイト数を指定する。詳しくはフロー制御の節とウィンドウスケーリングの節を参照。
チェックサム(16ビット) – ヘッダおよびデータの誤り検出用の16ビットチェックサム。後述の#誤り検出と#チェックサムの計算も参照。
緊急ポインタ(16ビット) – <tt>URG</tt>フラグがセットされている場合、最新の緊急データバイトのシーケンス番号からのオフセットを示す。
オプション(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である。
一部のオプションは<tt>SYN</tt>がセットされているときだけ送信される。それらは以下で <tt>[SYN]</tt> で示している。各行の先頭は「オプション種別[, オプション長, オプション値](全ビット数)」の形式で示す。
0(8ビット) - オプションリストの終了
1(8ビット) - 何もしない(NOP、パディング)。オプション・フィールドを32ビット境界に揃えるのに使う。
2,4,SS(32ビット) - 最大セグメント長(最大セグメントサイズ を参照) <tt>[SYN]</tt>
3,3,S(24ビット) - ウィンドウスケール(詳しくはウィンドウスケーリング参照)<tt>[SYN]</tt>
4,2(16ビット) - 選択確認応答が可能。<tt>[SYN]</tt> (選択確認応答を参照)
5,N,BBBB,EEEE,...(可変長、N は 10, 18, 26, 34 のいずれか) - 選択確認応答 (SACK)。最初の2バイトの後に選択確認応答される1から4ブロックのリストを32ビットの開始/終了ポインタで示す。
8,10,TTTT,EEEE(80ビット) - タイムスタンプと前のタイムスタンプのエコー(TCPタイムスタンプを参照)
14,3,S(24ビット) - チェックサム方式変更要求。<tt>[SYN]</tt>
15,N,...(可変長) - チェックサム方式が変更されて、そのチェックサムが16ビットより長い場合にこれでチェックサム値を示す。
他のオプションは既に使われていないもの、実験的なもの、標準になっていないものなどである。
TCPプロトコル操作は3つのフェーズに分けられる。コネクションは多段階のハンドシェイクプロセスで正しく確立する必要があり(コネクション確立フェーズ)、その後「データ転送フェーズ」に入る。データ転送が完了したら「コネクション終了フェーズ」で仮想回線を閉じ、確保していたリソースを解放する。
TCPコネクションはオペレーティングシステムがソケットというプログラミングインタフェースを通して管理する。TCPコネクションが存在する間、以下のような状態間で遷移する。
LISTENING : サーバの場合、任意のリモートクライアントからのコネクション要求を待ち受ける。
SYN-SENT : SYNフラグとACKフラグをセットしたTCPセグメントを相手側が送ってくるのを待つ状態(通常、クライアント側)。
SYN-RECEIVED : コネクション確立要求に対して確認応答を返した後、相手側が確認応答を返してくるのを待つ状態(通常、サーバ側)。
ESTABLISHED : 相手側とコネクションが確立し、指定ポートでのデータの送受信が可能な状態。
FIN-WAIT-1 : FINフラグをセットしたTCPセグメントを先に相手に送り、確認応答が返ってくるのを待つ状態。
FIN-WAIT-2 : FINに対する確認応答を受け取り、相手からのFINを待つ状態。
CLOSE-WAIT : 相手から先にFINを受け取り、アプリケーションがクローズ可能となるのを待つ状態。クローズ可能になったら相手にFINに対する確認応答を送る。
LAST-ACK : FINセグメントを送って、その確認応答を待っている状態。
TIME-WAIT : 「FIN-WAIT2」でFINセグメントを受信し、確認応答を返した状態。相手が確認応答を受信完了することを保証するため、十分な時間待つ。RFC 793 では最大4分間この状態でコネクションを保つことができるとされており、これをMSL (maximum segment lifetime) と呼ぶ。
CLOSED : コネクションがクローズした状態。
コネクションを確立する際、TCPでは3ウェイ・ハンドシェイクを行う。
クライアントがサーバと接続する前、サーバはコネクション可能なようにポートをバインドしておかなければならない。これをパッシブ・オープンと呼ぶ。サーバ側がパッシブ・オープンになっていれば、クライアントはアクティブ・オープンを開始できる。コネクションを確立するための3ウェイ・ハンドシェイクは次のようになる。
SYN: クライアントはサーバにSYNを送り、アクティブ・オープンを行う。クライアントはこの際に無作為な値Aを選び、SYNセグメントのシーケンス番号とする。
SYN-ACK: それに対してサーバはSYN+ACKを返す。確認応答番号は受信したSYNセグメントのシーケンス番号に1を加えたもの (A + 1) とし、SYN+ACK のシーケンス番号は別の無作為な値 B とする。
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)、TCP輻輳回避アルゴリズム、TCP高速再送アルゴリズム、ファストリカバリ(en, RFC 5681) という4つの相互にからみあったアルゴリズムを使用している。
スループットをあげるため輻輳しない限界まで送信側はスライディングウィンドウを大きくする必要があるが、ウィンドウサイズを調整する輻輳回避アルゴリズムは長年研究されている。一覧は 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フラグメンテーションを防ぐことができる。