TCP の TIMEWAIT が長すぎます

記事ディレクトリ

1.TIMEWAITとは何ですか?

time_wait はよくある質問ですが、tcp ネットワーク プログラミングで理解するのが最も難しいのは time_wait 状態であり、これは tcp/ip の 4 方向ウェーブにおける time_wait 状態の重要性も示しています。
この問題については、すでにインターネット上で多くの人が議論していますが、私がこれまで手掛けてきた関連ビジネスに基づいて、私の見解を述べたいと思います。TIMEWAIT とは何かについては、改めて詳しく説明しません。興味があれば、私の記事を読んでください。

TIMEWAIT 状態自体は、アプリケーション層のクライアントやサーバーとは何の関係もありませんアクティブに閉じるのは当事者のみであり、この TIMEWAIT は、TCP 接続が FIN|ACK|FIN|ACK の 4 つのパケットを使用して通常に閉じられるときに表示されます。サーバーがクライアント要求を処理しているときに、サーバーがアクティブにシャットダウンされるようにプログラムが設計されている場合は、TIMEWAIT 状態が多すぎる問題に注意する必要があるかもしれません。サーバーが受動的に閉じるように設計されている場合、最初に考慮する必要があるのはCLOSE_WAITです。

2. 原則

TIMEWAIT は冗長ではありません。TCP プロトコルが作成され、実際の現場で多くの実践が行われた後、TCP が積極的に接続を閉じる側、つまり私たちの友人が TIMEWAIT 状態を必要とするため、TIMEWAIT が登場しました。これは、『UNIX ネットワーク プログラミング』の著者です。TIMEWAIT に対する Steven の態度です。

3. TCP状態遷移図

TCP プロトコルの出版: 1974 年 12 月に、Kahn と Cerf による TCP プロトコルの最初の詳細な説明が正式に出版されました。当時、米国国防総省は 3 つの科学者グループと TCP/IP を完成させるための協定を締結しており、その結果、Cerf 率いるグループが主導となって、まず詳細に定義された TCP/IP プロトコル標準を策定しました。当時、情報パケットは、ポイントツーポイントの衛星ネットワーク、地上ケーブル、衛星ネットワーク、そして地上伝送と、ヨーロッパやアメリカのさまざまなコンピュータシステムを経由するという実験が行われていました。 94,000 キロメートルにわたってデータの損失はなく、長距離でも信頼性の高いデータ伝送が TCP/IP プロトコルの成功を証明しています。

TCP状態遷移図この図は『TCP IP 詳細解説 第 1 巻: プロトコル原書 第 2 版 中国語』より引用 13.5 TCP 状態遷移図
TCP状態遷移図
この画像は『UNIX ネットワーク プログラミング 第 1 巻: ソケット ネットワーク API』より引用 2.6.4 TCP 状態遷移図

TCP ステート マシンには合計 11 の状態が含まれており、その状態はさまざまなソケット API の駆動下で移行されます。この図は複雑に見えますが、TCP ネットワーク プログラミングの一定の経験がある学生にとっては比較的簡単に理解できます。スペースの制限があるため、この記事では詳しく説明しません。具体的な移行プロセスを知りたい初心者は、「Linux ネットワーク プログラミング Volume1」のセクション 2.6 を読むことをお勧めします。

TCP 状態遷移図からわかるように、アクティブ シャットダウンを開始するために最初に close() を呼び出した側だけが TIME_WAIT 状態に入り、必ず入る必要があります (図の左下隅に示されている 3 本の状態遷移線は、最終的には、最初の CLOSED 状態に戻る前にこの状態になります)。
この図から、TIME_WAIT 状態に入った TCP 接続は、初期状態に戻るために 2MSL を通過する必要があることもわかります。ここで、MSL は、最大セグメント存続期間、つまり、ネットワーク内のデータ パケットの最大存続期間を指します
。 。TCP プロトコルの各実装方法では、適切な MSL 値を指定する必要があります。たとえば、RFC1122 で指定されている推奨値は 2 分ですが、Berkeley システムの TCP 実装では通常、MSL 値として 30 秒が選択されます。これは、TIME_WAIT の通常の継続時間が 1 ~ 4 分であることを意味します。

4. ACKとRSTを送信するシナリオ

4.1 TCP が ACK を送信するシナリオ

次の状況では、TCP は ACK パケットを送信します。

  1. 1 つのパケットを受信した後、200ms タイマーを開始し、200ms タイマーが終了するまで待ちます (2 番目のパケットが来ません)。その後、このパケットに対する確認 ACK が送信されます。これは「遅延送信」と呼ばれます。
  2. 1 つのパケットを受信した後、200ms タイマーを開始します。200ms タイマーはまだ到着していませんが、2 番目のパケットが再び到着します (2 つのパケットと 1 つの ACK)。
  3. 1 パケットを受信した後、200 ミリ秒のタイマーを開始しますが、まだタイムアウトせず、コンテンツを相手に送信するだけです。したがって、このパッケージの確認応答が続きます。これは「便乗」と呼ばれます。
  4. TCP は、予期されたシーケンス番号を超える順序どおりでないデータを受信するたびに、そのシーケンス番号が予期されたシーケンス番号であることを確認する ACK を常に送信します。
  5. ウィンドウ更新、またはオープン ウィンドウとも呼ばれます (受信側ウィンドウが最大に達すると、受信バッファ内のすべてのデータがプロセスにプッシュされ、受信バッファが空になります)、送信側に送信を続行するように通知されます。
  6. 通常の状況では、相手側のキープアライブ プローブに対する応答。詳細については、「TCP キープアライブ」を参照してください。

4.2 TCP が RST を送信するシナリオ

RST パケットは次の状況で送信されます。

  1. 存在しないポートを接続します。
  2. 閉じられた接続にデータを送信します。
  3. クラッシュしたピアにデータを送信します (接続は以前に確立されていました)。
  4. close(sockfd)時は、受信バッファ内の未読データを直接破棄し、相手にRSTを送信します。これは SO_LINGER オプションによって制御されます。
  5. A が再起動し、b のキープアライブ プローブを受信し、a が rst を送信して、b に通知します。

TCP ソケットのどの状態でもRST パケットを受信して​​いる限り、 CLOSED の初期状態に入ることができますRST セグメントは相手側からの応答を引き起こさず、相手側はまったく確認しないことに注意してください。RST を受信した側は接続を終了します。プログラムは次のように動作します。

  • ブロッキング モデルでは、カーネルはアプリケーション層にエラーを積極的に通知できません。アプリケーション層が read() や write() などの IO システム コールを積極的に呼び出す場合にのみ、カーネルはエラーを使用してアプリケーション層にエラーを通知します。ピア RST。
  • ノンブロッキング モデルでは、select または epoll は sockfd readable を返し、アプリケーション層がそれを読み取ると、read() はエラー RST を報告します。

RST メッセージについて詳しく知りたい場合は、「TCP 異常終了 (リセット メッセージ)」の記事を参照してください。

5. TCPコネクション数の上限について

メモリやファイル記述子などの客観的な制限を考慮すると、ほとんどすべてのサーバーが理論上の最大 TCP 接続数に到達することは不可能です。これらを考慮して、この記事ではパフォーマンスが無制限の場合の TCP 接続の最大数のみを考慮します。

5.1 TCPポート番号の上限

TCP ポート タイプは unsigned short であるため、ポート番号の上限は 2 の 16 倍である 65536 です。ポート 0 には特別な目的があるため、実際のポート番号の上限は 65535 (2^16-1) になります。

