TCP 詳細


  1. 接続の確立
  2. 応答確認
  3. フロー制御とウインドウ
  4. 輻輳制御
  5. 誤り制御
  6. 接続の終了
  7. ハーフ・クローズ
  8. ハーフ・オープン
  9. キープアライブ
  10. 各種タイマー
  11. バルク・データ・フロー
  12. TCP ヘッダ



接続の確立

接続の確立とは、送信側ホストと受信側ホストの間で、 通信を行うための論理的な通信路(コネクション)を設定することをいいます。

コネクション型通信では、データ転送前に接続を確立し、 データ転送は必ずコネクション上で行われます。

  1. クライアントは、 接続したいサーバのポート番号とクライアントのシーケンス番号 (ISN) を指定した SYN セグメントを送る。
  2. サーバは、サーバのシーケンス番号を指定した SYN セグメントで応答する。
  3. クライアントはサーバからの SYN に対して、サーバの SYN+1 で確認応答を返す。

これら 3 つのセグメントの交換でコネクションは確立する。
これを三相ハンドシェーク (Three way handshake) と呼ぶ。

最初に SYN を送った側をアクティブ・オープンを実行したといい、 次の SYN を送った側をパッシブ・オープンを実行したという。

三相ハンドシェークの状態遷移
 クライアント : active open    pasive open : サーバ 
ステータス 送受信データ ステータス
CLOSED
LISTEN
SYN_SENT
ISN+SYN フラグ     
     ISN+SYN+ACK フラグ
SYN_RECV
ESTABLISHED
ISN+ACK フラグ     
    
ESTABLISHED




応答確認

送信したデータが受信側に正しく届いた時、 受信側からセグメントが正しく届いたことを知らせます。 TCP は、この機能によりエラー等でデータが正しく届かなかったことを認知します。

データが正しく届いていないことが解かれば、データを再送します。 これにより、信頼性の高い通信が実現できます。

  1. 送信側は、送信するデータを再送用バッファに保存してから、 データを送信し、タイマを動作させます。
  2. 受信側は、データを受信すると、その都度応答確認番号(ACK 番号)を、 送信側に送ります。
  3. 送信側では、ACK 番号が送られるのを待って、 次のデータを再送用バッファに保存してから、それを送信します。
  4. もし、適当な時間が過ぎても、ACK 番号が返って来ない場合は、 データが正しく届かなかったと判断し、 送信側は同じデータを再送用バッファから再送します。





フロー制御とウインドウ

送信側ホストから受信側ホストへデータを送るとき、 受信側ホストの処理が間に合わなくなることがあります。 この時、受信側ホストでは未処理データをバッファ(一時メモリ)に退避します。 しかし、このバッファの容量にも限度があるため、 バッファ容量をこえたデータは、欠落し、 結果として再送することになります。

データがあて先までは正しく届いたのに、取りこぼしたために再送するのは無駄です。 これを回避するため、受信側はその処理能力に合わせて、 送信するデータ量を制御し、取りこぼしを防ぐ動作をします。 これをフロー制御といいます。

TCP では、受信側が送信側のデータ量をウインドウ・サイズを使って制御します。 ウインドウ・サイズは、受信側が受信できるデータのバイト数です。 従って受信処理側からサイズ0のウィンドウが通知されると送信処理が停止します。 それにもかかわらずアプリケーションからの送信要求が続くと、 送信バッファがいっぱいになり、送信要求をブロックすることになります。

受信処理側から再度有限サイズのウィンドウが通知され送信処理が再開されると、 送信要求のブロックを解除します。

これは、TCP ヘッダの window に設定されています。 送信するデータがウインドウ・サイズより大きい場合、 受信側のバッファが空くまで待たなければなりません。 送信しようとするデータのサイズがウインドウ・サイズより小さい場合、 ACK 番号を受け取っていなくても、ウインドウに収まる間は、 送信し続けられます。

一方、送信側のデータ量を制御するのが、輻輳ウインドウです。 送信側は、輻輳ウインドウと広告ウインドウの最小値でデータを転送することが可能です。




