myesn

myEsn2E9

hi
github

4.1 TCP 三次ハンドシェイクと四次ハンドシェイク

TCP 基本认识#

TCP ヘッダフォーマットには何がありますか?#

色分けされたフィールドはこの記事と関連性が高いことを示します:
image

  • ** シーケンス番号:** 接続を確立する際にコンピュータによって生成されるランダムな数値がその初期値として使用され、SYN パケットを介して受信側ホストに送信されます。データを送信するたびに、その「データバイト数」のサイズが「累加」されます。ネットワークパケットの順序の問題を解決するために使用されます。
  • ** 確認応答番号:** 次に「期待」されるデータのシーケンス番号を指し、送信側はこの確認応答を受け取った後、このシーケンス番号以前のデータが正常に受信されたと見なすことができます。パケットロスの問題を解決するために使用されます。
  • 制御ビット:
    • **ACK:** このビットが 1 の場合、「確認応答」のフィールドが有効になり、TCP では最初の接続を確立する際の SYN パケットを除いて、このビットは必ず 1 に設定されなければなりません。
    • **RST:** このビットが 1 の場合、TCP 接続に異常が発生し、強制的に接続を切断する必要があることを示します。
    • **SYN:** このビットが 1 の場合、接続を確立したいことを示し、その「シーケンス番号」のフィールドでシーケンス番号の初期値を設定します。
    • **FIN:** このビットが 1 の場合、今後データを送信しないことを示し、接続を切断したいことを示します。通信が終了し接続を切断したい場合、通信の両方のホスト間で FIN ビットが 1 の TCP セグメントを相互に交換できます。

なぜ TCP プロトコルが必要なのか? TCP はどの層で動作するのか?#

ネットワークデータパケットの信頼性(配信、順次配信、データの完全性)を保証する必要がある場合、トランスポート層の TCP プロトコルが担当します。

TCPトランスポート層で動作する信頼性のあるデータ転送サービスであり、受信側が受け取るネットワークパケットが損傷なく、間隔なく、冗長性がなく、順序通りであることを保証します。
image

TCP とは何ですか?#

TCP は接続指向の、信頼性のある、バイトストリームベースのトランスポート層通信プロトコルです。
image

  • ** 接続指向:** 必ず「一対一」で接続しなければならず、UDP プロトコルのように一つのホストが同時に複数のホストにメッセージを送信することはできません。つまり、一対多は実現できません。
  • 信頼性:ネットワークリンクにどのような変化があっても、TCP はメッセージが必ず受信側に到達することを保証します
  • バイトストリーム:ユーザーメッセージが TCP プロトコルを介して送信されると、メッセージはオペレーティングシステムによって「パッケージ化」され、複数の TCP メッセージに分割される可能性があります。受信側のプログラムが「メッセージの境界」を知らない場合、有効なユーザーメッセージを読み取ることができません。また、TCP メッセージは「順序通り」であり、「前の」TCP メッセージが受信されていない場合、たとえ後の TCP メッセージが先に受信されても、アプリケーション層に処理を渡すことはできません。同時に、「重複」した TCP メッセージは自動的に破棄されます

TCP 接続とは何ですか?#

RFC 793 は「接続」を次のように定義しています:

Connections: The reliability and flow control mechanisms described above require that TCPs initialize and maintain certain status information for each data stream. The combination of this information, including sockets, sequence numbers, and window sizes, is called a connection.

つまり、信頼性とフロー制御を保証するために維持される特定の状態情報の組み合わせであり、これにはソケット、シーケンス番号、ウィンドウサイズが含まれ、接続と呼ばれます。
image

したがって、TCP 接続を確立するには、クライアントとサーバーが上記の 3 つの情報に関して合意する必要があります:

  1. ** ソケット:**IP アドレスとポート番号で構成されます。
  2. ** シーケンス番号:** メッセージの順序の問題を解決します。
  3. ** ウィンドウサイズ:** フロー制御に使用されます。

TCP 接続を一意に特定するにはどうすればよいですか?#

TCP の 4 つのタプルは、接続を一意に特定できます:

  • ソースアドレス
  • ソースポート
  • 宛先アドレス
  • 宛先ポート
    image

ソースアドレスと宛先アドレスのフィールド(32 ビット)はIP ヘッダーにあり、IP プロトコルを介して相手ホストにメッセージを送信する役割を果たします。

ソースポートと宛先ポートのフィールド(16 ビット)はTCP ヘッダーにあり、TCP プロトコルがメッセージをどのプロセスに送信すべきかを示します。

UDP と TCP の違いは何ですか?それぞれのアプリケーションシーンは?#

UDP は複雑な制御メカニズムを提供せず、IP を利用して「接続なし」の通信サービスを提供します