5.2 クライアントとサーバー間の TCP 接続の最大数

接続は、プロトコル タイプ、ローカル IP、ローカル ポート、リモート IP、リモート ポートの 5 つのタプルによって決定さます

  • tcp を使用する場合、クライアントはローカル ポートのみを選択できるため (プロトコル タイプは tcp、サーバー IP、ポートおよび自身の IP は固定です)、確立できる接続は最大2^16-1までです。
  • サーバーが tcp を使用する場合、プロトコル タイプ、ローカル IP およびローカル ポートは固定されるため、リモート IP とリモート ポートは可変です。ipv4 には 2^(8*4) 個、つまり 2^32 種類の ip がありますが、ipv6 にはさらに多くの種類の ip が必要です。したがって、それが ipv4 アドレスの場合、サーバーは2^32 * (2^16-1)個の接続を受信できます。

サーバー側の最大接続数は非常に大きいですが、実際には、サーバーのパフォーマンスは基本的にそのような膨大な数の接続をサポートできません。さらに、このような膨大な数の同時接続を必要とするサーバー タスクはほとんどないため、この最大接続数は一般に理論上のみ存在します。

6. TCP 接続の 11 状態

  1. 接続プロトコルの確立 (3 ウェイ ハンドシェイク)
     (1) クライアントは、SYN フラグを含む TCP メッセージをサーバーに送信します。これは 3 ウェイ ハンドシェイク プロセスのメッセージ 1 です
     (2) サーバーがクライアントに応答します これは 3 ウェイ ハンドシェイクの 2 番目のメッセージです このメッセージには ACK フラグと SYN フラグの両方が含まれています したがって、これはクライアントの SYN メッセージに対するたった今の応答を表し、同時にクライアントに SYN をマークし、クライアントがデータ通信の準備ができているかどうかを尋ねます。
     (3) クライアントは、サービス セグメント (セグメント 3) で ACK メッセージで再度応答する必要があります。

  2. 接続終了プロトコル (4 方向ハンドシェイク)
    TCP 接続は全二重であるため、各方向を個別に閉じる必要があります。原則として、一方の当事者がデータ送信タスクを完了すると、FIN を送信してこの方向の接続を終了できるということです。FIN の受信は、この方向のデータ フローがないことを意味するだけであり、TCP 接続は FIN を受信した後もデータを送信できます。最初にシャットダウンする側がアクティブなクローズを実行し、もう一方の側がパッシブなクローズを実行します。
     (1) TCP クライアントは FIN を送信して、クライアントからサーバーへのデータ送信を終了します (セグメント 4)。
     (2) サーバーは FIN を受信すると、シリアル番号が受信したシリアル番号に 1 を加えたものであることを確認して ACK を返します (セグメント 5)。SYN と同様に、FIN はシーケンス番号を占有します。
     (3) サーバーはクライアント接続を閉じ、FIN をクライアントに送信します (セグメント 6)。
     (4) クライアントセグメントは確認用の ACK メッセージを返信し、受信したシーケンス番号に 1 を加えた値を確認シーケンス番号に設定します (セグメント 7)。

  • CLOSED:特に言うことはなく、初期状態を意味します。

  • LISTEN : これも非常にわかりやすい状態で、サーバー側の特定のSOCKETがlisten状態にあり、接続を受け付けることができることを示します。

  • SYN_RCVD : この状態は、SYN メッセージが受信されたことを示します。通常の状況では、この状態は、TCP 接続を確立する際のサーバー側の SOCKET の 3 ウェイ ハンドシェイク セッション中の中間状態です。非常に短いです。基本的には、 netstat で確認するのは困難です。 この状態では、特別にクライアント テスト プログラムを作成しない限り、3 ウェイ TCP ハンドシェイク中に最後の ACK メッセージを意図的に送信しないでください。したがって、この状態では、クライアントから ACK メッセージを受信した後、ESTABLISHED 状態になります。

  • SYN_SENT : この状態は SYN_RCVD をエコーし​​ます。クライアント SOCKET が CONNECT 接続を実行するとき、最初に SYN メッセージを送信するため、すぐに SYN_SENT 状態に入り、サーバーが 3 ウェイ ハンドシェイクで 2 番目のメッセージを送信するのを待ちます。SYN_SENT 状態は、クライアントが SYN メッセージを送信したことを示します。

  • ESTABLISHED : 接続が確立されていることをわかりやすく示します。

  • FIN_WAIT_1 : この状態は注意して説明する必要がありますが、実際には、FIN_WAIT_1 および FIN_WAIT_2 状態の本当の意味は、相手からの FIN メッセージを待つことです。これら 2 つの状態の違いは、FIN_WAIT_1 状態は、SOCKET が ESTABLISHED 状態にあるときに、接続を積極的に閉じて相手に FIN メッセージを送信することを実際に意味しており、このとき、SOCKET は FIN_WAIT_1 状態に入ります。 。そして、相手が ACK メッセージに応答すると、FIN_WAIT_2 状態になりますが、もちろん、実際の通常の状況では、相手の状況に関係なく、すぐに ACK メッセージに応答する必要があるため、FIN_WAIT_1 状態になることは一般的に困難ですFIN_WAIT_2 状態は、netstat でよく確認できます。

  • FIN_WAIT_2 : この状態は上記で詳しく説明されています。実際、FIN_WAIT_2 状態の SOCKET は半接続を意味します。つまり、一方がクローズ接続を要求しますが、他方にはまだ送信するデータがあることを伝えます。しばらくの間、後で送信しますので、接続を閉じてください。

  • TIME_WAIT : 相手からの FIN メッセージを受信し、ACK メッセージを送信したことを示し、2MSL 待った後、CLOSED 使用可能状態に戻ります。FIN_WAIT_1 状態の場合、相手から FIN フラグと ACK フラグの両方が設定されたメッセージを受信した場合、FIN_WAIT_2 状態を経由せずに直接 TIME_WAIT 状態に移行することができます。

  • CLOSING : この状態は非常に特殊で、実際の状況ではまれなはずであり、比較的まれな例外状態に属します。通常、FIN メッセージを送信する場合は、まず相手の ACK メッセージを受信 (または同時に受信) し、次に相手の FIN メッセージを受信する必要があります。ただし、CLOSING 状態は、FIN メッセージを送信した後、相手の ACK メッセージを受信せず、代わりに相手の FIN メッセージを受信したことを意味します。どのような状況でこのようなことが起こるのでしょうか? 実際、注意深く考えてみれば、結論を出すのは難しくありません。つまり、両方の当事者がほぼ同時に SOCKET を閉じた場合、両方の当事者が同時に FIN メッセージを送信する状況が発生します。つまり、CLOSING 状態が表示され、双方が SOCKET 接続を閉じていることを示します。

  • CLOSE_WAIT : この状態の意味は、実際には閉じられるのを待っていることです。どうやって理解しますか?相手が SOCKET を閉じて自分自身に FIN メッセージを送信すると、システムは間違いなく相手に ACK メッセージで応答し、CLOSE_WAIT 状態に入ります。次に、本当に考えなければならないのは、相手に送信するデータがまだあるかどうかを確認し、データがない場合は、SOCKET を閉じて相手に FIN メッセージを送信する、つまり接続を閉じることができます。したがって、CLOSE_WAIT 状態で行う必要があるのは、接続が閉じるまで待つことです。

  • LAST_ACK : この状態は比較的理解しやすく、一方が FIN メッセージを送信した後に受動的に閉じられ、最後に他方の ACK メッセージを待ちます。ACK メッセージを受信すると、CLOSED 利用可能状態に入ることができます。