輻輳制御

ネットワーク上に処理しきれないほどのデータが流入すると、 データ同士の衝突が頻繁に発生し、データ転送ができない状態になってしまいます。 これを輻輳(congestion)といいます。

輻輳が発生すると、タイムアウトや重複 ACK の受信が増加し、 現行ウインドウサイズの 1/2 をスロースタートの閾値にセーブします。

この状態を防ぐため、データ入力量の制御、 輻輳が発生した場合にそれを解消するトラフィィック制御を行います。





誤り制御

ネットワーク上を移動するパケットには、損傷や欠損などのエラーが発生します。 パケットの損傷は、 チェックサムと呼ばれる誤り検出符号をパケットに付加することで検出でき、 誤りが検出されたパケットは破棄され、 そのパケットは再送によって回復されます。



接続の終了

データの送信が完了すると、通信に使っていた接続をクローズします。

TCP 接続は全二重なので、各方向が独立にシャットダウンしなければなりません。 データ送信を終了するときは FIN を送ります。TCP は FIN を受け取ると、 アプリケーションに対して、データフローの終了を告知しなければなりません。 しかし TCP は FIN を受け取った後もデータを送ることはできます(ハーフ・クローズ)。

最初にクローズした( FIN を送った) 側をアクティブ・クローズを実行したといい、 他方をパッシブ・クローズを実行したといいます。

このあと、アクティブ・クローズを実行した側は、TIME_WAIT 状態になります。 これを 2MSL(maximun segment lift time) 待ち状態といいます。 これは最後の ACK を送信後 MSL が 2 回繰り返される時間だけ TIME_WAIT で留まっていなければならないというものです。 これにより、最後の ACK が消失した場合でも、 TCP は再度 ACK を送信できることになります。 そのあと, TCP 接続は終了します。

 クライアント : active close    passive close : サーバ 
関     数 ステータス 送受信データ ステータス 関     数
close() → FIN_WAIT_1
FIN フラグ CLOSE_WAIT

(CLOSING) ACK フラグ

← EOF を配信
     FIN_WAIT_2 ACK+FIN フラグ
LAST_ACK ← close()
     TIME_WAIT
ACK フラグ CLOSED
     CLOSED








ハーフ・クローズ

TCP では、他エンドからのデータを受信中でも、データ出力を終了できます。 これをハーフ・クローズという。

 クライアント : active close    passive close : サーバ 
関     数 ステータス 送受信データ ステータス 関     数
shutdown() → FIN_WAIT_1
FIN フラグ CLOSE_WAIT
    
ACK フラグ

← EOF を配信
read() →
data

← write()
    

ACK フラグ

EOF を配信 →
FIN フラグ
LAST_ACK ← close()

TIME_WAIT
ACK フラグ CLOSED





ハーフ・オープン

一方のエンドが他方に知らせずに接続を終了もしくは中断したとき、 その状態をハーフ・オープンという。 いったんこの状態になると相手のクラッシュを検知することはできません。 そのためのオプションとしてキープアライブがあります。



キープアライブ

キープアライブを有効にするエンドはサーバであり、他方はクライアントです。 サーバはクライアントに検査セグメントをおくります。 この場合クライアントは以下の4つの状態のいずれかです。

  1. クライアントの TCP は通常どうり応答する。
    サーバはキープアライブ・タイマをセットし、その時点でトラフィックがあれば、 キープアライブ・タイマをリセットする。

  2. クライアントの TCP は応答しない。その後、10 回リトライするが、応答なし。
    サーバはクライアントがダウンしたと判断し、接続を終了する。

  3. クライアントの TCP は応答するが、応答は RESET である。
    サーバは接続を終了する。

  4. クライアントは稼動しているが、サーバから到達不可能である。
    サーバはクライアントがダウンしたと判断し、接続を終了する。




各種タイマー

