接続の確立とは、送信側ホストと受信側ホストの間で、 通信を行うための論理的な通信路(コネクション)を設定することをいいます。
コネクション型通信では、データ転送前に接続を確立し、 データ転送は必ずコネクション上で行われます。
これら 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 は、この機能によりエラー等でデータが正しく届かなかったことを認知します。
データが正しく届いていないことが解かれば、データを再送します。 これにより、信頼性の高い通信が実現できます。
![]() |
![]() |
送信側ホストから受信側ホストへデータを送るとき、 受信側ホストの処理が間に合わなくなることがあります。 この時、受信側ホストでは未処理データをバッファ(一時メモリ)に退避します。 しかし、このバッファの容量にも限度があるため、 バッファ容量をこえたデータは、欠落し、 結果として再送することになります。
データがあて先までは正しく届いたのに、取りこぼしたために再送するのは無駄です。 これを回避するため、受信側はその処理能力に合わせて、 送信するデータ量を制御し、取りこぼしを防ぐ動作をします。 これをフロー制御といいます。
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つの状態のいずれかです。
![]() |
![]() |
TCP は各コネクションに対して、次の4つの異なるタイマーを管理しています。
![]() |
![]() |
TCP がスライディング・ウィンドウ・プロトコルとよばれるフロー制御を利用すると、 送り手は確認応答を待たずに複数のパケットを転送できるようになり、 より高速なデータ転送が可能になります。 TCP では ACK は累積でき、それらは確認応答シーケンス番号 -1 までのすべての受信バイトが正しく受け取れたことを確認応答します。
時間の経過とともに、受け手がデータを確認応答すると、 スライディング・ウィンドウは右に移動します。 ウィンドウの両端が移動することで、ウィンドウのサイズは拡大、縮小します。 ウィンドウの両端が移動することは、以下の3つの用語で表現されます。
受け手によって提供されるウィンドウのサイズは通常、 受信プロセスによって制御されます。
スロースタート
送り手は受け手が広告するウィンドウ・サイズの限界まで、 複数のセグメントをネットワークに送り出すことで、スタートしている。 これは2台のホストが同じ LAN 上にあれば問題ないが、 送り手と受け手の間にルータや低速な回線があればトラブルの原因となります。 いくつかの中継ルータはパケットをキューしなければならず、 その結果ルータの容量を越えてしまう可能性もあります。
TCP は現在スロースタートと呼ばれるアルゴリズムをサポートする必要があります。 それはネットワークに送り出されるパケットの通信速度を、 他方のエンドから返される確認応答の通信速度と同調させるための機能です。
スロースタートは、送り手の TCP に別のウィンドウを追加します。
それは、輻輳ウィンドウまたは cwnd とよばれます。
ホストと他のネットワークの間で新しいコネクションが確立されると、
輻輳ウィンドウはセグメント1つ(他のエンドから告知されたサイズ)に初期化されます。
ACK を受け取るたびに、輻輳ウィンドウは1セグメントづつ増やされます。
送り手は輻輳ウィンドウと広告されたウィンドウの最小値まで
データを転送することが可能です。
広告されたウィンドウは受け手からのフロー制御だが、
輻輳ウィンドウは送り手によるフロー制御です。
![]() |
![]() |
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ヘッダの一部の情報も利用)に対して計算される。 |
![]() |
![]() |