7. TIMEWAIT状態の役割(理由)

TIME_WAIT 状態が存在する主な理由は次の 2 つです。

7.1 TCP全二重(フルデュプレックス)接続の確実な解放を実現するために

アクティブクローズを開始する側(図ではクライアント)がACK 4 つのインタラクションの最後のパケット)がネットワーク内で失われた場合、TCP 再送信メカニズムにより、パッシブ クローズを実行する側(図のサーバー)は FIN を再送信する必要があります。FIN がクライアントに到達する前に(クライアントはアクティブなクローズのイニシエーター)、クライアントは(クローズを呼び出したにもかかわらず)この接続の状態を維持する必要があります。具体的には、この TCP 接続に対応する (local_ip、local_port) リソースをすぐに解放したり、再割り当てしたりすることはできません。TCP 接続は、romete ピアによって再送信された FIN が到着し、クライアントも ACK を再送信するまで、最初の CLOSED 状態を復元できません。アクティブなクローズ パーティが接続状態を維持するために TIME_WAIT に入らない場合、パッシブなクローズ パーティによって再送信された FIN が到着すると、アクティブなクローズ パーティの TCP トランスポート層は RST パケットで相手に応答します。相手側によってエラーが発生した場合 (実際、これは接続を閉じる通常のプロセスであり、例外ではありません)。

7.2 ネットワーク内で古いデータパケットが期限切れにより消失するようにするには

この問題を説明するために、まず TCP プロトコルに TIME_WAIT 状態の制限がないことを仮定し、次に現在 TCP 接続 (local_ip、local_port、remote_ip、remote_port) が存在すると仮定します。何らかの理由で、最初にそれを閉じます。そして、同じ 4 つのタプルを使用して新しい接続をセットアップします。この記事の前半で述べたように、TCP 接続はクワドルプルによって一意に識別されます。したがって、この仮定の状況では、TCP プロトコル スタックは前後の 2 つの TCP 接続の違いを区別できません。その観点からすると、これは同じ接続です。最初にリリースしてから途中で確立するというプロセスは、まったく「認識」されていません。このようにして、前の TCP 接続でローカル ピアによって送信されたデータがリモート ピアに到達した後、そのデータが現在の TCP 接続の通常のデータとしてリモート ピアの TCP トランスポート層によって受信され、渡される可能性があります。アプリケーション層まで (実際、私たちの仮定のシナリオでは、古いデータがリモート ピアに到達する前に、古い接続が切断され、同じ 4 つで構成される新しい TCP 接続が確立されます。したがって、これらの古いデータは、アプリケーション層まで渡されない)、データの混乱を引き起こし、さまざまな予測不可能な奇妙な現象を引き起こします。信頼性の高いトランスポート プロトコルとして、TCP はプロトコル レベルでこの状況を考慮して回避する必要があります。これが、TIME_WAIT 状態が存在する 2 番目の理由です。
具体的には、ローカル ピアがアクティブに close を呼び出した後、この時点の TCP 接続は TIME_WAIT 状態に入り、この状態の TCP 接続は、同じクアドルプル、つまり、通信相手が占有しているローカル ポートとの新しい接続をすぐに確立することはできません。アクティブなクローズを開始しました。TIME_WAIT の間は再割り当てできなくなります。TIME_WAIT 状態の継続時間は 2MSL であるため、これにより、古い TCP 接続の二重リンク内の古いデータ パケットが期限切れ (MSL 以上) によって確実に消失することが保証され、その後、同じ 4 倍速で新しい接続を確立できます。なし 前後2回、接続データがおかしくなる事態が発生しました。

7.3 もう一つのより詳細な記述

TIME_WAIT 状態が存在する理由は 2 つあります。(1) 4 ウェイ ハンドシェイク終了プロセスの信頼性を高めるため4 ウェイ ハンドシェイクの最後の ACK は、アクティブなクロージング パーティによって送信され、この ACK が失われると、パッシブ クロージング パーティは FIN を再度送信します。アクティブな終了側が 2MSL TIME_WAIT 状態を維持できる場合、失われた ACK が再度送信される可能性が高くなります。(2) 失われた複製が後続の新しい通常のリンクの伝送に損傷を与えることを防止します実際のネットワークでは、重複の損失は非常に一般的であり、多くの場合、ルータの障害によりパスの収束が妨げられ、パケットが無限ループのようにルータ A、B、C の間を飛び交います。IP ヘッダーには TTL があり、ネットワーク内のパケットの最大ホップを制限します。そのため、このパケットには 2 つの運命があります。最後の TTL が 0 になってネットワーク内で消滅するか、TTL が 0 になる前にルータ パスが収束するかのどちらかです。 , 残りの TTL ホップを使用して、最終的に目的地に到着します。しかし、TCP がタイムアウト再送メカニズムを通じて以前とまったく同じパケットを送信し、そのパケットより先に宛先に到達したため、その運命は TCP プロトコル スタックによって放棄される運命にあるのは残念です。もう 1 つの概念は、インカネーション接続と呼ばれます。これは、前の接続のインカネーションと呼ばれる、最後のソケット ペアとまったく同じ新しい接続を指します。重複とインカネーションの接続が失われると、送信に致命的なエラーが発生します。TCP がストリーミングしていることは誰もが知っていますが、すべてのパケットが到着する順序は一貫していません。シーケンス番号は TCP プロトコル スタックによって結合されます。この時点でインカネーション接続が seq=1000 を受け取り、失われた重複は seq = であると仮定します。 1000、len=1000 の場合、tcp は失われた重複が正当であると判断し、受信バッファに保存するため、送信エラーが発生します。2MSL TIME_WAIT 状態を通じて失われた重複がすべて確実に消え、新しい接続のエラーが回避されます

8. TIMEWAIT に 2MSL が必要な理由

8.1 MSL (最大セグメント寿命) 最大パケット寿命

すべての TCP 実装は MSL を選択する必要があります。これは、セグメントが破棄されるまでにネットワーク内に存在できる最大時間です。TCP セグメントは IP データグラムとしてネットワーク内で送信され、IP データグラムには存続期間を制限する TTL 時間が設定されているため、この時間が制限されます。RFC 793 では MSL は 2 分と規定されていますが、実際には 30 秒または 1 分が一般的に使用されます。

8.2 2MSL

TCP がアクティブ クローズを実行して最後の ACK を送信するとき、リンクは 2MSL の間 TIME_WAIT 状態に留まる必要があります。これにより、(1) TCP が最後の ACK を再送信して、この ACK が失われるのを防ぎ (受動的に閉じられた側がタイムアウトして最後の FIN を再送信する)、TCP の信頼できる全二重接続を確実に終了できるようになります。 (2) 古い重複セクションがネットワーク内から消えるようにします。「Unix ネットワーク プログラミング」の記事を参照してください。 (3) TCP コネクションの確立と TIME_WAIT 状態での切断が行われると、両端のポートは使用できなくなり、2MSL 時間が終了するまで使用できなくなります。接続が 2MSL 待機フェーズにある場合、遅延セグメントはすべて破棄されます。ただし、実際のアプリケーションでは、SO_REUSEADDRオプションを設定することで、2MSL 時間が終了するのを待たずにポートを使用できます。皆様の理解を容易にするために、以下ではこの問題を 2 つのわかりやすい方法で説明します。

8.3 TCP が 4 回ウェーブするときに 2MSL を待機するのはなぜですか? 一つ説明して