TCP は各コネクションに対して、次の4つの異なるタイマーを管理しています。

  1. 再転送タイマー
    他エンドからの確認応答を期待する時間
  2. 接続タイマー
    他エンドが受信ウインドウをクローズした後、ウインドウ情報を維持する時間
  3. キープアライブタイマー
    アイドル状態のコネクションのエンドがクラッシュあるいは再起動したかを検出するためのの時間
  4. 2MSL タイマー
    コネクションが TIME_WAIT 状態でいる時間




バルク・データ・フロー

TCP がスライディング・ウィンドウ・プロトコルとよばれるフロー制御を利用すると、 送り手は確認応答を待たずに複数のパケットを転送できるようになり、 より高速なデータ転送が可能になります。 TCP では ACK は累積でき、それらは確認応答シーケンス番号 -1 までのすべての受信バイトが正しく受け取れたことを確認応答します。

時間の経過とともに、受け手がデータを確認応答すると、 スライディング・ウィンドウは右に移動します。 ウィンドウの両端が移動することで、ウィンドウのサイズは拡大、縮小します。 ウィンドウの両端が移動することは、以下の3つの用語で表現されます。

  1. ウィンドウの左端が右方向に移動することをクローズするといいます。 これはデータが送られ、確認応答されると行われます。
  2. ウィンドウの右端が右方向に移動することをオープンするといいます。 新しいデータの送信が可能になります。 これは他方のエンドの受信プロセスが確認応答データを読み、 TCP の受信バッファに空きスペースが増えたときに行われます。
  3. ウィンドウの右端が左に移動することをシュリンクするといいます。 シュリンクする相手に対応するためには、TCP もシュリンクできなければなりません。

受け手によって提供されるウィンドウのサイズは通常、 受信プロセスによって制御されます。

スロースタート

送り手は受け手が広告するウィンドウ・サイズの限界まで、 複数のセグメントをネットワークに送り出すことで、スタートしている。 これは2台のホストが同じ LAN 上にあれば問題ないが、 送り手と受け手の間にルータや低速な回線があればトラブルの原因となります。 いくつかの中継ルータはパケットをキューしなければならず、 その結果ルータの容量を越えてしまう可能性もあります。

TCP は現在スロースタートと呼ばれるアルゴリズムをサポートする必要があります。 それはネットワークに送り出されるパケットの通信速度を、 他方のエンドから返される確認応答の通信速度と同調させるための機能です。

スロースタートは、送り手の TCP に別のウィンドウを追加します。 それは、輻輳ウィンドウまたは cwnd とよばれます。 ホストと他のネットワークの間で新しいコネクションが確立されると、 輻輳ウィンドウはセグメント1つ(他のエンドから告知されたサイズ)に初期化されます。 ACK を受け取るたびに、輻輳ウィンドウは1セグメントづつ増やされます。 送り手は輻輳ウィンドウと広告されたウィンドウの最小値まで データを転送することが可能です。 広告されたウィンドウは受け手からのフロー制御だが、 輻輳ウィンドウは送り手によるフロー制御です。




TCP ヘッダ

TCP ヘッダは、以下の形式で 24(20) バイト長です。






フィールド 意味
source 発信元TCPポート番号です(16bits)
dest 送信先TCPポート番号です(16bits)
seq シーケンス番号でこのパケットがデータストリーム中のどこに位置するかを示す。
ack_seq 受信パケットに対するack(応答)のシーケンス番号で、 どこまで受信パケットを受け取ったか相手に通知する
doff TCPヘッダ長。TCPオプション域があるとヘッダのサイズが変わる
urg 緊急フラグでurg_ptrが有効であることを示す
ack 応答フラグでack_seqが有効であることを示す。 通常このフラグは常にONである
psh PUSHフラグで、なるべく早く送信することを示すが、 現在はあまり重要な意味はない
rst RESETフラグは、コネクションのリセットを要求する
syn コネクションの確立を要求する
fin コネクションの終了を要求する。
urg_ptr 緊急データの最後を指す。
window 受信windowの大きさを通知する。
check チェックサムで、UDPと同じくTCPヘッダとデータの 両方(IPヘッダの一部の情報も利用)に対して計算される。