UDP プロトコルは非常にシンプルで、ヘッダーはわずか 8 バイト(64 ビット)です。UDP のヘッダー形式は次のとおりです:
image

  • 宛先およびソースポート:主に UDP プロトコルがメッセージをどのプロセスに送信すべきかを示します。
  • パケット長:このフィールドは、UDP ヘッダーの長さとデータの長さの合計を保存します。
  • チェックサム:チェックサムは、信頼性のある UDP ヘッダーとデータを提供するために設計されており、ネットワーク伝送中に損傷を受けた UDP パケットを防ぎます。

TCP と UDP の違い:

  1. 接続
    1. TCP は接続指向のトランスポート層プロトコルであり、データを送信する前に接続を確立する必要があります
    2. UDP は接続を必要とせず、すぐにデータを送信します
  2. サービスオブジェクト
    1. TCP は一対一の二点サービスであり、つまり一つの接続には二つのエンドポイントしかありません
    2. UDP は一対一、一対多、多対多の相互通信をサポートします
  3. 信頼性
    1. TCP はデータを信頼性よく配信し、データはエラーなく、失われず、重複せず、順序通りに到達します
    2. UDP は最大限の努力で配信し、信頼性のあるデータ配信を保証しません。ただし、UDP 転送プロトコルに基づいて信頼性のある転送プロトコルを実装することができます。たとえば、QUIC プロトコルについては、次の記事を参照できます:UDP プロトコルに基づいて信頼性のある転送を実現する方法は?
  4. 輻輳制御、フロー制御
    1. TCP には輻輳制御とフロー制御メカニズムがあり、データ転送の安全性を保証します。
    2. UDP にはそれがなく、ネットワークが非常に混雑していても、UDP の送信速度には影響しません。
  5. ヘッダーオーバーヘッド
    1. TCP ヘッダーの長さは比較的長く、一定のオーバーヘッドがあり、オプションフィールドを使用しない場合、ヘッダーは 20 バイトです。オプションフィールドを使用すると、長さが変わります。
    2. UDP ヘッダーはわずか 8 バイトで、固定されており、オーバーヘッドが少ないです。
  6. 転送方式
    1. TCP はストリーム転送であり、境界がなく、順序と信頼性が保証されています。
    2. UDP はパケットごとに送信され、境界がありますが、パケットが失われたり、順序が乱れる可能性があります。
  7. フラグメンテーションの違い
    1. TCP のデータサイズが MSS(Maximum Segment Size)を超える場合、トランスポート層でフラグメンテーションが行われ、ターゲットホストが受信した後、同様にトランスポート層で TCP パケットが組み立てられます。途中でフラグメントが失われた場合、失われたフラグメントだけを再送信する必要があります。
    2. UDP のデータサイズが MTU(Maximum Transmission Unit)を超える場合、IP 層でフラグメンテーションが行われ、ターゲットホストが受信した後、IP 層でデータを組み立て、その後トランスポート層に渡されます。

TCP と UDP のアプリケーションシーン:
TCP は接続指向であり、データの信頼性のある配信を保証できるため、次のような用途でよく使用されます:

  • FTP ファイル転送
  • HTTP / HTTPS

UDP は接続なしであり、いつでもデータを送信でき、UDP 自体の処理がシンプルで効率的であるため、次のような用途でよく使用されます:

  • パケットの総量が少ない通信、例えば DNS 、SNMP など;
  • ビデオ、音声などのマルチメディア通信;
  • ブロードキャスト通信;

TCP と UDP は同じポートを使用できますか?#

できます。

データリンク層では、MAC アドレスを介してローカルネットワーク内のホストを探します
ネットワーク層では、IP アドレスを介してネットワーク内の相互接続されたホストまたはルーターを探します
トランスポート層では、ポートを介してアドレス指定を行い、同じコンピュータ内で同時に通信する異なるアプリケーションを識別します。

したがって、トランスポート層の「ポート番号」の役割は、同じホスト上の異なるアプリケーションのデータパケットを区別することです

トランスポート層には TCP と UDP の 2 つのトランスポートプロトコルがあり、カーネル内では 2 つの完全に独立したソフトウェアモジュールです。

ホストがデータパケットを受信すると、**IP パケットヘッダーの「プロトコル番号」フィールドを介して、そのデータパケットが TCP/UDP であることを知ることができるため、この情報に基づいてどのモジュール(TCP/UDP)に処理を送るかを決定できます。TCP/UDP モジュールに送信されるメッセージは「ポート番号」に基づいて、どのアプリケーションプログラムに処理を送るかを決定します。
image

したがって、TCP/UDP のそれぞれのポート番号は互いに独立しています。たとえば、TCP に 80 番ポートがある場合、UDP も 80 番ポートを持つことができ、両者は衝突しません。