通信において解決すべき主な問題は、通信中の 2 つの当事者が同期状態になるように、通信中の 2 つの当事者間の情報の対称性を維持することです

例から始めましょう:

ロミオは大学在学中に中学の同級生ジュリエットに手紙を書きましたが、その内容は次のとおりです。

リトルリーフ、私はあなたが好きです!

手紙を送った後、ロミオはジュリエットがそれを受け取ることができるかどうか知る由もなく、シャオ・イェジの返事を受け取って初めて自分の手紙が相手に届いたことを知ることができる。

3日後、Xiao Yeziから返信があり、その内容は次のとおりでした。

シャオオウ、あなたの手紙を読みました、私もあなたのことが好きです...

この時点で、Xiao Yezi の目に映る双方の状態は「相互賞賛」です。

Xiao Ou が返信を受け取った場合、Xiao Ou の目に映る双方の状態は、「お互いを愛している!」ということになります。

Xiao Ou が返事を受け取らなかった場合、Xiao Ou は双方の状態を目にしました。それは報われない愛です。

シャオウのあいまいな状態に終止符を打ち、自分との「相思相愛」の合意に達させるために、シャオエは次の作業を行う必要があります。

1) Xiaoou からの 3 通目の手紙を辛抱強く待ちます

2) 数日以内に返信が届かない場合は、再度 2 通目の手紙を送信する必要があります。

シャオオウの返事を受け取れば、ついに二人のステータスは「相思相愛」に同期する!

2) が発生した場合でも、N 日後には同期状態に到達できます。

その後、二人は優しく語り合い、恋に落ちることができます。

TCP の 4 波も同様のパターンに従います。

アクティブに切断される側が A、パッシブに切断される側が B です。

最初のメッセージ: A が FIN を送信します

2 番目のメッセージ: B は ACK で応答します

3 番目のメッセージ: B が FIN を送信

現時点では: B は一方的に、A と合意に達した、つまり双方が接続を閉じることに同意したと信じています。

このとき、B はこの TCP 接続によって占有されているメモリ リソースを解放できますか? いいえ、B は A が ACK と FIN を受信することを確認する必要があります。

したがって、B は、A の 4 番目のメッセージの到着を静かに待つ必要があります。

4 番目のメッセージ: A は B の FIN の受信を確認するために ACK を送信します

B がこのメッセージを受信すると、両者は同期に達したと考えます。両方の当事者は接続を解放できることを知っており、この時点で B は TCP 接続によって占有されているメモリ リソースとポート番号を安全に解放できます。

したがって、受動的に閉じられた B は、待ち時間なしでリソースを直接解放します。

ただし、A は B が ACK を受信したかどうかを知りません。A は次のように考えます。

1) B が自身の ACK を受信しない場合、タイムアウトして FiN を再送信します。

その後、A は再送信された FIN を再度受信し、再度 ACK を送信します

2) B が自身の ACK を受信した場合、ACK を含むそれ以上のメッセージは送信しません。

1 か 2 かに関係なく、A は待機する必要があり、これら 2 つの場合の待機時間の最大値を最悪のケースに対処する必要があります。

送信 ACK メッセージの最大ライフタイム (MSL) + 受信 FIN メッセージの最大ライフタイム (MSL)。

これは正確に2MSL (最大セグメント寿命)です。

2MSL 時間を待つと、A は TCP によって占有されているリソースとポート番号を安全に解放でき、この時点でこのポート番号を使用して任意のサーバーに接続できるようになります

なぜ 2MSL まで待たなければならないのでしょうか?

そうしないと、解放されたポートが切断されたばかりのサーバー ポートに再接続する可能性があるため、ネットワーク内にまだ残っている古い TCP パケットが新しい TCP 接続パケットと競合し、データの競合が発生する可能性があります。この状況を回避するには、しばらく待つ必要があります。ネットワーク上の古い TCP 接続のアクティブなパケットがすべて停止するまで、2MSL 時間でこの要件を満たすことができます (ただし、これは非常に控えめです)。

8.4 TCP が 4 回ウェーブするときに 2MSL を待つのはなぜですか? 解説2

MSL は、最大セグメント ライフタイム、つまりパケットの最大ライフタイムです。これは、パケットがネットワーク上に存在する最長時間です。この時間を過ぎると、パケットは破棄されます。TCP メッセージは IP プロトコルに基づいておりIP ヘッダーにはTTL フィールドがあり、これはIP データグラムが通過できるルートの最大数です。この値は、データグラムが通過するたびに 1 ずつ減ります。この値が 0 の場合、メッセージは破棄され、同時に送信元ホストに通知するために ICMP メッセージが送信されます。

MSL と TTL の違い: MSL の単位は時間ですが、TTL はルート ホップ数ですしたがって、パケットが自然に破棄されたことを保証するには、MSL は TTL が 0 の時間以上である必要があります。

2MSL の時間は、クライアントが FIN を受信した後、ACK を送信した時点から始まりますTIME-WAIT 時間内であれば、クライアントの ACK がサーバーに送信されず、クライアントがサーバーによって再送信された FIN メッセージを受信するため、2MSL 時間はリセットされます

次に、現在の接続に属するメッセージがネットワーク内でまだ生きていないことを確認するために、最後の ACK メッセージを送信した後、2MSL 時間待機する必要がある理由の分析を開始します (相手からの FIN メッセージが 2 時間以内に受信されない場合)。この 2MSL 時間のテキストですが、相手からの FIN メッセージを受信して​​も、FIN を受信すると ACK が返信され、計時が再開されるため、議論には影響しません)。

説明を簡単にするために、切断中の TCP 接続があると仮定します。この接続の 2 つの端は A と B です。A は、送信されたメッセージを送信したばかりなので、接続を積極的に閉じる端です。この時点では、FIN メッセージの ACK は TIME_WAIT 状態にありますが、B は受動的にクローズされたエンドですが、この時点では LAST_ACK 状態にあり、完了するまで FIN メッセージを再送信します。最後の ACK を受信する前にタイムアウトになります。時間が経つにつれて、A から B に送信された ACK メッセージには 2 つの結末が生じます。

  1. ACK メッセージはネットワーク内で失われます。前述したように、複数回の再送信が失敗しない限り、特定の ACK が失われなくなるまで AB の両端のステータスは変化しないため、この状況を考慮する必要はありません。
  2. ACK メッセージは B によって受信されます。A が ACK メッセージを送信してから時間 t 後に B が ACK を受信すると仮定します。その場合、0 < t <= MSL になります。A は、送信した ACK を相手が受信するまでにどれくらいの時間がかかるかわからないため、A は ACK がネットワークから確実に消えるように、少なくとも MSL 期間の間 TIME_WAIT 状態を維持する必要があります。同時に、LAST_ACK 状態にある B は、ACK を受信したため、ネットワークにメッセージを送信せずに、直接 CLOSED 状態に入ります。したがって、一見すると、A は 1 MSL を待つだけで済みますが、よく考えてみると、B が ACK を受信する前の瞬間に、B は ACK を受信しなかったため FIN を再送信する可能性があるため、1 MSL では十分ではありません。メッセージの場合、この FIN メッセージがネットワークから消えるには最大でも 1 つの MSL が必要であるため、A はさらに 1 つの MSL を待つ必要があります。

要約すると、TIME_WAIT は少なくとも 2MSL 続く必要があります。2 つの MSL のうち、最初の MSL は、自身が送信した最後の ACK がネットワークから消えるまで待機し、2 番目の MSL は、ACK がネットワークから受信されるのを待機します。ピア 直前に再送信された可能性のある FIN メッセージがネットワークから消失します。

