TCPプロトコル

TCPは、コネクション型のプロトコルです。
コネクションとは「確立された通信路」というイメージで、コネクションが確立できれば相手と通信できる状態にあるということになります。
例えば、その通信路に「あ」と伝えれば、相手に「あ」と伝わり、逆に相手側から「い」と伝えられれば、自分に「い」と伝わるということです。

TCPはこの通信路を使い、データを送受信します。また、転送の途中で消失したパケットを送りなおしたり、通信量を調整したり、パケットの順番を整えるといったことも行います。

3ウェイハンドシェーク

TCPで通信を開始する際、まず最初にコネクションを確立するため、3ウェイハンドシェークという作業を行います。

受信側(サーバープログラム)はまず、OSに対して「このボート番号で待ち受けます」と宣言します。
この宣言によりLISTEN状態となり、以後、そのポート番号宛の正常な通信はそのサーバープログラムに渡されます。

通信の準備が整ったので、送信側から3ウェイハンドシェークを開始します。

  1. TCPは「SYN」というフラグが立ったパケットを送ります。このSYNというフラグは、「接続を確立したい(synchronize)」という意味です。
  2. 受信側はSYNパケットを受け取ると、「SYN」と「ACK」のフラグが立ったパケットを送信元に送ります。
  3. 送信元はSYN ACKパケットを受け取ると、「ACK」のフラグが立ったパケットを送り返します。ACKは「了解」という意味です。

3ウェイハンドシェーク

このように、3ウェイハンドシェークによって送信元と宛先のアプリケーションが通信状態になることを、「コネクションの確立」といいます。コネクションの確立後、アプリケーション層プロトコルのパケットの送受信を開始します。

TCPヘッダの構造

TCPヘッダの構造は以下のようになっています。

TCPヘッダの構造

フィールド 説明
送信元ポート番号と宛先ポート番号 16ビットのフィールドで、0~65535の範囲の値が使用される。送信元と宛先のアプリケーションを識別するための番号。
シーケンス番号 32ビットのフィールドで、TCPセグメントのデータ部が、元データの何バイト目の位置のものかを表すための番号。※1
確認応答番号 32ビットのフィールドで、受信側が「次に受信することを想定しているシーケンス番号」を送信側に伝えるために使用する。「ACK番号」とも言う。
データオフセット TCPヘッダのサイズは可変長のため、受信側にヘッダとデータの境目を正しく伝えるために、データフィールドの開始位置を表す、4ビットのフィールド。
予約 6ビットのフィールドで、将来のために予約されている。現在は使用していないため「0」がセットされる。
制御ビット 1ビットのフィールドが6つある。※2
ウインドウ 16ビットのフィールドで、受信バッファの空きサイズを、受信側が送信側に通知するために使用される。送信側では、通知された値に従って、送信パケット数を調整する。
チェックサム 16ビットのフィールドで、エラーチェックのためのフィールド
緊急ポインタ 16ビットのフィールドで、データ部に緊急データが含まれているときに、緊急データの位置を受信側に伝えるために使用される。
オプション 必要に応じてオプションを追加できる任意のフィールド
パディング TCPヘッダは32ビット(4バイト)の倍数と決められているため、必要に応じてTCPヘッダのサイズを調整するために使用される。(パディングとは「詰め物」という意味がある。)※3

※1 TCPは、上位層のプロトコルから渡されたデータサイズが大きい場合、データを適切なサイズに分割する。このとき、受信側で元のデータを再構築するために、シーケンス番号が必要になる。

※2 制御ビットはフラグ形式(1ならON、0ならOFF)になっており、下表のような意味を示す。