ポートに関する知識は多くあり、たとえば次のような問題にも関連します:

  • 複数の TCP サービスプロセスが同じポートに同時にバインドできますか?
  • TCP サービスプロセスを再起動する際に「Address in use」というエラーメッセージが表示されるのはなぜですか?また、どうすれば避けられますか?
  • クライアントのポートは再利用できますか?
  • クライアントの TCP 接続が TIME_WAIT 状態になりすぎると、ポートリソースが枯渇して新しい接続を確立できなくなりますか?

上記の問題については、次の記事を参照できます:TCP と UDP は同じポートを使用できますか?

TCP 接続の確立#

TCP の三回のハンドシェイクプロセスはどのようになっていますか?#

TCP接続指向のプロトコルであるため、TCP を使用する前に接続を確立する必要があり、接続の確立は三回のハンドシェイクによって行われます。三回のハンドシェイクのプロセスは次の図のようになります:
image

  1. 最初に、クライアントとサーバーは CLOSE 状態にあります。最初にサーバーが特定のポートをアクティブにリッスンし、LISTEN 状態に入ります。
  2. クライアントはランダムにシーケンス番号(client_isn)を初期化し、このシーケンス番号を TCP ヘッダーの [シーケンス番号] フィールドに置き、SYN フラグを 1 に設定して、サーバーに最初の SYN パケットを送信します。このパケットにはアプリケーション層のデータは含まれていません。その後、クライアントは SYN-SENT 状態に入ります。
    image
  3. サーバーはクライアントの SYN パケットを受信すると、まずサーバーもランダムに自分のシーケンス番号(server_isn)を初期化し、このシーケンス番号を TCP ヘッダーの [シーケンス番号] フィールドに記入します。次に、TCP ヘッダーの [確認応答番号] フィールドに client_isn + 1 を記入し、SYNACK フラグを 1 に設定します。最後に、このパケットをクライアントに送信します。このパケットにはアプリケーション層のデータは含まれていません。その後、サーバーは SYN-RCVD 状態に入ります。
    image
  4. クライアントはサーバーからのパケットを受信した後、サーバーに最後の応答パケットを返します。最初にこの応答パケットの TCP ヘッダーの ACK フラグを 1 に設定し、次に [確認応答番号] フィールドに server_isn + 1 を記入します。最後に、このパケットをサーバーに送信します。このパケットにはクライアントからサーバーへのデータを含めることができます。その後、クライアントは ESTABLISHED 状態に入ります。
    image
  5. サーバーはクライアントの応答パケットを受信した後、ESTABLISHED 状態に入ります。

上記のプロセスから、三回目のハンドシェイクはデータを含むことができ、最初の二回のハンドシェイクはデータを含むことができません
三回のハンドシェイクが完了すると、両者は ESTABLISHED 状態に入り、この時点で接続が確立され、クライアントとサーバーは相互にデータを送信できるようになります。

Linux システムで TCP 状態を確認するにはどうすればよいですか?#

Linux では netstat -napt を使用して TCP の接続状態を確認できます:
image

なぜ三回のハンドシェイクなのか?二回でも四回でもないのか?#

一般的な片面的な回答は、「三回のハンドシェイクの後に、両者が送受信の能力を持つことが保証されるため」です。これは主な理由を述べていません。
前述の TCP 接続の定義を紹介しました:信頼性とフロー制御を維持するために必要な特定の状態情報の組み合わせであり、これには ソケットシーケンス番号ウィンドウサイズが含まれ、接続と呼ばれます。
したがって、重要なのはなぜ三回のハンドシェイクがソケット、シーケンス番号、ウィンドウサイズを初期化し、TCP 接続を確立できるのかです。
次に、三回のハンドシェイクの理由を三つの側面から分析します:

  • 三回のハンドシェイクは、古い重複接続の初期化を防ぐことができます(主な理由)
  • 三回のハンドシェイクは両者の初期シーケンス番号を同期させることができます
  • 三回のハンドシェイクはリソースの浪費を防ぐことができます

理由 1:古い接続を防ぐ#

RFC 793 では、TCP 接続が三回のハンドシェイクを使用する主な理由を次のように述べています:

The principle reason for the three-way handshake is to prevent old duplicate connection initiations from causing confusion.

簡単に言えば、三回のハンドシェイクの主な理由は、古い重複接続の初期化による混乱を防ぐためです
次のシナリオを考えてみましょう。クライアントが最初に SYN(seq = 90)パケットを送信し、その後クライアントがダウンし、さらにこの SYN パケットがネットワークでブロックされ、サーバーは受信しませんでした。次に、クライアントが再起動し、サーバーに接続を再確立し、SYN(seq = 100)パケットを再送信しました(注意!これは SYN の再送信ではなく、再送信された SYN のシーケンス番号は同じです)。
三回のハンドシェイクが古い接続を防ぐ方法を見てみましょう:
image