2MSL の期間は、実際には少なくとも 1 つのパケット損失を許容するのと同等であることがわかりますたとえば、ACK が 1 つの MSL 内で失われた場合、パッシブ パーティによって再送信された FIN は 2 番目の MSL 内に到着し、TIME_WAIT 状態の接続でこれを処理できます。

なぜ 4 または 8 MSL ではないのでしょうか? パケット損失率が 1% の不良ネットワークを想像してください。2 回連続してパケット損失が発生する確率はわずか 1/10,000 です。この確率は小さすぎます。問題を解決するよりも無視した方がコスト効率が高くなります。

Linux システムでは、デフォルトで2MSL は60秒であるため、1 MSL は30秒になります。Linux システムは、固定の 60 秒間 TIME_WAIT 状態になります

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 状態が必要なのは、主に次の 2 つの理由です。

同じ「4 倍」を持つ「古い」データ パケットが受信されないようにします。「接続を受動的に閉じる」側が正しく閉じることができることを確認します。つまり、受動的に閉じた側が最後の ACK を受信できるようにします。正常に閉じるのを助けます。

理由 1: 古い接続からのパケットを防ぐ
TIME-WAIT に待ち時間がないか、待ち時間が短すぎると仮定すると、遅延したパケットが到着した後はどうなりますか?
時間待ち

  • 上の図に示すように、接続を閉じる前に黄色のボックス内のサーバーによって送信された SEQ = 301 メッセージは、ネットワークによって遅延されました。
  • このとき、同一ポートへの TCP 接続が多重化された後、遅延した SEQ=301 がクライアントに到着すると、クライアントは期限切れメッセージを正常に受信する可能性があり、データの混乱などの重大な問題が発生します。

したがって、TCP はそのようなメカニズムを設計しており、2MSL 以降は、双方向のデータ パケットを破棄すれば十分であるため、最初に接続されていたデータ パケットはネットワーク内で自然に消滅し、再び現れるデータ パケットは新たに確立された接続によって生成される必要があります。

理由 2: 接続が正しく閉じられるようにするためRFC 793 では、 TIME-WAIT のもう 1 つの重要な役割が次
のように指摘されています

TIME-WAIT - リモート TCP が接続終了要求の確認応答を確実に受信するために十分な時間が経過するまで待機することを表します。

言い換えれば、TIME-WAIT の役割は、受動的終了側が最後の ACK を確実に受信できるように十分な時間を待機し、それによって正常に終了できるようにすることです。

TIME-WAIT に待ち時間がない場合、または時間が短すぎる場合、接続が切断されるとどのような問題が発生しますか?
時間待ち

  • 上図の赤枠で4回振ったクライアントの最後のACKメッセージがネットワーク内で失われた場合、クライアントのTIME-WAITが短すぎるか存在しない場合、クライアントは直接CLOSED状態に入り、サーバーは常に LASE_ACK 状態になります。
  • クライアントが接続を確立するために SYN 要求メッセージを開始すると、サーバーは RST メッセージをクライアントに送信し、接続確立プロセスは終了します。

TIME-WAIT が長時間待機すると、次の 2 つの状況が発生します。

  1. サーバーが 4 つの手を振った最後の ACK パケットを正常に受信した場合、サーバーは通常どおり接続を閉じます。
  2. サーバーは、手を振った 4 回のうち最後の ACK メッセージを受信しなかった場合、FIN 接続終了メッセージを再送信し、新しい ACK メッセージを待ちます。

したがって、クライアントは TIME-WAIT 状態で 2MSL 待機した後、双方の接続を正常に閉じることができます。

9. TIME_WAIT が大量に発生する問題

9.1 TIMEWAIT はフレンドリーです

TCP は、考えられるすべての状況下ですべてのデータが正しく配信されることを保証する必要があります。ソケットを閉じると、アクティブに閉じられたソケットは TIME_WAIT 状態になり、受動的に閉じられたソケットは CLOSED 状態になり、実際にすべてのデータが確実に送信されます。ソケットが閉じられるときは、両端での 4 ウェイ ハンドシェイクによって完了しますが、一方の端で close() が呼び出された場合、この端には送信するデータがないことを意味します。ハンドシェイクが完了すると、ソケットは初期の CLOSED 状態になれるように見えますが、そうではありません。その理由は、このように状態を整えるには 2 つの問題があるからです、1 つは最後の ACK が正常に送信できることを保証する仕組みがないこと、 2つ目は、パケット上にまだデータ パケット (さまよっている重複) が残っている可能性があることです。ネットワークに接続されており、それらを正常に処理できる必要があります。
この2つの問題を解決するためにTIMEWAITは生まれました。

  1. 最後の ACKが失われたと仮定すると、パッシブな近接パーティは FIN を再送信します。アクティブな終了側は、ACK を再送信できるように、有効な状態情報 (TIMEWAIT 状態で維持) を維持する必要があります。アクティブに閉じられたソケットがこの状態を維持せずに CLOSED 状態になった場合、アクティブに閉じられたソケットが CLOSED 状態にあるとき、FIN を受信した後にRSTで応答します。消極的な終了側は、RST を受信した後、何か問題が発生したと考えます。TCP プロトコルが必要な操作を正常に完了し、2 者間のデータ ストリーム送信を終了したい場合は、4 ウェイ ハンドシェイクの 4 つのセクションを損失なく完全かつ正確に送信する必要があります。これが、ソケットが閉じられた後も TIME_WAIT 状態のままである最初の理由です。ACK を再送信するまで待機する必要があるためです。

  2. 現在接続されている通信当事者がすでに close() を呼び出しており、両方の当事者が TIME_WAIT 状態に移行するのではなく、同時に CLOSED 最終状態に入ると仮定します。次の問題が発生します。新しい接続が確立され、使用される IP アドレスとポートは以前のものとまったく同じで、後で確立される接続は元の接続を完全に再利用します。また、元の接続のネットワークにデータグラムが残っているため、新しい接続で受信されるデータグラムが以前の接続のデータグラムである可能性もあると想定されます。これを防ぐために、TCP は新しい接続が TIME_WAIT 状態のソケットを再利用することを許可しませんTIME_WAIT 状態のソケットは MSL 時間の 2 倍待機します (MSL の 2 倍である理由は、MSL はデータグラムがネットワーク内で一方向に送信され、失われたとみなされるまでの時間であるためです。データグラムは送信される可能性があります)途中または応答処理中にデータグラムが残存し、データグラムの破棄と応答の確認に MSL の 2 倍の時間がかかります)となり、CLOSED 状態に遷移します。これは、接続が正常に確立された場合、以前のネットワークに残っていたデータグラムが失われる必要があることを意味します。

9.2 一部のシナリオでの大量の TIMEWAIT によって引き起こされる厄介なビジネス上の問題

