目次
序文
前章では、UDP プロトコルについて説明し、UDP プロトコルの形式と機能を理解しました。この記事では、 TCP プロトコルの関連内容について詳しく説明します。TCPは、「Transmission Control Protocol」の略です。その名前が示すように、データ送信を詳細に制御する必要があります。TCP プロトコルは、コンピュータ ネットワークの重要な部分であるだけでなく、インタビューの頻度が非常に高いコンテンツの一種、特に3 ウェイ ハンドシェイク、4 ウェイ ウェーブとそこから派生するさまざまな問題について、この記事では TCP プロトコルの形式、TCP 接続の確立方法、信頼性やパフォーマンスを確保するためのさまざまな戦略など、多くの内容があり、辛抱強く読むことで間違いなく多くのことを得ることができます。
TCPプロトコル形式
操作を実行する前に、TCP プロトコルのヘッダー形式を理解する必要があります。写真は次のとおりです。
- 送信元/宛先ポート番号: データがどのプロセスから送信され、どのプロセスに送信されるかを示します。
- 32ビットシリアル番号/32ビット確認番号:詳細は後述。
- 4 ビット TCP ヘッダー長: TCP ヘッダーにある 32 ビット ビットが何個あるか (4 バイトが何個あるか) を示します。つまり、TCP ヘッダーの最大長は 15 * 4 = 60 です。(上記の通り)
- 6桁のフラグ:
- URG: 緊急ポインタは有効ですか?
- ACK: 番号が有効かどうかを確認します
- PSH: 受信アプリケーションに、TCP バッファからデータをすぐに読み取るように要求します。
- RST: 相手は接続を再確立する必要があります。RST 識別子を運ぶメッセージ セグメントをリセット セグメントと呼びます。
- SYN: 接続を確立するリクエスト。SYN 識別子を同期セグメントと呼びます。
- FIN: ローカルエンドが閉じようとしていることを相手に通知する FIN 識別子を持つセグメントをエンドセグメントと呼びます。
16 ビット ウィンドウ サイズ: 詳細については後ほど説明します。
16ビットチェックサム:送信側でのパディング、CRCチェック、受信側での検証に失敗した場合、データに問題があるとみなされます、ここでのチェックサムにはTCPヘッダーだけでなくTCPデータも含まれます16 ビット緊急ポインタ:データのどの部分が緊急データであるか
を識別します;
40 バイトのヘッダー オプション: 現時点では無視します
これには、次の 2 つの必要な質問に答える必要があります。
a.上層への納品方法 b.開梱方法
a. TCP ヘッダーには16 ビットの宛先ポート番号が含まれており、そのポート番号に基づいて上位層のプロセスを見つけて配信できるため、これは簡単です。
☆ b.TCPヘッダーはオプションが付加されているため可変長ヘッダーとなります。毎回、最初に最初の 20 バイトが抽出され、次にheader に記載されている 4 ビットのヘッダー長に基づいてヘッダー全体 (オプションを含む) のサイズが取得されます。
ただし、4 桁のヘッダーの長さが表現できる範囲は 0000 ~ 1111、つまり 0 ~ 15 バイトの長さであり、ヘッダー自体の 20 バイトにも収まりません。
実際、4 ビットのヘッダーの長さを指定する単位は 1 バイトではなく 4 バイトです。つまり、 0001 は1 バイトではなく4 バイトを表すため、 4 ビットのヘッダー長が表現できる範囲は 0 ~ 60 バイトになります。ヘッダーは少なくとも 20 バイトであるため、4 桁のヘッダー長で表される実際の範囲は [20-60] になります。
次に、4 ビットのヘッダー長で表されるバイト数を使用します。20 バイトはオプションのサイズであり、オプションを解析し、残りがデータになります。
要約は次のとおりです。
- 最初の 20 バイトを抽出する
- 標準ヘッダーに従い、4桁のヘッダー長 * 4 - 20 を抽出し、 res に設定します
- a. res == 0 の場合は、オプションが存在せず、この時点で読み取りが完了していることを意味します b. res > 0 の場合、オプションがあり、この時点でオプションを読み取ることができることを意味します。
- ヘッダーを読み込んだら、残りはペイロードです。
確認応答メカニズム (ACK)
信頼性を理解する
たとえば、AとBの2人が電話で話しているときに、AがBに「ご飯食べた?」と話しかけたとき、このときBが「食べました」と答えれば、AはBの話を聞いてBであることが分かります。確認の返事をしてくれたので、私の言ったことを聞いていたのでしょう。
しかし、B は、A が「食べました」と言ったことをうまく受け取ったかどうかは知りませんが、このとき A が「何を食べました」と言えば、A が言ったことを受け取ったに違いないと確信できます。
しかしこの時、Aさんは自分の「何食べた?」という言葉が相手に受け取られたとは知りませんでした。
したがって、A も B も、最後に送信したデータが相手に受信されることを保証できません。
しかし、地元では100% 信頼できます。
なぜなら、送信されたすべてのメッセージに対して、一致する応答がある限り、送信したばかりのメッセージが相手に受信されることを保証できるからです。
したがって、TCP プロトコルの確認応答メカニズムでは、メッセージが対応する応答を受信する限り、送信したデータが相手に受信されたことが保証されます。
確認応答メカニズム
確認応答メカニズム (ACK) は、コンピュータ ネットワークの通信メカニズムです。TCP 通信では、送信者が受信者にデータを送信すると、送信者は受信者がデータが送信されたことを確認する確認応答 (ACK) が返されるのを待ちます。無事に受け取りました。
実装方法:シーケンス番号と確認シーケンス番号により、応答がどのメッセージに応答するかを保証します。
シリアル番号と確認用シリアル番号:
シーケンス番号フィールドは、 TCP データグラムのデータ部分の最初のバイトのシーケンス番号を示し、データの番号付けと並べ替えに使用されます。
確認応答シーケンス番号フィールドは、送信者が受信することを期待している次のバイトのシーケンス番号を示します。
TCP の信頼性の高い送信とフロー制御を実現するには:
1. リクエストとレスポンスが 1 対 1 で対応するようにします。
2. シリアル番号の確認:シリアル番号以前のデータをすべて受信していることを確認します。(たとえば、1000、2000、3000 を送信した後、受信者は確認のシリアル番号 1001、2001、3001 を送信しました。送信者が最終的に 3001 しか受信しなかった場合、3001 より前のデータはすべて受信されたことを意味します。これは、確認シリアルナンバーの意味(確定)
3. 部分的な確認が失われるか、応答が与えられないことを許容します。
部分損失の確認:たとえば、送信者が 1000、2000、3000 を送信し、受信者がそれを受信して送信者に応答した場合、送信者は最終的に 3001 しか受信しませんでしたが、現時点では問題ありません。 3001より前のデータを受信しました
応答なし: 送信者が受信者に 1000、2000、および 3000を送信し、受信者が 1000、3000 のみを受信した場合、 2000 が受信されなかったため、受信者は 1001 で送信者に応答することしかできませんが、3001 では応答できません。
4.なぜ2 つのフィールド (シーケンス番号と確認シーケンス番号) があるのですか?
送信時にはシリアル番号を使用し、受信時には確認用のシリアル番号を使用します。なぜ 2 つ使用するのですか? 1 つで十分ではないでしょうか? 送信されたシーケンス番号を確認シーケンス番号として使用しても問題ありませんか?
TCP は全二重であり、どちらの側でも受信または送信できること を知っておく必要があります。区別しないと送信データと受信データが混在してしまいます。たとえば、送信者は受信者が送信したデータを応答として扱うことも、相手の応答を送信データとして扱うこともできます。
5. 順序を確認するにはどうすればよいですか? たとえば、送信者が 123 を送信した場合、受信者は 231 を受信する可能性がありますか? どちらの当事者もシーケンス番号を含むメッセージを受信し、受信後、データの順序を確保するために並べ替えおよび再編成されます。データが正しい順序でアプリケーション層に配信されるようにします。
16ビットウィンドウサイズ
16 ビットのウィンドウ サイズは、データ受信時に受信機が利用できるバッファ サイズを表します。このフィールドは、送信側が確認応答を待たずに受信側に継続的に送信できるデータの最大量を示します。
バッファ
まず、TCP は全二重でもあります。つまり、データを送信しながらデータを受信することもできます。
TCP の送信側と受信側には送信バッファと受信バッファがありますが、UDP には送信バッファがありません。
フロー制御
受信側のデータ処理速度には限界があり、送信側の送信速度が速すぎると受信側のバッファが一杯になってしまいますが、このとき送信側が送信を続けるとパケットロスが発生し、パケットロスや再送などが連鎖的に起こり、効率が低下します。
したがって、TCP は、受信側の処理能力に基づいて送信側の送信速度を決定することをサポートしており、このメカニズムはフロー制御と呼ばれます。
データが送信されるたびに、受信バッファーはいっぱいになり、受信バッファーの残りのスペースは 16 ビット ウィンドウ サイズになります。
- 受信側は、TCPヘッダーの「16ビットウィンドウサイズ」フィールドに受信可能なバッファサイズを入れ、ACK側で送信側に通知します。
- ウィンドウ サイズフィールドが大きいほど、ネットワークのスループットが高くなります。
- 受信側はバッファがほぼいっぱいであることを検出すると、ウィンドウ サイズをより小さい値に設定し、送信側に通知します。送信側はこのウィンドウを受信した後、送信速度を遅くします。
- 受信側のバッファがいっぱいの場合、ウィンドウは 0 に設定されます。この時点で、送信側はデータを送信しませんが、受信側が送信側に通知できるように、定期的にウィンドウ検出データ セグメントを送信する必要があります。ウィンドウのサイズ。
6つの旗
TCP プロトコルでは、TCP ヘッダーにはいくつかのフラグ (Flags) が含まれており、通信プロセス中に特定の制御情報とステータスを伝達するために使用されます。
合計 6 つのフラグ、つまり URG、ACK、PSH、RST、SYN、および FIN があります。 最初に、いくつかの一般的なフラグについて説明します。
- ACK: 確認応答フラグ。メッセージがメッセージに応答する特性を持つ場合、このフラグは 1 に設定されます。このフラグは、最初の接続要求を除き、ほとんどのネットワーク パケットで 1 に設定されます。
- SYN: 接続要求を確立します。SYN フラグが設定されている場合、接続を開始する側 (通常はクライアント) が接続を確立してシーケンス番号を初期化したいことを示します。
- FIN: 切断要求。FIN フラグが設定されている場合、送信者が接続を閉じてデータを送信しないことを望んでいることを示します。
- URG: 緊急ポインター フィールドが有効かどうかを示します。URG フラグが設定されている場合、セグメント内のデータの一部が緊急データとしてマークされており、できるだけ早く処理する必要があることを示します。
- PSH: 受信側がキャッシュがいっぱいになるのを待たずに、すぐに上位アプリケーションにデータを渡す必要があることを示します。PSH フラグの設定は、受信データがバッファリングせずに直ちに処理されるべきであることを受信機に伝えます。
- RST: 接続のリセットを示します。RST フラグがセットされると、現在の接続が中断されリセットされたことを示します。通常は、データの欠落またはその他のエラー状態が検出された場合に使用されます。
これらのフィールドの特定の機能は、次のメカニズムで徐々に使用されます。
16ビット緊急ポインタ
16 ビットの緊急ポインタは、 URGフラグに関連付けられています。
URG フラグが設定されている場合は、緊急ポインタが有効であることを意味し、メッセージ。
緊急ポインタ:緊急ポインタは、TCP ヘッダー内の 16 ビットを占めるフィールドで、データ セグメント内の緊急データのオフセット位置を示すために使用されます。メッセージの先頭からの緊急データのオフセットを表します。
送信者が URG フラグを設定してデータ セグメントを送信すると、緊急ポインタはデータ セグメント内の緊急データの位置を示します。受信側は、URG フラグを含む TCP データ セグメントを受信すると、緊急ポインタをチェックすることで緊急データの処理をどこから開始するかを決定できます。
たとえば、テキストのデータが 100 バイトで、緊急ポインタのオフセットが 50 の場合、受信機は緊急データを読み取るときに 50 バイト目から読み取りを開始します。
★握手3回、手を振る4回
つながりを理解する方法
将来的に多数のクライアントがサーバーに接続する可能性があるため、サーバーには多数の接続が必要です。前述したように、オペレーティング システム OS はこれらの接続を管理する必要があります。つまり、最初に管理し、次に整理します 。リンクをデータ構造に抽象化し、それらを何らかのデータ構造で管理します。
いわゆる接続: これは本質的にカーネルのデータ構造であり、接続が正常に確立されると、対応する接続オブジェクトがメモリ内に作成されます。次に、これらのオブジェクトをリンク リストなどの何らかのデータ構造に編成します。
したがって、これらの接続を維持するにはコスト(メモリ + CPU) がかかります。
スリーウェイ ハンドシェイクを理解する方法
スリーウェイ ハンドシェイク: クライアントはまずフラグ ビット SYN を含むリクエストをサーバーに送信し、サーバーがそれを受信した後、SYN (接続) + ACK (確認) リクエストをクライアントに送信します。このとき、クライアントはすでに接続が確立されたと判断し(サービス側はすでにデータを正常に送受信できると判断し)、再度 ACK(サーバーが送信した SYN 接続要求を確認する)応答をサーバーに送信します。
フローチャートは次のとおりです。
ここで疑問がありますが、なぜ1回ではなく2回でもなく、3回握手をする必要があるのでしょうか?5回くらいかな?
ここには 2 つのポイントがあります。1. 双方の全二重を検証するには、双方がデータを正常に送受信できることを確認します。
最初のハンドシェイクで、クライアントはサーバーに SYN を送信し、サーバーが SYN を受信して SYN+ACK で応答できれば、クライアントの送受信能力は正常である と判断できます (クライアントが SYN を送信した後であるため) 、サーバーのACK確認を受信しますが、SYNを送信した後、クライアントからのACK応答を受信していないため、サーバーが正常であるかどうかを判断できません);クライアントがサーバーにACKリクエストを送信し、がサーバーに受信されます(サーバーが ACK 確認を受信します)。これは、サーバーの送受信機能が正常であることを示します。
2. サーバーセキュリティのため
ハンドシェイクが 1 回しかない場合、サーバーは攻撃に対して脆弱になる可能性があります。たとえば、ホストがサーバーに同時に大量の SYN リクエストを送信する場合、最初に述べたように、これらの接続は維持する必要があり、維持にはリソースが必要です。したがって、これはサーバー リソースを大量に占有し、麻痺を引き起こすため、お勧めできません。これはSYN フラッド攻撃です。
ハンドシェイクが 2 回のみの場合、最初のハンドシェイクでサーバー接続が確立された後、2 回目のリクエストがクライアントに送信され、クライアントは接続を直接破棄できます。これにより、サーバーは引き続き接続を維持しますが、クライアントが接続を行わない場合でも、大量の SYN 接続要求を送信することは可能ですが、これは現実的ではありません。
スリーウェイ ハンドシェイク中のみ、クライアントも接続を確認した場合にのみ、正常に接続できます。このようにして、クライアントはサーバーと同じリソース消費を負担することになります。クライアントが接続要求を送信するときは常に、サーバーがメンテナンスを実行する前に、クライアントはサーバーに正常に接続する必要があります。このように、サーバーもこれらの接続を維持しますが、同じ数の接続がそれ自体のホストでも維持されます。したがって、これにより攻撃を効果的に防ぐことができます。
ここで問題があり、2 回目のハンドシェイクが完了すると、クライアントはハンドシェイクが確立されたと判断し、データの送信を開始します。同時にクライアントはサーバーに ACK メッセージを送信しますが、この時点で何らかの理由でサーバーが ACK を受信しない場合、サーバーは接続を確立できません。サーバーは接続を確立していないのに、クライアントは接続が確立されていると認識してデータの送信を開始すると、間違いなく問題が発生します。
したがって、このとき、サーバーは、クライアントとの接続が再確立されたことを示すRST (再接続)フラグを含むヘッダーをクライアントに送信し、このとき、クライアントとサーバーは 3 方向通信を再開します。ハンドシェーク。
また、受信側の受信バッファが満杯でこれ以上データを受信できないと想定される状況もある。このとき、16 ビット ウィンドウ サイズは 0 に設定され、送信者にメッセージを送信しないように指示します。
その後、送信者は、受信者がデータを受信できるかどうかを確認するために、その都度空のデータ メッセージを送信しますが、受信者の受信バッファが常にいっぱいの場合は、メッセージを受信できません。このとき、PSHフラグを付けたメッセージ、現在のデータをできるだけ早く上位層に届けるよう相手に伝えます。
4つの波を理解する方法
4 つの波は、TCP 接続を終了するプロセスを指します。これは、接続を正常に閉じるための TCP プロトコルのメカニズムです。4回手を振る具体的な手順は以下の通りです。
-
クライアントは接続切断メッセージを送信します。クライアントはまず接続メッセージをサーバーに送信し、フラグ ビットにFIN (終了) フラグが設定され、クライアントがデータを送信しないことを示します。次に、FIN_WAIT_1状態に入り、サーバーからの確認を待ちます。
-
サーバーは確認メッセージを送信します。サーバーはクライアントの接続切断メッセージを受信した後、応答として確認メッセージを送信します。サーバーの確認メッセージのフラグ ビットにはACKが含まれます。ACK は、クライアントの接続切断メッセージの受信の確認を示し、サーバーはCLOSE_WAIT状態に入ります。
-
サーバーは接続切断メッセージを送信します。サーバーは、クライアントとの接続が終了したことを確認した後、フラグ ビットに FIN フラグを指定した接続切断メッセージを送信します。これは、サーバーがデータを送信しなくなったことを意味します。サーバーはLAST_ACK状態に入り、クライアントからの確認を待ちます。
-
クライアントは確認メッセージを送信します。クライアントはサーバーの接続切断メッセージを受信した後、確認として確認メッセージを送信します。ACK フラグは、クライアントがサーバーの接続解放メッセージの受信を確認したことを示します。このようにして、クライアントとサーバーの両方が、相手が接続の終了を確認したことを知ります。
同時に、クライアントが確認メッセージを送信すると、TIME_WAIT状態に入り、遅延する可能性のあるメッセージの到着を待ちます。一定時間待機した後、メッセージが受信されない場合、クライアントは接続を閉じ、4 波の Wave プロセス全体を完了します。
なぜTIME_WAITが必要なのでしょうか?
積極的に切断する側が 3 つのウェーブを完了すると、TIME_WAIT 状態になり、一定期間待機してから終了することがわかっています。これはなぜですか?
4 ウェイ ハンドシェイクが完了しても、アクティブに切断する側は一定期間 TIME_WAIT 状態を維持する必要があることを知っておく必要があります。この状態 (TIME_WAIT) では、接続は解放されていますが、アドレス情報の IP とポートは保持されません。まだ占領されています。
TCP プロトコルにおける TIME_WAIT 状態の役割は、TCP 接続が確実に閉じられるようにすることです。
TCP 接続の一方の当事者 (通常、クロージャを積極的に開始した当事者) が最後の ACK 確認メッセージを送信すると、TIME_WAIT状態になり、一定時間待機します (通常、最大セグメント生存時間 (MSL) の 2 倍)。 。この期間中、接続は遅延パケットを受信することができます。
MSL は、TCP メッセージの最大生存時間です。したがって、TIME_WAIT が 2MSL 持続する場合、両送信方向の未受信または遅延メッセージ セグメントが確実に消滅することができます(そうしないと、サーバーがすぐに再起動し、
遅延データからのメッセージを受信する可能性があります)。前のプロセスを実行していますが、以下のポイント 2 に示すように、このデータは間違っている可能性があります)TIME_WAIT 状態が存在する主な理由は次のとおりです。
ピアが最終確認応答を受信することを確認します。TCP の 4 つのウェーブの間に、最後の ACK 確認メッセージがネットワーク上で遅延または失われる可能性があります。送信者が接続をすぐに閉じると、この確認がピアに届かず、ピアが接続が適切に閉じられなかったと誤って認識される可能性があります。TIME_WAIT 状態に入ることにより、送信者は一定期間待機して、ピアが最終確認応答を確実に受信できるようになります。
ネットワーク内で滞留したパケットの処理: ネットワークでは、ネットワーク遅延、ルーターのキャッシュ、または再送信により、パケットが一定期間滞留する可能性があります。接続を閉じた後、すぐに同じ IP アドレスとポート番号を使用して接続を再作成すると、以前に取り残されたパケットを受信し、新しい接続に属するデータとして誤って解釈される可能性があります。TIME_WAIT 状態に入ることにより、新しい接続上の滞留パケットによる干渉を排除できます。
接続の一意性を確保する: TIME_WAIT 状態は、この状態での接続の一意性も保証します。TIME_WAIT 状態の間、対応する TCP 接続の 4 つ (送信元 IP、送信元ポート、宛先 IP、宛先ポート) が一定期間保持されます。これにより、新しく確立された接続がその接続と同じ 4 倍になることを防ぎ、混乱や競合の可能性を回避します。
TIME_WAIT 状態は一部のリソースを占有し、接続で使用されるポートの解放を遅らせますが、これは接続の信頼性、完全性、および一意性を維持するのに役立つ TCP プロトコルの重要なメカニズムです。
サーバーが突然ハングアップするなど、別の問題もあります。つまり、サーバーが積極的に切断する側です。このとき、サーバーは CLOSE_WAIT 状態になり、再接続するまで 一定時間待機する必要があります。状態、IP およびポートが占有されています。これによりバインドが失敗し、サーバーは再接続できなくなります。ダブルイレブンのような消費量の多いフェスでこれが起こってしまうと、今回の損失は非常に大きなものになります。したがって、時間の終了を待たずに TIME_WAIT 状態ですぐに再起動するには、setsockopt() を使用する必要があります。
setsockopt()
ソケット記述子オプション SO_REUSEADDR を 1 に設定します。これは、同じポート番号を持つが異なる IP アドレスを持つ複数のソケット記述子を作成できることを意味します。
つまり、setsockopt() は一時的な救済に使用でき、元の IP アドレスとポートは TIME_WAIT 状態で使用できないため、サーバーは元の IP アドレスとポートを使用してすぐに再起動できます。
TCPの信頼性メカニズム
確認応答メカニズム(補足)
この内容のほとんどは上記の記事で紹介されていますが、ここで内容の一部を説明します。
データセグメント: TCPプロトコルヘッダーとデータ部で構成されます。
先ほども述べたように、TCP通信はシーケンス番号を相手に送り、相手はそれを自分に返してシーケンス番号を確認します。では、このシリアル番号とは一体何なのでしょうか?
相手にメッセージを送信するたびに、実際にはデータを自分の送信バッファにコピーしてから、相手の受信バッファに送信します。このバッファは char 型の配列とみなすことができ、データの各バイトには番号が付けられます。
32 ビットのシーケンス番号は、TCP データ ストリーム内の各バイトを識別するために使用されるシーケンス番号です。
たとえば、100 バイトのデータを送信する場合、各データには 100 まで個別に番号が付けられます。受信者は 100 を受信すると、送信者に確認シーケンス番号 101 を送信します。これは、送信者が次回 101 から始まるシーケンス番号を送信することを期待していることを示します。
タイムアウト再送信メカニズム
タイムアウト再送信メカニズムは、パケット損失に対処するために TCP プロトコルで使用されるメカニズムです。送信者がデータ セグメントを送信すると、タイマー (再送信タイマーと呼ばれる) を開始し、受信者が対応する確認シーケンス番号を送信するのを待ちます。タイマーが期限切れになる前に確認応答が受信されなかった場合、送信者はパケットが失われたとみなし、パケットを再送信します。
データ パケット損失には 2 つの状況があります: 1 つは、送信者によって送信されたデータが失われ、受信者がデータを受信できず応答できなくなることです。もう 1 つは、受信者によって送信されたデータが失われ、送信者が応答できなくなることです。 ACK、確認シーケンス番号、その他の情報を受信しません。
最初のケースでは、指定された時間内にデータが受信されなかった場合、送信者はデータを再送信できます。
2 番目のケースでは、受信者はデータを受信しましたが、ACK 確認が失われます。このとき、送信者は同じデータを再送信するため、サーバーは重複データを受信することになり、サーバーは重複を排除する必要があるため、このようになります。どうやって解決すればいいでしょうか?
このとき、先ほど述べたシリアル番号が登場し、サーバーは受け取ったシリアル番号を元に受信を行います。シリアル番号がすでに所有されている場合、データのこの部分は直接破棄され、重複排除が完了します。
では、タイムアウトの適切なサイズはどれくらいでしょうか?
最も理想的な状況では、「この時間内に確認応答が返される必要がある」という最低限の時間を見つけます。ただし、この時間の長さはネットワーク環境によって異なります。タイムアウトを長く設定しすぎると、全体的な影響が
生じます。再送効率。
タイムアウトの設定が短すぎると、重複したパケットが頻繁に送信される可能性があります。
あらゆる環境で高パフォーマンスの通信を保証するために、TCP は最大タイムアウト (500 ミリ秒など)を動的に計算します。
1 回再送信しても応答が得られない場合は、2*500ms 待ってから再送信し、
それでも応答が得られない場合は、4*500ms 待ってから再送信するというように、一定の数まで指数関数的に増加していきます
。再送信回数に達しても応答がない場合、TC はネットワークまたは相手ホストに異常があると判断し、接続を強制的に切断します。
流量制御機構(補助)
基本的には上記ですべて説明しましたが、上にスクロールして検索するだけです。ここに別の質問があります。
フロー制御を行う際、初めて相手の受信能力(受信バッファの残量)を知るにはどうすればよいでしょうか?
初めてデータを送信するときに相手の受信能力が分からず、相手の受信能力を超えるデータを直接送信してしまったらどうなるでしょうか?
実際、データを初めて送信することは、メッセージを初めて送信することを意味するわけではないことを知っておく必要があります。通信 の最初の3 回のハンドシェイク中に、双方からのメッセージが交換されます。これには、相手側の受信バッファ サイズ、つまり16 ビット ウィンドウ サイズが含まれます。したがって、TCP の 3 ウェイ ハンドシェイク中に、双方の受信能力がすでにわかっています。
相手の受信バッファがいっぱいでデータを受け入れられなくなった場合、2 つの方法で通知を検出します。
1.送信側は、相手の受信バッファがデータを受信できるかどうかを検出するために、ウィンドウ検出パケットを送信することがあります。
2.受信側の受信バッファがデータを受信できるようになると、ウィンドウ更新通知が送信側に送信され、データを送信できることを相手に知らせます。
★引き違い窓
スライディング ウィンドウのワークフロー
上で確認応答メカニズムについて説明しました。送信されるデータ セグメントごとに、ACK 確認応答を行う必要があります。ACKを受信した次のデータ セグメントが送信されます。これには大きな欠点があります。つまり、パフォーマンスが低下します。特に、これは次のような場合です。データの往復時間が長くなります。
この 1 回送信 1 回受信方式のパフォーマンスは低いため、複数のデータを一度に送信する(実際には複数のセグメントの待ち時間をオーバーラップさせる)ことでパフォーマンスを大幅に向上させることができます。
では、送信されるデータを制御するにはどうすればよいでしょうか? ここではスライディング ウィンドウが使用されます。
スライディング ウィンドウは、送信者と受信者間のデータ送信を制御するために使用される TCP プロトコルのメカニズムです。これは、送信されたが確認されていないデータ シーケンス番号を管理するために使用される動的データ バッファーです。
ウィンドウサイズとは、送信済みで未確認のデータの最大値を指します(現時点で判明しているものは輻輳制御により修正されます)。
つまり、スライディング ウィンドウは次のことを望んでいます: 1. より多くのデータを相手に送信すること; 2. 相手がデータを受信する時間を確実に確保すること。
送信されたデータが多すぎて、相手がそれを受信する時間がありませんでした。相手の受信能力の範囲内で、できるだけ多くのデータの送信を制御する必要があります。
したがって、スライディング ウィンドウの本質は、送信者が一度に相手にデータの上限をプッシュできることです。
この上限は相手の受信能力によって決まります。
プロセスは次のとおりです。
4 つのデータ セグメントを送信する場合 (特定の状況によっては、必ずしも 4 つのデータ セグメントである必要はありません)、ACK を待つ必要はなく、直接送信するだけです。
ACK を受信した後、スライディング ウィンドウは逆方向に移動し、5 番目のセグメントのデータの送信を継続します。以下同様です。
このスライディング ウィンドウを維持するために、オペレーティング システム カーネルは送信バッファを開いて、現在応答のないデータを記録する必要があります。 ;バッファから削除できるのは確認応答 (ACK) のみです。つまり、スライディング ウィンドウは独自の送信バッファ内にあり、独自の送信バッファの一部です。
ウィンドウが大きいほど、より多くのデータを送信できるため、ネットワークのスループットが高くなります。
スライディング ウィンドウの一般的な問題
前述したように、スライディング ウィンドウは本質的に、送信側が一度に相手にデータを送信できる上限値、つまりウィンドウ サイズですが、実際には 2 つのポインタによって維持されます。 win_end はスライディング ウィンドウ、win_end はスライディング ウィンドウの終了位置を指します Win_end = win_start + 逆受信能力。
を更新するたびに、win_start = 受信した応答メッセージの確認シーケンス番号、 win_end = win_start + 受信した応答メッセージのウィンドウ サイズを設定するだけで済みます。
いくつかの質問に答えてください。
1. スライディング ウィンドウは毎回右に移動する必要がありますか?
答えは必ずしもそうではありません。相手の受信バッファが上位層に渡されていないものとみなされ、win_endは右に移動せず変化しない。
2. スライディング ウィンドウを 0 にすることはできますか?
答えは「はい」です。win_start = win_end の場合、スライディング ウィンドウは 0 です。これは通常、現時点で相手の受信バッファがフル状態であることを意味します。
3. 最初の応答メッセージは受信されないが、中間の応答メッセージは受信される場合、これは可能であり、影響を受けますか?
答えは「かもしれない」ですが、それは問題ではありません。まず、中間確認メッセージ内の確認シーケンス番号により、このシーケンス番号より前のすべてのメッセージが正常に受信されたことが保証されます。以前のメッセージがすべて受信されていない場合、この確認メッセージは送信されません。
たとえば、スライディング ウィンドウが 1000 ~ 5000 になりました。確認番号 3001 を受信しましたが、確認番号 2001 を受信しませんでした。ただし、影響はありません。これは、3001 より前のデータは受信しましたが、確認メッセージが表示されないことを意味します。失われます。
(写真の例には、先ほど引用した例とは異なるデータが含まれていることに注意してください。混乱しないようにしてください)
4.タイムアウト再送の意味: 応答が受信されない場合、データを一時的に保存する必要があります。
実際には 1000 ~ 2000 が失われ、2001 ~ 3000、3001 ~ 4000、4001 ~ 5000 のデータは受信されたと仮定します。ただし、メッセージのこのセグメントが受信されていないため、受信者は最終的に 1001 しか返せません。たとえ後続のものをすべて受信した後でも、1001 のみを返すことができます。このときタイムアウト時間が経過した場合は、一時保存したデータを再送信に使用します。
高速再送信
- 特定のセグメント (たとえば、1 ~ 1000) が失われると、送信者は常に 1001 のような ACK を受信します。これは、送信者に「私が欲しいのは 1001 です」と思い出させるようなものです。
- 送信ホストが同じ「1001」応答を 3 回続けて受信すると、対応するデータ 1001 ~ 2000 を再送信します。
- このとき、受信側が 1001 を受信した後、再度返される ACK は 7001 (2001 - 7000 であるため) ですが、受信側は実際に以前に受信しており、受信側オペレーティング システム カーネルの受信バッファに置かれていました。
この仕組みを「高速再送制御」(「高速再送」ともいう)といい、同一の確認応答メッセージを3回受信したらすぐに再送するというものです。
では、高速再送信があるのに、なぜタイムアウト再送信が必要なのでしょうか? 高速再送信は高速ではないのに、なぜ待たなければならないのでしょうか?
まず、高速再送信は、同一の応答メッセージを 3 つ連続して受信した後にトリガーされるメカニズムであり、データ セグメントを 2 つしか送信しない場合、再送信する方法はありません。
第 2 に、応答メッセージも失われる可能性があり、時間内に再送信できなくなります。
輻輳制御
TCP にはスライディング ウィンドウというキラーがあり、大量のデータを効率的かつ確実に送信できますが、最初に大量のデータを送信すると、依然として問題が発生する可能性があります。
私たちはこれまで、クライアントとサーバー間の送受信の問題など、両端の問題を解決してきましたが、送信中のネットワーク関連の問題についてはまだ話し合っていませんでした。
ネットワーク上には多数のコンピュータが存在し、現在のネットワーク状況はすでに比較的混雑している可能性があるため、現在のネットワーク状況を把握せずに、むやみに大量のデータを送信すると、事態が悪化する可能性があります。
TCP では、最初に少量のデータを送信し、パスを探索し、現在のネットワークの輻輳状態を調べて、データを送信する速度を決定するスロー スタート メカニズムが導入されています。
ここでは、輻輳ウィンドウ、輻輳ウィンドウの概念 を参照します。単一のホストが一度に大量のデータをネットワークに送信すると、ネットワーク輻輳が発生する可能性があります。
したがって、このときのスライディングウィンドウ値の大きさは、相手の受信能力だけでなく、輻輳ウィンドウの大きさにも依存する。
スライディングウィンドウの値は、輻輳ウィンドウと相手のウィンドウサイズ(受信能力)の最小値である。
送信開始時、定義された輻輳ウィンドウ サイズは 1 です
ACK 応答を受信するたびに輻輳ウィンドウが +1 され、輻輳ウィンドウと受信側のウィンドウ サイズの最小値が実際に送信されるウィンドウ値となります。
上記の輻輳ウィンドウの増加率によると、これは指数関数的な「スロー スタート」であり、最初はいっぱいですが、増加率は非常に速いです。
急激に増加しないようにするには、輻輳ウィンドウを単純に 2 倍にすることはできません。
ここでは、スロー スタートと呼ばれるしきい値
が導入されています。輻輳ウィンドウが このしきい値を超えると、指数関数的に増加することはなくなり、直線的に増加します。
- TCP が開始されるとき、スロー スタートしきい値は毎回ウィンドウの最大値と等しくなります。
- タイムアウトと再送信のたびに、スロー スタートしきい値は元の値の半分になり、輻輳ウィンドウは 1 にリセットされます。
少量のパケット損失の場合、タイムアウト再送信をトリガーします。大量のパケット損失の場合、ネットワークが混雑していると考えられます
。TCP 通信が開始されると、ネットワークのスループットは徐々に増加します。ネットワークが混雑すると、スループットはすぐに増加します。減少;
輻輳制御は、最終的には TCP プロトコルです
1. できるだけ早くデータを相手に送信します。 2. ただし、ネットワークに過度の圧力をかけるような妥協は避けてください。
遅延応答
データを受信したホストがデータ受信直後に ACK 応答を返した場合、この時点では上位層がデータを受信する時間がないため、応答ウィンドウは比較的小さくなる可能性があります。相手に返される受信能力(16ビットウィンドウサイズ)は小さくなります。
ネットワーク伝送では、 1 回の IO内のデータが多ければ多いほど、スループットが向上し、効率が向上することを知っておく必要があります。
- 受信側のバッファが 1M であるとすると、一度に 500K のデータを受信し、応答が即時であれば返されるウィンドウは 500K になります。
- しかし実際には、処理側の処理速度は非常に速く、10ms 以内に 500K のデータがバッファから消費されます。
- この場合、受信側の処理はまだ限界に達しておらず、ウィンドウを拡大しても処理できるため、受信側の処理が限界に達することはありません。
- 受信側が応答するまでにしばらく待機する場合(たとえば、応答するまでに 200ms 待機する)、この時点で返されるウィンドウ サイズは 1M です。
ウィンドウが大きいほど、ネットワークのスループットが向上し、伝送効率が高くなることに注意してください。私たちの目標は、ネットワークが混雑しないようにしながら伝送効率を最大化することです。
では、すべてのパケットに遅延応答できるでしょうか?
答えは明らかにそうではありません。理由は 2 つあります。
1. 数量制限: N パケットごとに応答します。初めてパケットが来たときは応答せず、2回目のパケットが来たときに応答を返すとします。2 番目のパケットを待つことで、データを上位層に配信する時間が得られます。
2. 制限時間: 最大遅延時間を超えた場合は 1 回応答します。初めてパケットが来てから一定時間が経過すると、当然タイムアウト再送時間を越えることはできず、相手に応答を返しますが、この時間であればデータは十分に届けられます。上の層へ。
具体的な数とタイムアウト期間はオペレーティング システムによって異なりますが、一般的に N は 2 で、タイムアウト期間 (タイムアウト再送信の場合を除く) は 200 ミリ秒です。
便乗返信
クライアントとサーバーは基本的に送受信することがわかりました。送受信のたびに相手にACK応答を返す必要がありますが、単純にACK応答を送ってから毎回データを送信すると効率が悪くなります。したがって、メッセージを送信しているので、それと一緒に ACK を送信するだけで済みます。つまり、データを含むメッセージの ACK フラグ ビットを 1 に設定します。この方法では、メッセージを 1 回送信するだけで済みます (基本的には常にこれがデフォルトになっています)。
TCPの概要
TCP がなぜこれほど複雑なのかというと、パフォーマンスを可能な限り向上させながら信頼性を確保する必要があるからです。
信頼性を確保する仕組み:
- チェックサム
- シリアルナンバー(順次入荷)
- 応答を確認する
- タイムアウトして再送信する
- 接続管理
- フロー制御
- 輻輳制御
パフォーマンスを向上させるメカニズム:
- スライドウィンドウ
- 高速再送信
- 遅延応答
- 便乗返信
バイトストリーム指向を理解する
まずは次のプロセスを理解しましょう。
TCPソケットを作成し、カーネル内に送信バッファと受信バッファを作成します。
- write を呼び出すと、データはまず送信バッファに書き込まれます。
- 送信バイト数が長すぎる場合は、複数の TCP パケットに分割されて送信されます。
- 送信バイト数が短すぎる場合は、バッファ長がほぼ同じになるまでバッファ内で待機するか、別の適切なタイミングで送信します。
- データを受信するとき、データはネットワーク カード ドライバーからカーネルの受信バッファーにも到達します。
- その後、アプリケーションは read を呼び出して受信バッファからデータを取得できます。
バッファーの存在により、TCP プログラムの読み取りと書き込みは1 対 1 で一致する必要はありません。次に例を示します。
- 100 バイトのデータを書き込む場合、write を 1 回呼び出して 100 バイトを書き込むことも、write を 100 回呼び出して毎回 1 バイトを書き込むこともできますし、TCP ポリシーなどにより途中で送信することもできます。
- 100バイトのデータを読み込む場合は、書き込み方法を考える必要はなく、100バイトずつ読み込むことも、2バイトずつ読み出すことを50回繰り返すこともできます。
これは UDP のデータグラムとは異なります。UDP は、速達を送るのと同じように、数回書き込んで送信します。何件送っても、ここでいくつか受け取ります。1 件しか受け取らない場合は、必ず送信する必要があることを意味します。1 件あります。
TCPはバケツで水を汲むようなもので、パイプで注いでも、ボウルに次々と水を注いでも、どのように仕上げても、最終的に必要なのはバケツの中の水だけです。これはバイトストリーム指向です。
TCPはデータを相手に渡すだけで、データをどうやって解析するかというと、上位のアプリケーション層のプロトコルがやっていることで、安全かつ確実にデータを相手に送れればいいのです。したがって、アプリケーション層の http プロトコルなどが存在します。
粘着性のあるバッグの問題
先ほども言いました。TCPはバイトストリーム指向であり、受信者は送信者が送信した一連のバイトデータを受け取りますが、アプリケーションプログラムはその一連のバイトデータを見て、どの部分がどの部分から始まるのかを知りません。データパケット。
では、粘着性のあるパッケージの問題を回避するにはどうすればよいでしょうか? それは2 つのパッケージ間の境界を明確にするという1 つの文に要約されます。
- 固定長パケットの場合は、毎回固定サイズで読み取られるようにしてください。たとえば、UDP の場合、ヘッダーには 16 ビットの UDP 長があります。これに基づいてデータを抽出できます。
- 可変長パケットの場合、パケット ヘッダーの位置にあるパケットの全長のフィールドについて合意できるため、パケットの終了位置がわかります。
- 可変長パケットの場合、パケット間に明確な区切り文字を使用することもできます(区切り文字がテキストと競合しない限り、アプリケーション層プロトコルはプログラマによって決定されます)。
では、UDP にはスティッキーな問題があるのでしょうか?
答えは存在しません、先ほども述べたように、UDP ヘッダーは 16 ビットの UDP 長を持っており、これによりデータの全長とデータの境界を明確にすることができます。UDP を使用する場合、完全な UDP メッセージを受信するか受信しないかのどちらかになり、「半分」の状況は存在しません。
ここでは、TCP プロトコルの関連内容について説明し、TCP プロトコル ヘッダーの理解と役割から、信頼性を実現するための TCP の背後にあるさまざまなメカニズムまで、非常に詳細に説明しました。この記事を読む忍耐力があれば、間違いなく多くのことを得ることができるでしょう。