クライアントが同じ四つのタプルで接続を確立するために連続して複数の SYN(すべて同じ四つのタプル)を送信し、ネットワークが混雑している場合

  • 「古い SYN パケット」が「最新の SYN」パケットよりも早くサーバーに到達した場合、サーバーはクライアントに SYN + ACK パケットを返します。このパケットの確認番号は 91(90+1)です。
  • クライアントはこれを受信し、期待される確認番号は 100 + 1 であり、90 + 1 ではないことを確認し、RST パケットを返します。
  • サーバーは RST パケットを受信すると、接続を解放します。
  • その後、最新の SYN がサーバーに到達すると、クライアントとサーバーは正常に三回のハンドシェイクを完了できます。

上記の「古い SYN パケット」は古い接続と呼ばれます。TCP が三回のハンドシェイクを使用して接続を確立する最も重要な理由は「古い接続」の初期化を防ぐことです

理由 2:両者の初期シーケンス番号を同期させる#

TCP プロトコルの通信の両者は、必ず「シーケンス番号」を維持する必要があります。シーケンス番号は信頼性のある転送の重要な要素です。その役割は次のとおりです:

  • 受信側は重複データを除去できます。
  • 受信側はデータパケットのシーケンス番号に従って順序通りに受信できます。
  • 送信されたデータパケットの中で、どれが相手に受信されたかを識別できます(ACK パケットのシーケンス番号を通じて知ることができます)。

見ての通り、シーケンス番号は TCP 接続において非常に重要な役割を果たします。したがって、クライアントが「初期シーケンス番号」を含む SYN パケットを送信する際、サーバーは ACK 応答パケットを返す必要があります。クライアントの SYN パケットがサーバーによって正常に受信されたことを示すために、サーバーが「初期シーケンス番号」をクライアントに送信する際にも、クライアントの応答を受け取る必要があります。こうして、双方向の初期シーケンス番号が確実に同期されることが保証されます。
image

四回のハンドシェイクでも両者の初期シーケンス番号を確実に同期できますが、二回目と三回目のステップを一回に最適化できるため、三回のハンドシェイクとなります

二回のハンドシェイクは、一方の初期シーケンス番号が相手に正常に受信されたことを保証するだけであり、両者の初期シーケンス番号が確認されることはありません。

理由 3:リソースの浪費を防ぐ#

「二回のハンドシェイク」の場合、クライアントが SYN パケットをネットワークでブロックされ、クライアントが ACK パケットを受信できなかった場合、クライアントは再度 SYN を送信します。三回目のハンドシェイクがないため、サーバーはクライアントが自分の ACK パケットを受信したかどうかを把握できず、サーバーは受信するたびに接続を確立する必要があります。これにより、どのような状況が発生するでしょうか?

クライアントが SYN パケットをネットワークでブロックされ、複数回 SYN パケットを再送信すると、サーバーはリクエストを受信するたびに複数の冗長で無効な接続を確立し、不要なリソースの浪費を引き起こします
image

つまり、二回のハンドシェイクはメッセージの滞留状況を引き起こし、サーバーは無駄な接続要求(SYN パケット)を繰り返し受信し、リソースを重複して割り当てることになります。

小結#

TCP が接続を確立する際、三回のハンドシェイクを通じて古い接続の確立を防ぎ、両者の不要なリソースのオーバーヘッドを削減し、初期シーケンス番号を同期させることができます。シーケンス番号はデータパケットの重複、廃棄、順序伝送を保証します。

「二回のハンドシェイク」と「四回のハンドシェイク」を使用しない理由:

  • 「二回のハンドシェイク」:古い接続の確立を防ぐことができず、両者のリソースの浪費を引き起こし、両者のシーケンス番号を信頼性よく同期させることができません。
  • 「四回のハンドシェイク」:三回のハンドシェイクは理論的に最小限の信頼性のある接続確立の回数を満たしているため、より多くの通信回数を使用する必要はありません。

なぜ TCP 接続を確立するたびに、初期シーケンス番号が異なる必要があるのですか?#

主な理由は二つあります:

  1. 古いパケットが次の同じ四つのタプルの接続で受信されるのを防ぐため(主な側面);
  2. セキュリティのため、ハッカーが偽造した同じシーケンス番号の TCP パケットが相手に受信されるのを防ぐため;

次に、最初のポイントについて詳しく説明します。
仮に、クライアントとサーバーの初期シーケンス番号が毎回 0 から始まるとします:
image

