Transmission Control Protocol
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タイムスタンプは 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内で進められている。
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はホスト内の"部屋番号"に相当するポート、ポートとインターネットアドレスの組み合わせであるソケット(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) が割り当てを行っており、主にシステムレベルや重要なプロセスで使われている。サーバとして動作する有名なアプリケーションは、それらのポートを使いコネクション確立要求を待ち受けているのが一般的である。例えば、 (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は有線ネットワーク向けに最適化されてきた。一般にパケット喪失はネットワーク輻輳の結果と判断され、予防のために輻輳ウィンドウサイズが大幅に縮小される。しかし無線の場合、減衰、影に入る、ハンドオーバーなどの無線特有の原因でパケットを喪失することがあり、輻輳が原因とは限らない。無線パケット喪失による(誤った)輻輳ウィンドウサイズ縮小後、輻輳回避のための保守的なウィンドウサイズの縮小も行われる可能性がある。これにより無線リンクの効率が低下する。このような問題への対策が広く研究されている。提案されている対策としては、エンドツーエンド型の対策(クライアントとサーバの修正が必要)とリンク層の対策(RLPなど)とプロキシを使った対策(端点以外のネットワークの何らかの変更が必要)がある。
LANアナライザはネットワークリンク上のTCPトラフィックをインターセプトできる機器で、リンク上を通るパケットの内容を表示でき、ネットワーク、プロトコルスタック、TCPを使っているアプリケーションのデバッグに有効である。一部の実装ではソケットの <tt>setsockopt()</tt> で <tt>SO_DEBUG</tt> オプションを指定でき、全パケット、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の場合、チェックサム計算法は RFC 793 で定義されている。
チェックサム・フィールドは、ヘッダおよびテキストの全16ビットワードの1の補数の総和の1の補数の下位16ビットである。オクテット数が奇数の場合、最後のオクテットの右にゼロの列をパディングして16ビットワードにしてからチェックサムを計算する。このパディングはセグメントの一部として送信することはない。チェックサム計算時、チェックサム・フィールド自体はゼロとして計算する。
言い換えれば、正しくパディングした後、全16ビットワードを1の補数表現で加算していく。そして総和をビット毎に反転してチェックサム・フィールドに挿入する。チェックサム計算時には、IPv4パケットヘッダを真似た擬似ヘッダも含めて行うことになっている。擬似ヘッダを含めたチェックサム計算範囲を以下に示す。
上のピンクの部分はIPv4ヘッダの一部である。プロトコル番号はTCPでは 6 である。パケット長は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日)。