大量の TIMEWAIT が表示されるため、解決する必要があります:
同時接続が多く短い TCP サーバーでは、サーバーがリクエストの処理を完了すると、通常は自動的に接続が直ちに閉じられます~~~ このシナリオでは、多数のソケットがTIMEWAIT状態になります。クライアントの同時実行性が高い状態が続くと、一部のクライアントはこの時点で接続に失敗します。
その場面を説明しましょう。TCP 接続を通常どおりアクティブに閉じると、TIMEWAIT が表示されます。この大量の同時接続に注意を払う必要があるのはなぜでしょうか? 次の 2 つの点に注意する必要があります

  1. 同時実行性が高いため、サーバーは短期間に多数のポートを同時に占有することができ、ポートの範囲は 0 ~ 65535 ですが、それほど多くはありません。システムや他のサービスで使用されるポートを除くと、残りのポートが少なくなります。
  2. このシナリオでの短い接続とは、 「業務処理 + データ送信の時間が TIMEWAIT タイムアウトの時間よりもはるかに短い 」接続を意味します。これは比較的長くて短い概念です。たとえば、Web ページを考えます。1 秒間の http の短い接続でビジネスが処理されます。接続が閉じられた後、ビジネスで使用されるポートは、しばらくの間 TIMEWAIT 状態のままになります。数分間、そしてこの数分間、他の HTTP リクエストが来たときにこのポートを占有することはできません。この業務だけを使ってサーバーの稼働率を計算すると、サーバーが本格的な業務を行っている時間と、ポート(リソース)がハングアップして使用できなくなる時間の比率は1:100であることがわかります。これはサーバー リソースの重大な無駄遣いです。(余談になりますが、この意味でサーバーのパフォーマンスチューニングを考えると、長時間接続ビジネスのサービスではTIMEWAIT状態を考慮する必要はありません。同時に、サーバーのビジネスシナリオに精通していれば、実際のビジネス シナリオでは、一般に、長時間接続に対応するビジネスの同時実行性はそれほど高くありません)これら 2 つの側面を組み合わせると、一定量の同時実行性の高い短い接続が継続的に到着すると、サーバーは一部の顧客へのサービスの提供を拒否することになります。ポートリソースが不足しています同時に、これらのポートはサーバーによって一時的に割り当てられるため、SO_REUSEADDRオプションを使用してこの問題を解決することはできません。(サーバーは SO_REUSEADDR ソケット オプションを設定して、ポートが使用中の場合にカーネルに通知できますが、TCP 接続が TIME_WAIT 状態のときにポートを再利用できます。非常に便利なシナリオでは、サーバー プログラムが停止したときに、すぐに再起動しても、新しいソケットは依然として同じポートの使用を希望するため、SO_REUSEADDR オプションを使用すると TIME_WAIT 状態を回避できます。)

9.3 一対の矛盾

TIMEWAIT は、頭が痛くなるほどフレンドリーです。しかし、サーバーの堅牢性を確保するために
最善を尽くしているため、私たちは依然としてこれを友好的な態度で見る必要があります

9.4 実現可能であり、存在する必要があるが、非原則的な解決策

  1. Linux は、sysctl または proc ファイル システムで TIMEWAIT タイムアウトを変更するためのインターフェイスを公開していません。カーネル プロトコル スタックコードで TIMEWAIT のタイムアウト パラメータを変更し、カーネルを再プログラムしてタイムアウト時間を短縮し、リサイクルを高速化することができます。
  2. SO_LINGER オプションの強制クローズ メソッドを使用して、FIN の代わりに RST を送信し、TIMEWAIT 状態をバイパスし、直接 CLOSED 状態に入ります。詳細については、私のブログ投稿「TCP オプション SO_LINGER」を参照してください。

9.5 この問題をどう見るか

上記 2 つの解決策は実行可能だと思うのに、原則に沿っていないのはなぜですか?
サーバー プログラムの堅牢性を確保するには、TIMEWAIT 状態に依存する必要があると最初に考えました。ネットワーク上には厄介な問題が多すぎるため、サービスが正常に機能する必要があります。
パフォーマンスは必要ないということですか?あまり。サーバー上で実行されている短い接続トラフィックが、TIMEWAIT 状態が多すぎるという問題に実際に対処しなければならない点に達した場合、私の原則は、TIMEWAIT を使用する代わりに、可能な限りそれに対処することです。対処しようとしても問題を解決できず、依然として一部のリクエストの処理を拒否する場合は、マシンを分割する方法を採用し、複数のマシンがこれらの同時実行性の高い短いリクエストに抵抗できるようにします。100,000 の同時短い接続リクエストを維持するには、2 台のマシン (それぞれ 50,000) で十分です。一般的な業務量や国内のほとんどのWebサイトでは、実はこの問題を意識する必要はなく、一言で言えば、この問題を意識する必要があるほどのアクセス数には達していないのです。
私が上記で不合理だと感じた方法でこの問題を解決しなければならないシナリオは本当にありますか? 答えは「はい」です。
Taobao、Baidu、Sina、JD.com などのサイトには、静止画を扱う小規模なビジネスが多数あります。サーバーが過度に分散されている場合、多数のマシンを起動する必要があります。より多くのマシンを購入するとコストがかかり、コンピュータ ルームも増加します。これらのマシンを保護するためのコストの増加は非常に深刻です。現時点では、最適化のためにできる限りのことを行う必要があります。
余談ですが、サーバーには絶対的な技術的な問題はなく、すべてはビジネス ニーズのためです。

9.6 TIMEWAITが長すぎる場合の対処法

/etc/sysctl.conf 内の 2 つのカーネル パラメータを次のように変更するだけです。

net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1

変更後、実行して/sbin/sysctl -pパラメータを有効にします。
簡単に言えば、システムの TIMEWAIT 再利用と高速リサイクルを可能にするということですが、再利用と高速リサイクルをどのように行うかについては、ここでは詳しく説明していませんが、実際のシナリオでは確かに効果的です。netstat または ss を使用して観察すると、結論を導き出すことができます。
一部の友人は、次のように syncookies の機能も同時に開いています。

net.ipv4.tcp_syncookies = 1

この syncookies を開く目的は、実際には次のとおりです。「サーバー リソースが不足している場合 (ポート リソースだけでなく、サービス拒否攻撃では多くのリソース不足が発生します)、TCP syn (接続) 要求を拒否しないようにし、syn 要求を送信しようとしてキャッシュします」後で可能になったときにこれらの TCP 接続リクエストを処理できるように保存してください。」
同時実行性が実際に非常に高い場合、これをオンにすることはあまり役に立ちません。

10. /etc/sysctl.conf を構成する

10.1 /etc/sysctl.conf

# sysctl settings are defined through files in
# /usr/lib/sysctl.d/, /run/sysctl.d/, and /etc/sysctl.d/.
#
# Vendors settings live in /usr/lib/sysctl.d/.
# To override a whole file, create a new file with the same in
# /etc/sysctl.d/ and put new settings there. To override
# only specific settings, add a file with a lexically later
# name in /etc/sysctl.d/ and put new settings there.
#
# For more information, see sysctl.conf(5) and sysctl.d(5).
#关闭ipv6
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1

# 避免放大攻击
net.ipv4.icmp_echo_ignore_broadcasts = 1

# 开启恶意icmp错误消息保护
net.ipv4.icmp_ignore_bogus_error_responses = 1

# 关闭路由转发
#net.ipv4.ip_forward = 1
#net.ipv4.conf.all.send_redirects = 0
#net.ipv4.conf.default.send_redirects = 0

 #开启反向路径过滤
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1

#处理无源路由的包
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0

 #关闭sysrq功能
kernel.sysrq = 0

#core文件名中添加pid作为扩展名
kernel.core_uses_pid = 1
net.ipv4.tcp_syncookies = 1

#修改消息队列长度
kernel.msgmnb = 65536
kernel.msgmax = 65536

#设置最大内存共享段大小bytes
kernel.shmmax = 68719476736
kernel.shmall = 4294967296

#timewait的数量,默认180000
net.ipv4.tcp_max_tw_buckets = 6000
net.ipv4.tcp_sack = 1
net.ipv4.tcp_window_scaling = 1
net.ipv4.tcp_rmem = 4096        87380   4194304
net.ipv4.tcp_wmem = 4096        16384   4194304
net.core.wmem_default = 8388608
net.core.rmem_default = 8388608
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.core.netdev_max_backlog = 262144