プロセスは次のとおりです:

  • クライアントとサーバーが TCP 接続を確立し、クライアントがデータパケットを送信したが、ネットワークでブロックされ、タイムアウトしてこのデータパケットを再送信しました。この時、サーバーのデバイスが電源を切られ、クライアントとの接続が消失しました。サーバーがクライアントのデータパケットを受信したとき、RST パケットを送信します。
  • 次に、クライアントが再びサーバーに同じ四つのタプルの接続を確立します。
  • 新しい接続が確立された後、以前の接続でネットワークでブロックされたデータパケットがちょうどサーバーに到達し、そのデータパケットのシーケンス番号がサーバーの受信ウィンドウ内にあるため、サーバーはこのデータパケットを正常に受信し、このデータパケットは前の接続から残されたものであるため、データの混乱が発生します。

見ての通り、クライアントとサーバーの初期シーケンス番号が毎回同じである場合、古いパケットが次の同じ四つのタプルの接続で受信される問題が発生しやすくなります

クライアントとサーバーの初期シーケンス番号が毎回「異なる」場合、古いパケットのシーケンス番号が「存在しない」ため、相手の受信ウィンドウに入らない可能性が高くなり、古いパケットを回避することができます。たとえば、次の図:
image

逆に、クライアントとサーバーの初期シーケンス番号が毎回「同じ」である場合、古いパケットのシーケンス番号がちょうど相手の受信ウィンドウ内に「存在する」可能性が高くなり、新しい接続が古いパケットを正常に受信することにつながります。

したがって、毎回初期シーケンス番号が異なることは、古いパケットが次の同じ四つのタプルの接続で受信されるのを大幅に回避できることを意味します。注意すべきは、これは大幅に回避できるということであり、完全に回避できるわけではありません(シーケンス番号には回転の問題があるため、古いパケットを判断するためにタイムスタンプメカニズムを使用する必要があります。詳細については、次の記事を参照してください:TCP はどのように古いパケットを回避するのか?)。

初期シーケンス番号 ISN はどのようにランダムに生成されるのですか?#

初期 ISN は時計に基づいており、4 マイクロ秒ごとに +1 され、1 回転するのに 4.55 時間かかります。
RFC 793 では、初期シーケンス番号 ISN のランダム生成アルゴリズムが次のように述べられています:ISN = M + F(localhost, localport, remotehost, remoteport)

  • M はタイマーで、4 マイクロ秒ごとに +1 されます。
  • F はハッシュアルゴリズムで、ソース IP、宛先 IP、ソースポート、宛先ポートに基づいてランダムな数値を生成します。ハッシュアルゴリズムは外部から簡単に推測されないようにする必要があり、MD5 アルゴリズムは比較的良い選択です。

見ての通り、ランダム数は時計タイマーに基づいて増加するため、初期シーケンス番号が同じになることはほぼ不可能です。

IP 層がフラグメンテーションを行うのに、なぜ TCP 層が MSS(Maximum Segment Size)を必要とするのですか?#

まず、MTU(Maximum Transmission Unit)と MSS(Maximum Segment Size)を理解しましょう:
image

  • MTU:ネットワークパケットの最大長さであり、イーサネットでは一般的に 1500 バイトです。
  • MSS:IP および TCP ヘッダーを除いた、ネットワークパケットが収容できる TCP データの最大長さです。

最初のハンドシェイクが失われた場合、何が起こりますか?#

クライアントがサーバーとの TCP 接続を確立しようとすると、最初に送信するのは SYN パケットであり、SYN_SENT 状態に入ります。

その後、クライアントがサーバーの SYN-ACK パケット(2 回目のハンドシェイク)を長時間受信できない場合、タイムアウト再送信(RTO: Retransmission TimeOut)メカニズムがトリガーされ、SYN パケットが再送信されます。さらに、再送信された SYN パケットのシーケンス番号はすべて同じです

異なるバージョンのオペレーティングシステムでは、タイムアウト時間が異なる場合があります。1 秒のものもあれば、3 秒のものもあります。このタイムアウト時間はカーネルにハードコーディングされており、変更するにはカーネルを再コンパイルする必要があります。これは比較的面倒です。

クライアントが 1 秒後にサーバーの SYN-ACK パケットを受信できない場合、クライアントは SYN パケットを再送信しますが、何回再送信するのでしょうか?

Linux では、クライアントの SYN パケットの最大再送信回数は tcp_syn_retries カーネルパラメータによって制御されており、このパラメータはカスタマイズ可能で、デフォルト値は通常 5 です。

cat /proc/sys/net/ipv4/tcp_syn_retries

