# TCP TCPは"Transmission Control Protocol"の略で、トランスポート層のプロトコルの一つです。TCPは、信頼性の高い通信を提供するために設計されており、以下のような特徴を持っています。 - **コネクション指向**: 通信を開始する前に、接続を確立する必要があります。これにより、データの送受信が確実に行われます。 - **信頼性**: データの順序が保証され、パケットの損失や重複が検出され、再送信が行われます。 ## TCPの概要 それではTCPの概要について確認しましょう。 TCPでは情報は連続して入出力されるということで、UDPにおける『データグラム』とは違う『ストリーム(stream)』という概念で扱われます。 ストリームは、データの連続的な流れを表し、TCPはこのストリームを確実に送受信するための機能を提供します。 - ストリームには順序(最初に送信されたデータが最初に届く)を保証する必要があります。 - ストリームは、データの断片化を行わず、連続したデータとして扱います。つまり、アプリケーション層でのデータの分割や結合は必要ありません。 - 分割は必要であればTCPの層が行うため、アプリケーション側では考えなくて良いということです。 - ストリームは、データの到達確認を行い、パケットの損失や重複を検出し、再送信を行います。これにより、信頼性の高い通信が実現されます。 ### 順序の保証 TCPにおいて、送出されるデータは順序が保証されるようになっています。 これは、断片化が発生する際に『この断片はストリームの何番目』という情報を付与して送信されるためです。 受信側はこの情報を利用して、受信したデータを正しい順序で再構築します。 ### 信頼性の保証 TCPは、信頼性の高い通信を提供するために、以下のような機能を持っています。 - **再送信**: パケットが損失した場合、TCPはそのパケットを再送信します。これにより、データの完全性が保証されます。 - **フロー制御**: TCPは、送信側と受信側のバッファサイズを考慮して、データの送信速度を調整します。これにより、受信側が処理できる範囲内でデータが送信され、輻輳を防ぎます。 ### コネクションの確立と終了 信頼性のひとつとしても考えられますが、どこから通信が開始され、どこで終了するのかが明確にしておかないといけません。 そのために、接続(コネクション)の確立と終了の際に、3ウェイハンドシェイクと呼ばれる手順が用いられます。 (3wayhandshake)= #### 接続時の確立フロー ```{mermaid} sequenceDiagram participant Client participant Server Client->>Server: SYN (接続要求) Server->>Client: SYN-ACK (接続承認) Client->>Server: ACK (接続完了) ``` 接続の確立の前にある意味確認とも言える『接続要求』の信号を送信します、これをSYNと呼びます。 受け取った相手側は双方向通信を行うため、ACKと呼ばれる『承認』の信号と共にSYNも返します(SYN-ACK)。 受け取った側(すなわち本来の接続要求側)も、ACKを返すということになります。 これにより双方から通信の承認が下り、双方向回線が開くということになります。 #### 接続終了のフロー ```{mermaid} sequenceDiagram participant Client participant Server Client->>Server: FIN (接続終了要求) Server->>Client: ACK (接続終了承認) Server->>Client: FIN (接続終了要求) Client->>Server: ACK (接続終了承認) ``` 接続を終了する際には、FINと呼ばれる『ここで通信は終わります』に相当する信号を送信します。これは『接続終了要求』の意味を持ちます。 こちらでも、受け取った側からACK(承認)と共にFINを返します。 送信元もこれに応じてACKを返すことで、双方のFINが成立して切断となります。 もちろん実際にはFINの処理が行われる前に『強制的に回線が切られる』などの問題も起こりえますが、正常な終了という意味ではこの流れとなっています。 ### フロー制御 フロー制御は、TCPの重要な機能の一つであり、送信側と受信側の間でデータの流れを調整する役割を果たします。これにより、受信側が処理できる範囲内でデータが送信され、輻輳を防ぎます。 TCPでは、**ウィンドウサイズ**という概念を用いてフロー制御を実現しています。ウィンドウサイズは、送信側が一度に送信できるデータの量を制限するもので、受信側のバッファサイズに基づいて動的に調整されます。 具体的には、受信側は自分のバッファの空き状況を送信側に通知し、送信側はその情報をもとにデータの送信速度を調整します。これにより、受信側がオーバーフローすることなく、スムーズなデータ転送が実現されます。 この部分をいかに効率的に行うかというのが輻輳制御(ふくそうせいぎょ)というものです。 輻輳制御のアルゴリズムなどまではこの授業では取り扱いません。 ### TCPのヘッダー構造 以下の図は、基本となるTCPのヘッダー構造です。 ```{mermaid} classDiagram class TCPHeader { +Source Port(送信元ポート番号): 16bit +Destination Port(宛先ポート番号): 16bit +Sequence Number(シーケンス番号): 32bit +Acknowledgment Number(確認応答番号): 32bit +Data Offset(データオフセット): 4bit +Reserved(予約領域): 6bit +Flags(コントロールフラグ): 6bit +Window Size(ウィンドウサイズ): 16bit +Checksum(チェックサム): 16bit +Urgent Pointer(緊急ポインタ): 16bit } ``` それぞれを簡単に説明します。 - **Source Port(送信元ポート番号)**: 送信元のアプリケーションが使用するポート番号です。 - **Destination Port(宛先ポート番号)**: 宛先のアプリケーションが使用するポート番号です。 - **Sequence Number(シーケンス番号)**: 送信されたデータの順序を識別するための番号です。これにより、受信側はデータの順序を正しく再構築できます。 - **Acknowledgment Number(確認応答番号)**: 受信側が受け取ったデータの最後のシーケンス番号を示します。これにより、送信側はどのデータが受信されたかを確認できます。 - **Data Offset(データオフセット)**: ヘッダーの長さを示すフィールドで、ヘッダーのサイズを**4の倍数で**表します。 - ここだけは少しややこしくて、4バイトを1つのかたまりと考えてn倍という形で表現します。 - 標準的なTCPヘッダは20バイトのため、ここの値は(20/4で)5です。 - オプションがある場合は32ビット単位のパディングで入力するキマリのため、必ず4の倍数です。 - **Reserved(予約領域)**: 将来の拡張のために予約されたビットです。 - **Flags(コントロールフラグ)**: TCPの制御フラグを示すビットフィールドで、接続の確立や終了、データの確認応答などの状態を示します。 - **Window Size(ウィンドウサイズ)**: 受信側のバッファサイズを示し、送信側が一度に送信できるデータの量を制限します。 - **Checksum(チェックサム)**: データの整合性を確認するためのフィールドで、送信されたデータに対するチェックサムが含まれます。 - **Urgent Pointer(緊急ポインタ)**: 緊急データの位置を示すためのフィールドで、緊急データが含まれている場合に使用されます[^urgent]。 [^urgent]: 実際のところほとんどアプリケーションで使うことも無ければ、受けた側も意識することはほとんどありません。緊急でもないデータに緊急とフラグを立てられることが考えられるからです。 シーケンス番号により、上位レイヤーから送られたデータが分割されている状態において『どの位置か』がわかる仕組みとなっています。 そして確認応答番号により『受信側がどのデータを受け取ったのか』がわかる仕組みとなっています[^ack-inc]。 [^ack-inc]: 実際は『次に欲しいシーケンス番号』が入ります、例えばシーケンス番号1000まで受け取っている場合、次に欲しいものは1001となるため、1001を確認応答として返します。 ### 再送について TCPにおいては、送信側が『受信側がどこまで受信できているか』を確認応答番号で把握します。 そのため、受信側が確認応答を返さない場合、送信側はそのデータが失われたと判断し、再送信を行います。 再送が発生するまでの時間(いわゆる『タイムアウト』)をどうするかはOSによる違いがあります。 一般的なUNIXでは、初期のタイムアウトを0.5秒単位で制御しており、タイムアウトは0.5秒の整数倍とされていることが多いです。ただし最初は多め(6秒程度)に設定されていることが多いです。 再送しても確認応答が戻らない場合は、繰り返し送信していきますが、タイムアウトが倍々となっていきます(0.5秒→1秒→2秒→4秒…)。さらにタイムアウトが増えていくことで、最終的には『再送をやめる』となります。 回線の正常な切断が行われなければ、いずれ再送が起きなくなり、回線は切れた状態となります。 ### ウィンドウサイズとバースト送信 ひとつパケットを送るたびにACKを待っていると非常に効率が悪いため、実際にはある程度まとめて送信を行っています。 このまとめて送る際に重要となるのがウィンドウサイズです。 確認応答パケットにあるウィンドウサイズを利用して、TCPパケットの分割された状態(セグメント、分割されたIPパケットとざっくり考えておくとよい)をまとめて送信します。 このウィンドウサイズは、受信側のバッファサイズに基づいて動的に調整されます。送信側は、ウィンドウサイズを考慮して、同時に送信できるデータの量を制限します。 この場合の確認応答は『受信の完了したデータの最後のシーケンス番号(+1)』となります。 もし途中のシーケンスが損失した場合は多少問題が発生します。 - ACKにより足りないシーケンス番号が返されるため、それ以降の再送となります。 - そもそもACKが帰ってこない場合は、まとめて送り直しということになります。 ### TCPの利用例 TCPは、信頼性が重要なアプリケーションで広く使用されています。以下は、TCPの利用例です。 - **ウェブブラウジング**: HTTPやHTTPSプロトコルは、TCPを基盤として動作します。ウェブページのデータが正確に順序通りに送信されることが求められます。ただし最近はQUICというプロトコルに置き換えられる状況が出ています(HTTP/3)。 - **電子メール**: SMTP、POP3、IMAPなどの電子メールプロトコルは、TCPを使用して信頼性の高いメールの送受信を実現します。 - **ファイル転送**: FTP[^ftp](File Transfer Protocol)やSFTP(Secure File Transfer Protocol)などのファイル転送プロトコルは、TCPを使用してファイルの正確な転送を行います。 [^ftp]: 現在ではほとんど使われません、ブラウザも昔は対応していました(`ftp://`)が、現在はほとんどのブラウザで対応していません。ftps(ftp over SSL)とsftp(Secure File Transfer Protocol)は違います。ファイル転送という意味では、後者(sftp)の利用が推奨されます。