#限制仅仅是为了防止简单的DoS 攻击
net.ipv4.tcp_max_orphans = 3276800

#未收到客户端确认信息的连接请求的最大值
net.ipv4.tcp_max_syn_backlog = 262144
net.ipv4.tcp_timestamps = 0

#内核放弃建立连接之前发送SYNACK 包的数量
net.ipv4.tcp_synack_retries = 1

#内核放弃建立连接之前发送SYN 包的数量
net.ipv4.tcp_syn_retries = 1

#启用timewait 快速回收
net.ipv4.tcp_tw_recycle = 1

#开启重用。允许将TIME-WAIT sockets 重新用于新的TCP 连接
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_mem = 94500000 915000000 927000000
net.ipv4.tcp_fin_timeout = 1

#当keepalive 起用的时候,TCP 发送keepalive 消息的频度。缺省是2 小时
net.ipv4.tcp_keepalive_time = 30

#允许系统打开的端口范围
net.ipv4.ip_local_port_range = 1024    65000

# 确保无人能修改路由表
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.secure_redirects = 0
net.ipv4.conf.default.secure_redirects = 0

10.2 TIME_WAIT リソースを迅速にリサイクルおよび再利用するように sysctl.conf を構成する

サーバーがこれらの TIME_WAIT リソースを迅速にリサイクルして再利用できるように、カーネル パラメータを最適化します。

编辑内核文件/etc/sysctl.conf,加入以下内容:

net.ipv4.tcp_syncookies = 1    %表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;
net.ipv4.tcp_tw_reuse = 1      %表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;
net.ipv4.tcp_tw_recycle = 1    %表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。
net.ipv4.tcp_fin_timeout      %表示如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间

执行 /sbin/sysctl -p 让参数生效:
/etc/sysctl.conf是一个允许改变正在运行中的Linux系统的接口,它包含一些TCP/IP堆栈和虚拟内存系统的高级选项,修改内核参数永久生效。

10.3 tcp_fin_timeout の誤解

よくある間違いは、tcp_fin_timeout が TIME_WAIT の時間であると考えることですが、実際には、tcp_fin_timeout は FIN-WAIT-2 状態に留まる時間です。
TIME_WAIT の 2MSL は Linux カーネル コードに記述されています。この記事の 6.4 を参照してください。

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

10.4 共通パラメータ

TCPパラメータの説明:

net.ipv4.tcp_syncookies = 1 表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;

net.ipv4.tcp_tw_reuse = 1 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;

net.ipv4.tcp_tw_recycle = 1 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。

net.ipv4.tcp_fin_timeout = 30 表示如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间。

net.ipv4.tcp_keepalive_time = 1200 表示当keepalive起用的时候,TCP发送keepalive消息的频度。缺省是2小时,改为20分钟。

net.ipv4.ip_local_port_range = 1024 65000 表示用于向外连接的端口范围。缺省情况下很小:32768到61000,改为1024到65000。

net.ipv4.tcp_max_syn_backlog = 8192 表示SYN队列的长度,默认为1024,加大队列长度为8192,可以容纳更多等待连接的网络连接数。

net.ipv4.tcp_max_tw_buckets = 5000表示系统同时保持TIME_WAIT套接字的最大数量,如果超过这个数字,TIME_WAIT套接字将立刻被清除并打印警告信息。默认为180000,改为5000。

Apache や Nginx などのサーバーの場合、上記の行のパラメータによって TIME_WAIT ソケットの数を大幅に減らすことができますが、Squid の場合、その効果は大きくありません。このパラメータは、TIME_WAIT ソケットの最大数を制御して、Squid サーバーが多数の TIME_WAIT ソケットによって死亡するのを防ぐことができます。

10.5 共通コマンド

多数の短い接続の場合、Linux の TCP スタックは通常、TIME_WAIT 状態のソケットを大量に生成します。次のコマンドで確認できます。

netstat -ant| grep -i time_wait

場合によっては、この数字が驚くべきものになることがあります。
tcp

netstat -ant|grep -i time_wait |wc -l

ネット統計
3万~4万以上かかることもあります。このとき、Linux カーネルの tcp 時間の待ち時間を変更する必要がありますが、これを短縮するには sysctl パラメータが利用できるようです /proc/sys/net/ipv4/tcp_fin_timeout で、デフォルト値はインターネット上の多くの情報では、この値を低く設定すると netstat の TIME_WAIT ステータスが減少する可能性があると記載されていますが、この記述は誤りです

Linux カーネルのソース コードを注意深く読んだ後、この値は実際には出力用であることがわかりました。変更後、実際にカーネルに読み戻されて使用されるわけではありませんが、カーネル内で実際に機能するのは、$KERNEL/ 内のマクロ定義です。 include/ net /tcp.h 内には次の行があります。

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

そして、このマクロは実際に TCP TIME_WAIT 状態のタイムアウト期間を制御します。TIME_WAIT 状態の数を減らしたい場合(カーネルの動作時間を少し節約したい場合)、この値をより低く設定できます。テストによると、10 秒に設定するのがより適切です。

  • TCPの各状態の番号を表示する
netstat -ant|awk '/^tcp/ {++S[$NF]} END {for(a in S) print (a,S[a])}'

出力:

LAST_ACK 14
SYN_RECV 348
ESTABLISHED 70
FIN_WAIT1 229
FIN_WAIT2 30
CLOSING 33
TIME_WAIT 18122

コマンドの説明

先来看看netstat:
netstat -n
Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 123.123.123.123:80 234.234.234.234:12345 TIME_WAIT
你实际执行这条命令的时候,可能会得到成千上万条类似上面的记录,不过我们就拿其中的一条就足够了。
 
再来看看awk:
/^tcp/
滤出tcp开头的记录,屏蔽udp, socket等无关记录。
state[]相当于定义了一个名叫state的数组
NF
表示记录的字段数,如上所示的记录,NF等于6
$NF
表示某个字段的值,如上所示的记录,$NF也就是$6,表示第6个字段的值,也就是TIME_WAIT
state[$NF]表示数组元素的值,如上所示的记录,就是state[TIME_WAIT]状态的连接数
++state[$NF]表示把某个数加一,如上所示的记录,就是把state[TIME_WAIT]状态的连接数加一
END
表示在最后阶段要执行的命令
for(key in state)
遍历数组

11. TCP の 3 方向ハンドシェイクと 4 方向ハンドシェイクに関するいくつかの質問

第一次握手,如果客户端发送的SYN一直都传不到被服务器,那么客户端是一直重发SYN到永久吗?客户端停止重发SYN的时机是什么?
第三次握手,如果服务器永远不会收到ACK,服务器就永远都留在 Syn-Recv 状态了吗?退出此状态的时机是什么?
第三次挥手,如果客户端永远收不到 FIN,ACK,客户端永远停留在 Fin-Wait-2状态了吗?退出此状态时机是什么时候呢?
第四次挥手,如果服务器永远收不到 ACK,服务器永远停留在 Last-Ack 状态了吗?退出此状态的时机是什么呢?
如果客户端 在 2SML内依旧没收到 FIN,ACK,会关闭链接吗?服务器那边怎么办呢,是怎么关闭链接的呢?
可以看到,这些问题都是关于 TCP 是如何处理这些异常场景的,我们在学 TCP 连接建立和断开的时候,总是以为这些过程能如期完成。
可惜理想很丰满,现实很骨感,事实预料呀。
TCP 当然不傻,对以上这些异常场景都是有做处理的。
这些异常场景共分为两大类,第一类是 TCP 三次握手期间的异常,第二类是 TCP 四次挥手期间的异常。