通常、最初のタイムアウト再送信は 1 秒後、2 回目のタイムアウト再送信は 2 秒後、3 回目のタイムアウト再送信は 4 秒後、4 回目のタイムアウト再送信は 8 秒後、5 回目はタイムアウト再送信 16 秒後に行われます。そうです、各タイムアウトの時間は前回の 2 倍です
5 回目のタイムアウト再送信後、32 秒間待機し、サーバーからの ACK がまだ受信できない場合、クライアントは再度 SYN パケットを送信しなくなり、TCP 接続を切断します。
したがって、総時間は 1+2+4+8+16+32=63 秒、約 1 分です。
例を挙げると、tcp_syn_retries パラメータの値が 3 の場合、クライアントの SYN パケットがネットワークで失われた場合、次のようなプロセスが発生します:
image

具体的なプロセス:クライアントが SYN パケットを 3 回タイムアウト再送信した後、tcp_syn_retries が 3 であるため、最大再送信回数に達し、次に一定の時間(前回のタイムアウト時間の 2 倍)を待機します。もしまだサーバーの 2 回目のハンドシェイク(SYN-ACK パケット)を受信できない場合、クライアントは接続を切断します。

二回目のハンドシェイクが失われた場合、何が起こりますか?#

サーバーがクライアントの最初のハンドシェイクを受信すると、最初に SYN-ACK パケットをクライアントに返します。これが二回目のハンドシェイクであり、この時点でサーバーは SYN_RCVD 状態に入ります。

二回目のハンドシェイクの SYN-ACK パケットには実際に二つの目的があります:

  • 二回目のハンドシェイクの ACK は、最初のハンドシェイクの確認パケットです。
  • 二回目のハンドシェイクの SYN は、サーバーが TCP 接続を確立するためのパケットです。

したがって、二回目のハンドシェイクが失われた場合、どのような興味深いことが起こるのでしょうか?

  • 二回目のハンドシェイクのパケットには、クライアントの最初のハンドシェイクの ACK 確認パケットが含まれているため、クライアントが二回目のハンドシェイクを受信できない場合、クライアントは自分の SYN パケット(最初のハンドシェイク)が失われた可能性があると考え、クライアントはタイムアウト再送信メカニズムをトリガーし、SYN パケットを再送信します
  • 次に、二回目のハンドシェイクに含まれるサーバーの SYN パケットを受信した場合、クライアントはサーバーに ACK 確認パケット(第三回のハンドシェイク)を送信する必要があります。サーバーはこの SYN パケットがクライアントによって受信されたと見なすため、もし二回目のハンドシェイクが失われた場合、サーバーはタイムアウト再送信メカニズムをトリガーし、SYN-ACK パケットを再送信します。

Linux では、SYN-ACK パケットの最大再送信回数は tcp_synack_retries カーネルパラメータによって決定されており、デフォルト値は 5 です。

cat /proc/sys/net/ipv4/tcp_synack_retries

したがって、二回目のハンドシェイクが失われた場合、クライアントとサーバーの両方が再送信を行います

  • クライアントは SYN パケットを再送信します。これは最初のハンドシェイクであり、最大再送信回数は tcp_syn_retries カーネルパラメータによって決定されます。
  • サーバーは SYN-ACK パケットを再送信します。これは二回目のハンドシェイクであり、最大再送信回数は tcp_synack_retries カーネルパラメータによって決定されます。

例を挙げると、tcp_syn_retries パラメータの値が 1、tcp_synack_retries パラメータの値が 2 の場合、二回目のハンドシェイクが失われた場合のプロセスは次のようになります:
image
具体的なプロセス:

  • クライアントが SYN パケットを 1 回タイムアウト再送信した後、tcp_syn_retries が 1 であるため、最大再送信回数に達し、次に一定の時間(前回のタイムアウト時間の 2 倍)を待機します。もしまだサーバーの二回目のハンドシェイク(SYN-ACK パケット)を受信できない場合、クライアントは接続を切断します。
  • サーバーが SYN-ACK パケットを 2 回タイムアウト再送信した後、tcp_synack_retries が 2 であるため、最大再送信回数に達し、次に一定の時間(前回のタイムアウト時間の 2 倍)を待機します。もしまだクライアントの三回目のハンドシェイク(ACK パケット)を受信できない場合、サーバーは接続を切断します。

三回目のハンドシェイクが失われた場合、何が起こりますか?#

クライアントはサーバーからの SYN-ACK パケットを受信した後、サーバーに ACK パケットを返します。これが三回目のハンドシェイクであり、この時点でクライアントの状態は ESTABLISH 状態に入ります。

この三回目のハンドシェイクの ACK パケットは、二回目のハンドシェイクの SYN の確認パケットです。したがって、三回目のハンドシェイクが失われた場合、サーバーは ACK パケットを受信できず、タイムアウト再送信メカニズムをトリガーし、SYN-ACK パケットを再送信します。最大再送信回数に達するまで続きます。