制御ビット 説明
URG(Urgent Pointer) 1の場合は緊急ポインタフィールドが有効であることを示す。0の場合は、緊急ポインタフィールドの値も0になる。
ACK(Acknowledgement Number) 1の場合は確認応答番号フィールドが有効であることを示す。つまり、1の場合は確認応答番号フィールドに0以外の値がセットされている。0の場合は、確認応答番号フィールドの値も0になる。コネクション確立時の最初のSYNパケット以外は、必ず1でなければならない。
PSH(Push) 送信側TCPは、プッシュ機能が指示されるまでデータを送信バッファにためて、自分の都合が良いときに送信できる。ただし、PSH命令が出た場合は、未送信データをすべて送信しなければならないと定義されている。Telnetを使用している場合などは、PSHフィールドに1がセットされる。
RST(Reset) コネクションのリセット。通常は0がセットされているが、コネクションを一方的に強制終了させるときなどに1をセットして送信する。Webブラウザで、Webページの読み込み中止などを行ったりすると、RSTフィールドに1をセットしたTCPセグメントがWebサーバーに送信される。
SYN(Synchronize) コネクション確立時に使われる。このビットが1の場合、コネクションの確立を要求するとともに、シーケンス番号に格納されている数字でシーケンス番号を初期化する。
FIN(Fin Flag) 1の場合、以後送信するデータがないことを示す。通信が終了し、コネクションを切断したい場合に使用する。

※3 オプションとパディングはTCPの必須フィールドではない(TCPの必須フィールドは送信元ポート番号から緊急ポインタまで)。基本的に、使われることはないためTCPヘッダは20バイトと考えて良い。

順序制御

TCPによる信頼性ある通信は、TCPヘッダに格納されている情報により成り立っています。

その信頼性ある通信を実現する仕組みのひとつが「順序制御」です。

データをパケットという単位に分割して送信するわけですが、受信側では、分割されたパケットを送信側が送ったとおりの順番に整列できないと元のデータに復元することは当然できません。

しかし、送信側が送ったパケットは、必ずしも送信された順番のまま受信側に届くとは限らないのでパケットの順番を示す情報が必要です。

さて、そこでTCPヘッダの構造を示した上表を確認すると「シーケンス番号」フィールドというものがあります。

同フィールドに格納される「シーケンス番号」は、パケットに分割されたデータが、「元データの何バイト目の位置にあるものか」を表しています。

このシーケンス番号を頼りに、たとえ受信側でバラバラで到着しても、元のデータに復元してやることができます。

このような制御を「順序制御」と言います。

再送制御

送信元が送ったデータがちゃんと相手に届いたかどうか、届いてなければ、該当するパケットを再送してやる仕組みが「再送制御」です。

「確認応答番号(ACK番号)」フィールドによって、「500バイト目までは受信できたので、次は501バイト目以降を送信してください」という要求を、相手側に伝えることができます。
相手側は、この番号をチェックし、送ったはずのパケットが届いていなければ、該当するパケットを自動的に再送します。

ウインドウ制御

TCPには信頼性を提供するために、上述の順序制御や再送制御に出てきたようにシーケンス番号と確認応答番号を使って、パケットの喪失や重複、順序の入れ替わりなどを適切に管理し処理します。

最初にSYNパケットを送るときに、ランダムに決めた初期値を設定したシーケンス番号を、通信相手に送ります。相手は、そのパケットを受け取ったら確認応答(ACK)を返し、次に受け取るべきデータのシーケンス番号を確認応答番号として返します。

このシーケンス番号と確認応答番号を使って、1パケットずつ確認応答(ACK)を行っていると、パケットの往復時間(ラウンドトリップ時間)が長くなってしまい、通信速度が落ちてしまいます。そのためTCPでは、ウインドウという概念を使って、一度に複数のパケットを送信できるようにしました。
確認応答を待たずに送信できるデータの大きさがウインドウサイズです。例えば、ウインドウサイズが5000で1パケット1000ずつ送れるとした場合、5パケットを同時に送信することができます。

フロー制御

受信側の処理能力を超える量のパケットが送信されると、受信側では処理しきれず、パケットを破棄することになります。
そうならないようTCPでは、「ウインドウ」フィールドで受信側の受信バッファの残りのサイズを把握できるため、送信側で受信側のウインドウサイズを超えないように送信量を調整します。

受信側は「このウインドウサイズまでのパケットなら、一度に送っても処理しきれます」と通知します。通信に余裕があるときには、ウインドウサイズを大きく指定して、高スループットで通信を行い、受信バッファがあふれそうなときはウインドウサイズを小さく指定してデータの送信量を抑制します。

このような制御を「フロー制御」と言います。