12. TCP 三次握手期间的异常

我们先来看看 TCP 三次握手是怎样的。
3回の握手

12.1 第一次握手丢失了,会发生什么?

当客户端想和服务端建立 TCP 连接的时候,首先第一个发的就是 SYN 报文,然后进入到SYN_SENT状态。
在这之后,如果客户端迟迟收不到服务端的 SYN-ACK 报文(第二次握手),就会触发超时重传机制。
不同版本的操作系统可能超时时间不同,有的 1 秒的,也有 3 秒的,这个超时时间是写死在内核里的,如果想要更改则需要重新编译内核,比较麻烦。
当客户端在 1 秒后没收到服务端的 SYN-ACK 报文后,客户端就会重发 SYN 报文,那到底重发几次呢?
在 Linux 里,客户端的 SYN 报文最大重传次数由tcp_syn_retries内核参数控制,这个参数是可以自定义的,默认值一般是 5。
通常,第一次超时重传是在 1 秒后,第二次超时重传是在 2 秒,第三次超时重传是在 4 秒后,第四次超时重传是在 8 秒后,第五次是在超时重传 16 秒后。没错,每次超时的时间是上一次的 2 倍。
当第五次超时重传后,会继续等待 32 秒,如果服务端仍然没有回应 ACK,客户端就不再发送 SYN 包,然后断开 TCP 连接。
所以,总耗时是 1+2+4+8+16+32=63 秒,大约 1 分钟左右。

12.2 第二次握手丢失了,会发生什么?

当服务端收到客户端的第一次握手后,就会回 SYN-ACK 报文给客户端,这个就是第二次握手,此时服务端会进入SYN_RCVD状态。
第二次握手的SYN-ACK报文其实有两个目的 :

  • 第二次握手里的 ACK, 是对第一次握手的确认报文;
  • 第二次握手里的 SYN,是服务端发起建立 TCP 连接的报文;

所以,如果第二次握手丢了,就会发送比较有意思的事情,具体会怎么样呢?
因为第二次握手报文里是包含对客户端的第一次握手的 ACK 确认报文,所以,如果客户端迟迟没有收到第二次握手,那么客户端就觉得可能自己的 SYN 报文(第一次握手)丢失了,于是客户端就会触发超时重传机制,重传 SYN 报文。
然后,因为第二次握手中包含服务端的 SYN 报文,所以当客户端收到后,需要给服务端发送 ACK 确认报文(第三次握手),服务端才会认为该 SYN 报文被客户端收到了。
那么,如果第二次握手丢失了,服务端就收不到第三次握手,于是服务端这边会触发超时重传机制,重传 SYN-ACK 报文。
在 Linux 下,SYN-ACK 报文的最大重传次数由tcp_synack_retries内核参数决定,默认值是 5
因此,当第二次握手丢失了,客户端和服务端都会重传:

  • 客户端会重传 SYN 报文,也就是第一次握手,最大重传次数由 tcp_syn_retries内核参数决定。;
  • 服务端会重传 SYN-AKC 报文,也就是第二次握手,最大重传次数由 tcp_synack_retries 内核参数决定。

12.3 第三次握手丢失了,会发生什么?

客户端收到服务端的 SYN-ACK 报文后,就会给服务端回一个 ACK 报文,也就是第三次握手,此时客户端状态进入到ESTABLISH状态。
因为这个第三次握手的 ACK 是对第二次握手的 SYN 的确认报文,所以当第三次握手丢失了,如果服务端那一方迟迟收不到这个确认报文,就会触发超时重传机制,重传 SYN-ACK 报文,直到收到第三次握手,或者达到最大重传次数。
注意,ACK 报文是不会有重传的,当 ACK 丢失了,就由对方重传对应的报文。

13. TCP 四次挥手期间的异常

我们再来看看 TCP 四次挥手的过程。四回手を振った

13.1 第一次挥手丢失了,会发生什么?

当客户端(主动关闭方)调用 close 函数后,就会向服务端发送 FIN 报文,试图与服务端断开连接,此时客户端的连接进入到FIN_WAIT_1状态。
正常情况下,如果能及时收到服务端(被动关闭方)的 ACK,则会很快变为FIN_WAIT2状态。
如果第一次挥手丢失了,那么客户端迟迟收不到被动方的 ACK 的话,也就会触发超时重传机制,重传 FIN 报文,重发次数由tcp_orphan_retries参数控制。
当客户端重传 FIN 报文的次数超过tcp_orphan_retries后,就不再发送 FIN 报文,直接进入到close状态。

13.2 第二次挥手丢失了,会发生什么?

当服务端收到客户端的第一次挥手后,就会先回一个 ACK 确认报文,此时服务端的连接进入到CLOSE_WAIT状态。
在前面我们也提了,ACK 报文是不会重传的,所以如果服务端的第二次挥手丢失了,客户端就会触发超时重传机制,重传 FIN 报文,直到收到服务端的第二次挥手,或者达到最大的重传次数。
这里提一下,当客户端收到第二次挥手,也就是收到服务端发送的 ACK 报文后,客户端就会处于FIN_WAIT2状态,在这个状态需要等服务端发送第三次挥手,也就是服务端的 FIN 报文。
对于 close 函数关闭的连接,由于无法再发送和接收数据,所以FIN_WAIT2状态不可以持续太久,而
tcp_fin_timeout控制了这个状态下连接的持续时长,默认值是 60 秒。
这意味着对于调用 close 关闭的连接,如果在 60 秒后还没有收到 FIN 报文,客户端(主动关闭方)的连接就会直接关闭。

13.3 第三次挥手丢失了,会发生什么?

当服务端(被动关闭方)收到客户端(主动关闭方)的 FIN 报文后,内核会自动回复 ACK,同时连接处于CLOSE_WAIT状态,顾名思义,它表示等待应用进程调用 close 函数关闭连接。
此时,内核是没有权利替代进程关闭连接,必须由进程主动调用 close 函数来触发服务端发送 FIN 报文。
服务端处于 CLOSE_WAIT 状态时,调用了 close 函数,内核就会发出 FIN 报文,同时连接进入LAST_ACK 状态,等待客户端返回 ACK 来确认连接关闭。
如果迟迟收不到这个 ACK,服务端就会重发 FIN 报文,重发次数仍然由tcp_orphan_retries 参数控制,这与客户端重发 FIN 报文的重传次数控制方式是一样的。

13.4 第 4 波は失われたが、何が起こるか?

クライアントはサーバーから 3 回目の FIN メッセージを受信すると、4 回目の ACK メッセージを返し、クライアント接続は TIME_WAIT 状態になります。
Linux システムでは、TIME_WAIT 状態はシャットダウン状態に入るまで 60 秒間続きます。
その後、サーバー (受動的終了側) が ACK メッセージを受信しない前は、まだ LAST_ACK 状態にあります。
4 番目のウェーブの ACK メッセージがサーバーに届かない場合、サーバーは FIN メッセージを再送信しますが、再送信の回数は依然として前に導入した tcp_orphan_retries パラメーターによって制御されます。
はい、TCP は非常に賢いです。

14. 関連記事

15. 今後の予定

TCP 接続とメモリの関係については、後でさらに詳しく説明します。

おすすめ

転載: blog.csdn.net/craftsman2020/article/details/127998630