注意すべきは、三回目のハンドシェイクの ACK パケットは再送信されないため、ACK が失われた場合、相手(サーバー)が再送信する必要があるのは二回目のハンドシェイクの SYN-ACK パケットです

例を挙げると、tcp_synack_retries パラメータの値が 2 の場合、三回目のハンドシェイクが失われた場合のプロセスは次のようになります:
image

具体的なプロセス:サーバーが SYN-ACK パケットを 2 回タイムアウト再送信した後、tcp_synack_retries が 2 であるため、最大再送信回数に達し、次に一定の時間(前回のタイムアウト時間の 2 倍)を待機します。もしまだクライアントの三回目のハンドシェイク(ACK パケット)を受信できない場合、サーバーは接続を切断します。

四回目のハンドシェイクが失われた場合、何が起こりますか?#

クライアントはサーバーからの三回目のハンドシェイクの FIN パケットを受信した後、ACK パケットを返します。これが四回目のハンドシェイクであり、この時点でクライアントの接続は TIME_WAIT 状態に入ります。

Linux システムでは、TIME_WAIT 状態は 2MSL の後に閉じる状態に入ります。

その後、サーバー(受動的に接続を切断する側)は ACK パケットを受信する前に、まだ LAST_ACK 状態にあります

もし四回目のハンドシェイクの ACK パケットがサーバーに到達しなかった場合、サーバーは FIN パケットを再送信します。再送信の回数は、前述の tcp_orphan_retries パラメータによって制御されます。

例を挙げると、tcp_orphan_retries が 2 の場合、四回目のハンドシェイクが失われた場合のプロセスは次のようになります:
image
具体的なプロセス:

  • サーバーが三回目のハンドシェイクのパケットを 2 回再送信した後、tcp_orphan_retries が 2 であるため、最大再送信回数に達し、次に一定の時間(前回のタイムアウト時間の 2 倍)を待機します。もしまだクライアントの四回目のハンドシェイク(ACK パケット)を受信できない場合、サーバーは接続を切断します。
  • クライアントは三回目のハンドシェイクを受信した後、TIME_WAIT 状態に入り、2MSL のタイマーを開始します。もし途中で再び三回目のハンドシェイク(FIN パケット)を受信した場合、タイマーがリセットされ、2MSL の時間が経過した後、クライアントは接続を切断します。

なぜ TIME_WAIT の待機時間は 2MSL なのですか?#

MSL は Maximum Segment Lifetime、パケットの最大生存時間であり、ネットワーク上に存在する最大の時間を示します。この時間を超えると、パケットは破棄されます。TCP パケットは IP プロトコルに基づいているため、IP ヘッダーには TTL フィールドがあり、IP データグラムが通過できる最大のルーター数を示します。各ルーターを通過するたびにこの値は 1 減少し、0 になるとデータグラムは破棄され、送信元ホストに ICMP パケットが通知されます。

MSL と TTL の違い: MSL の単位は時間であり、TTL は通過したルーターの数です。したがって、MSL は TTL が 0 になる時間以上である必要があります。これにより、パケットが自然に消失することが保証されます。

TTL の値は一般的に 64 であり、Linux では MSL を 30 秒に設定しています。これは、Linux がデータパケットが 64 のルーターを通過するのに 30 秒を超えないと考えていることを意味します。もし超えた場合、パケットはネットワーク内で消失したと見なされます

TIME_WAIT が 2 倍の MSL を待機する理由は、ネットワーク内に送信元からのデータパケットが存在する可能性があるためです。これらの送信元データパケットが受信側で処理された後、相手に応答を送信することになります。したがって、往復で 2 倍の時間を待機する必要があります

たとえば、受動的に接続を切断する側が断続的に最後の ACK パケットを受信できなかった場合、タイムアウトが発生し、サーバーは FIN パケットを再送信します。受動的に接続を切断する側が FIN パケットを受信すると、ACK を送信します。このように、往復で 2 つの MSL の時間が必要です。

見ての通り、2MSL の時間は、少なくともパケットが一度失われることを許可することを意味します。たとえば、ACK が 1 つの MSL 内で失われた場合、受動的に接続を切断する側が再送信する FIN パケットは第 2 の MSL 内に到達し、TIME_WAIT 状態の接続は対応できます。

なぜ 4 または 8 MSL の時間ではないのか?あなたは、パケットロス率が 1% に達するような悪化したネットワークを想像できます。連続して 2 回パケットロスが発生する確率は 0.01% であり、この確率は非常に小さく、無視する方がコストパフォーマンスが高いです。

2MSL の時間は、クライアントが FIN を受信した後、ACK を送信する開始時点から計算されます。TIME-WAIT の時間内にクライアントの ACK がサーバーに到達しなかった場合、クライアントがサーバーの再送信 FIN パケットを受信した場合、2MSL の時間は再計算されます

