TCPとは、TCP/IPにおける、トランスポート層プロトコルの1つである。
概要
TCPは信頼性のある通信を担保するのだが、それはどういう意味なのかというと、
というこれらの特性を併せ持つ。結果として、仕様は非常に大きなものになっている(UDPはわずか3ページなのに対し、TCPは初期の1974年版の段階で70ページ、現在のバージョンで98ページ)。あまりに巨大なので、簡単に仕様を説明していく。
TCPセグメントの中身
TCPセグメントの中身は、以下の通り。
領域 | サイズ | 説明 | |
---|---|---|---|
送信元ポート番号 | 16ビット | 送信元のポート番号 | |
送信先ポート番号 | 16ビット | 送信先のポート番号 | |
シーケンス番号 | 32ビット | 送るデータの開始地点 | |
確認番号 | 32ビット | 次にほしいシーケンス番号。ACK=1の時のみ有効 | |
TCPヘッダ長 | 4ビット | TCPヘッダの長さを32ビットで割ったもの | |
フラグ | 予約済み | 4ビット | 将来の用途に予約されている 0を入れること なお、一番最後のビットはかつてはRFC3540でNSフラグになっていた |
CWR | 1ビット | 輻輳制御ウィンドウ縮小 RFC3168で定められている |
|
ECE | 1ビット | ECN-Echo RFC3168で定められている |
|
URG | 1ビット | 緊急フラグ これが1の場合、緊急ポインタが有効である |
|
ACK | 1ビット | 確認応答フラグ これが1の場合、確認番号が有効である TCP通信において、ここが1でないパターンは限定される |
|
PSH | 1ビット | プッシュフラグ これが1の場合、速やかに上位層へ引き渡すよう要求される |
|
RST | 1ビット | リセットフラグ 不正なセグメントを受け取った場合、ここを1にして接続を強制終了する |
|
SYN | 1ビット | シーケンス番号同期 接続の開始時には、ここを1にする |
|
FIN | 1ビット | 送信データ終了 これ以上データを送らないことを伝える |
|
ウィンドウサイズ | 16ビット | ウィンドウサイズのオクテット数 ただし、ウィンドウスケールオプションを使った場合、ビットがシフトする 例えばここに32768と指定し、オプションで4と指定した場合、512KiBとなる |
|
チェックサム | 16ビット | データが破損してないかチェックするフィールド 作り方に関しては割愛(ほぼUDPと同じ) |
|
緊急ポインタ | 16ビット | 緊急データの場所(シーケンス番号からのオフセット) URG=1の時のみ有効 |
|
オプション | 可変長 | TCPの様々なオプション。最終的に32ビットの倍数になる | |
ペイロード | 可変長 | 実際のデータ |
接続の状態、およびその遷移
まず、接続の状態には、以下のものがある。
- LISTEN
- 他からの接続を待機している
- SYN-SENT
- 他への接続要求を送信し、その応答を待機している
- SYN-RECEIVED
- 接続要求に対して認容し、その応答を返した後、相手側からの応答を待っている
- ESTABLISHED
- 接続が確立しており、双方でデータのやり取りを行っている
- FIN-WAIT-1
- 接続のデータ送信を終了し、FINフラグのセグメントを送信した後、その認容応答を待っている
- FIN-WAIT-2
- FIN-WAIT-1の後、認容応答をもらった
- CLOSE-WAIT
- FINフラグのセグメントを受信した
- CLOSING
- FIN-WAIT-1の後、相手側からもその応答の前にFINフラグのセグメントを受信した
- LAST-ACK
- CLOSE-WAITの後、こちらもデータの送信が終了し、FINフラグのセグメントを送信した後、その認容応答を待っている
- TIME-WAIT
- FIN-WAIT-2からFINフラグのセグメントを受信した、もしくはCLOSINGから認容応答を受信した後、データの重複などで処理がおかしくならないよう待機している
- CLOSED
- 接続中でも、接続待機中でもない
状態遷移は、以下のようになっている。
- CLOSED→LISTEN
- 接続の待ち受けを開始すると、LISTEN状態へ移行する
- CLOSED→SYN-SENT
- 接続要求(SYN=1のセグメント)を送信すると、SYN-SENT状態へ移行する
- LISTEN→CLOSED
- 接続の待ち受けを終了すると、CLOSED状態へ移行する
- LISTEN→SYN-RECEIVED
- 接続要求を受け付けると、接続確認要求(SYN=1・ACK=1)を送信し、SYN-RECEIVED状態へ移行する
- LISTEN→SYN-SENT
- 接続の待ち受けを開始した後、自分から接続要求を送信すると、SYN-SENT状態へ移行する
- SYN-SENT→CLOSED
- 接続要求送信後、受理前にキャンセルすると、CLOSED状態へ移行する
- SYN-SENT→SYN-RECEIVED
- 接続要求送信後、相手からの接続確認要求到着前に接続要求を受信した場合、接続確認要求を送信し、SYN-RECEIVED状態へ移行する
- SYN-SENT→ESTABLISHED
- 接続要求送信後、相手からの接続確認要求を受信すると、確認応答(ACK=1)を送信し、ESTABLISHED状態へ移行する
- SYN-RECEIVED→ESTABLISHED
- 接続確認要求を送信し、その確認応答を受信すると、ESTABLISHED状態へ移行する
- SYN-RECEIVED→FIN-WAIT-1
- 接続確認要求を送信し、その後、確認応答受信前に送信終了(FIN=1・ACK=1)を送信すると、FIN-WAIT-1状態へ移行する
- ESTABLISHED→FIN-WAIT-1
- 接続が確立した状態で、送信終了を送信すると、FIN-WAIT-1状態へ移行する
- ESTABLISHED→CLOSE-WAIT
- 接続が確立した状態で、送信終了を受信すると、確認応答を送信し、CLOSE-WAIT状態へ移行する
- FIN-WAIT-1→FIN-WAIT-2
- FIN-WAIT-1状態で、その送信終了に対する確認応答を受信すると、FIN-WAIT-2状態へ移行する
- FIN-WAIT-1→CLOSING
- FIN-WAIT-1状態で、送信終了を受信すると、確認応答を送信し、CLOSING状態へ移行する
- CLOSE-WAIT→LAST-ACK
- CLOSE-WAIT状態で、送信終了を送信すると、LAST-ACK状態へ移行する
- FIN-WAIT-2→TIME-WAIT
- FIN-WAIT-2状態で、送信終了を受信すると、確認応答を送信し、TIME-WAIT状態へ移行する
- CLOSING→TIME-WAIT
- CLOSING状態で、送信終了に対する確認応答を受信すると、TIME-WAIT状態へ移行する
- LAST-ACK→CLOSED
- LAST-ACK状態で、送信終了に対する確認応答を受信すると、CLOSED状態へ移行し、接続を終了する
- TIME-WAIT→CLOSED
- TIME-WAIT状態へ移行した後、MSL[1]の2倍が経過したら、CLOSED状態へ移行し、接続を終了する。待機する理由は、重複したセグメントが再送されてきたときに、前の接続のものか後の接続のものか判断がつかなくなることを防ぐためである
これだけでもすでにおなかいっぱいになってる人も多いだろう。
この中で、CLOSED→SYN-SENT→ESTABLISHED、およびLISTEN→SYN-RECEIVED→ESTABLISHEDの一連の流れのことを、TCPの3ウェイハンドシェイクと呼ぶ。これは
という1往復半の流れから来ている。
データの送受信
まず、最初のSYN→ACK+SYN→ACKで、どのシーケンス番号から送信・受信するかの合意をとっている。その後、データを送信するが、どのシーケンス番号のデータかを送る。
なお、FINを送った後も、データがないACKは送ってもよいし、送らないとデータが受け取れたのか把握できないので送らないといけない。
例えば、このようになったらどうだろうか?
こうなったら、しばらく待ってから同じセグメントを送りなおす。
だが、いちいち送るたびに確認応答を待っているのでは時間の無駄である。まとめていくつかのセグメントを連続して送ってよいのだろうか?答えからいうと可能である。
- A→B シーケンス番号1000、確認番号1300、データサイズ100オクテット、ACK
- A→B シーケンス番号1100、確認番号1300、データサイズ100オクテット、ACK
- A→B シーケンス番号1200、確認番号1300、データサイズ100オクテット、ACK
- Bに2が到着、B→A シーケンス番号1300、確認番号1000、ACK
- Bに1が到着、B→A シーケンス番号1300、確認番号1200、ACK
- Bに3が到着、B→A シーケンス番号1300、確認番号1300、ACK
1と2が到着順序が入れ替わってしまっているが、シーケンス番号でデータの順番はわかるので、正しい順序でデータの組み立てなおしができる。
でも、そんなに無秩序に送って大丈夫か?大丈夫だ、問題ない。
まず、ウィンドウサイズというフィールドがあるが、ここに、残りの空き容量が入っている。例えば2000あったとしたら、2000オクテットまではまとめて送って大丈夫だろうという判断になる(少なくとも受信側のバッファの問題はない)。
そんなこと言ってたらデータがボロボロ零れ落ちないか?という心配がある。ごもっとも。いくらデータをたくさんバッファできるからとたくさんまとめてデータを送ったら、回線輻輳で再送を余儀なくされることも出てくる。
なので、いきなりはたくさん連続して送ることはなく、スロースタートでどんどん増やしていく。再送が必要になったということは、まとめて送ってたら回線輻輳が起きているわけだから、まとめて送るデータ量を減らしてリトライする。
関連リンク
関連項目
脚注
親記事
子記事
- なし
兄弟記事
- 0
- 0pt