Linux システムでは 2MSL のデフォルトは 60 秒であり、1 MSL は 30 秒です。Linux システムの TIME_WAIT の時間は固定で 60 秒です

その定義は、Linux カーネルコード内の名前として TCP_TIMEWAIT_LEN として次のように示されています:

#define TCP_TIMEWAIT_LEN (60*HZ) /* how long to wait to destroy TIME-WAIT state, about 60 seconds  */

TIME_WAIT の時間を変更するには、Linux カーネルコード内の TCP_TIMEWAIT_LEN の値を変更し、Linux カーネルを再コンパイルする必要があります。

なぜ TIME_WAIT 状態が必要なのですか?#

接続を切断する側がアクティブに切断を行う場合、TIME_WAIT 状態が発生します

TIME_WAIT 状態が必要な主な理由は二つあります:

  • 古い接続のデータが後の同じ四つのタプルの接続で誤って受信されるのを防ぐため
  • 「受動的に接続を切断する側」が正しく切断されることを保証するため

理由 1:古い接続のデータが後の接続で誤って受信されるのを防ぐ#

この理由をよりよく理解するためには、シーケンス番号(SEQ)と初期シーケンス番号(ISN)を理解する必要があります。

  • シーケンス番号は、TCP のヘッダーのフィールドであり、TCP 送信側から TCP 受信側へのデータストリームのバイトを識別します。TCP はバイトストリーム指向の信頼性のあるプロトコルであり、メッセージの順序性と信頼性を保証するために、TCP は各転送方向の各バイトに番号を付け、成功した転送後の確認、失われた場合の再送信、受信側での順序の保証を行います。シーケンス番号は 32 ビットの符号なし数であるため、4G に達すると再び 0 に戻ります
  • 初期シーケンス番号は、TCP 接続を確立する際にクライアントとサーバーがそれぞれ生成する初期シーケンス番号であり、時計に基づいて生成されるランダムな数値であり、各接続が異なる初期シーケンス番号を持つことを保証します。初期シーケンス番号は 32 ビットのカウンターと見なすことができ、このカウンターの値は 4 マイクロ秒ごとに 1 増加し、1 回転するのに 4.55 時間かかります

次の図の Seq はシーケンス番号であり、赤い枠で囲まれた部分はクライアントとサーバーがそれぞれ生成した初期シーケンス番号です
image

前述の内容から、シーケンス番号と初期シーケンス番号は無限に増加するわけではなく、初期値に回帰することがあるため、新旧データを判断することができません

仮に TIME-WAIT が待機時間がないか、時間が短すぎる場合、遅延したデータパケットが到達した場合、何が起こるでしょうか?
image
上の図を見てみましょう:

  • サーバーが接続を切断する前に送信した SEQ = 301 パケットがネットワークで遅延しました。
  • 次に、サーバーが同じ四つのタプルで新しい接続を再確立し、遅延した SEQ = 301 パケットがちょうどサーバーに到達し、そのデータパケットのシーケンス番号がサーバーの受信ウィンドウ内にあるため、サーバーはこのデータパケットを正常に受信します。しかし、このデータパケットは前の接続から残されたものであり、データの混乱などの深刻な問題が発生します。

見ての通り、クライアントとサーバーの初期シーケンス番号が毎回同じである場合、古いパケットが次の同じ四つのタプルの接続で受信される問題が発生しやすくなります

TIME-WAIT が待機時間がないか、時間が短すぎる場合、古い接続のデータが後の接続で誤って受信されるのを防ぐために、TCP は TIME_WAIT 状態を設計しました。この状態は 2MSL の時間が経過するまで持続します。この時間は、両方向のデータパケットがすべて破棄されるのに十分であり、以前の接続のデータパケットがネットワーク内で自然に消失し、再出現するデータパケットは新しい接続から生成されたものであることが保証されます

理由 2:受動的に接続を切断する側が正しく切断されることを保証する#

RFC 793 では、TIME-WAIT のもう一つの重要な役割が次のように示されています:

TIME-WAIT - represents waiting for enough time to pass to be sure the remote TCP received the acknowledgment of its connection termination request.

つまり、TIME-WAIT の役割は、最後の ACK が受動的に接続を切断する側に届くことを確認するために十分な時間を待機することです

もし ** クライアント(アクティブに接続を切断する側)** の最後の ACK パケット(四回目のハンドシェイク)がネットワークで失われた場合、TCP の信頼性の原則に従い、サーバー(受動的に接続を切断する側)は FIN パケットを再送信します。

仮にクライアントが TIME-WAIT 状態を持たず、最後の ACK パケットを送信した後、すぐに CLOSE 状

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。